bcachefs: Debug asserts for ca->ref
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 3 May 2024 22:07:40 +0000 (18:07 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Wed, 8 May 2024 21:29:22 +0000 (17:29 -0400)
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/sb-members.h
fs/bcachefs/super.c

index 82544f826c5883afb2141a650b4d9ec1437b7fe2..f5ba8c6a34d73c68fa9cace74c15833768769ebc 100644 (file)
@@ -537,7 +537,13 @@ struct io_count {
 
 struct bch_dev {
        struct kobject          kobj;
+#ifdef CONFIG_BCACHEFS_DEBUG
+       atomic_long_t           ref;
+       bool                    dying;
+       unsigned long           last_put;
+#else
        struct percpu_ref       ref;
+#endif
        struct completion       ref_completion;
        struct percpu_ref       io_ref;
        struct completion       io_ref_completion;
index 0aeb7285dc8c7eb08d654e285cb2e86b2b387867..ecb8284af0dedf8a1f507523076d1b6499cb7425 100644 (file)
@@ -107,12 +107,25 @@ static inline struct bch_dev *__bch2_next_dev(struct bch_fs *c, struct bch_dev *
 
 static inline void bch2_dev_get(struct bch_dev *ca)
 {
+#ifdef CONFIG_BCACHEFS_DEBUG
+       BUG_ON(atomic_long_inc_return(&ca->ref) <= 1L);
+#else
        percpu_ref_get(&ca->ref);
+#endif
 }
 
 static inline void __bch2_dev_put(struct bch_dev *ca)
 {
+#ifdef CONFIG_BCACHEFS_DEBUG
+       long r = atomic_long_dec_return(&ca->ref);
+       if (r < (long) !ca->dying)
+               panic("bch_dev->ref underflow, last put: %pS\n", (void *) ca->last_put);
+       ca->last_put = _THIS_IP_;
+       if (!r)
+               complete(&ca->ref_completion);
+#else
        percpu_ref_put(&ca->ref);
+#endif
 }
 
 static inline void bch2_dev_put(struct bch_dev *ca)
index 8182cc971e7c687c21096b54f8537986fbd42a87..ed135166ab86e20ce0f4df5e51f93279e95f5a98 100644 (file)
@@ -656,6 +656,7 @@ void bch2_fs_free(struct bch_fs *c)
                struct bch_dev *ca = rcu_dereference_protected(c->devs[i], true);
 
                if (ca) {
+                       EBUG_ON(atomic_long_read(&ca->ref) != 1);
                        bch2_free_super(&ca->disk_sb);
                        bch2_dev_free(ca);
                }
@@ -1200,7 +1201,9 @@ static void bch2_dev_free(struct bch_dev *ca)
        bch2_time_stats_quantiles_exit(&ca->io_latency[READ]);
 
        percpu_ref_exit(&ca->io_ref);
+#ifndef CONFIG_BCACHEFS_DEBUG
        percpu_ref_exit(&ca->ref);
+#endif
        kobject_put(&ca->kobj);
 }
 
@@ -1227,12 +1230,14 @@ static void __bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca)
        bch2_dev_journal_exit(ca);
 }
 
+#ifndef CONFIG_BCACHEFS_DEBUG
 static void bch2_dev_ref_complete(struct percpu_ref *ref)
 {
        struct bch_dev *ca = container_of(ref, struct bch_dev, ref);
 
        complete(&ca->ref_completion);
 }
+#endif
 
 static void bch2_dev_io_ref_complete(struct percpu_ref *ref)
 {
@@ -1301,9 +1306,14 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
        ca->nr_btree_reserve = DIV_ROUND_UP(BTREE_NODE_RESERVE,
                             ca->mi.bucket_size / btree_sectors(c));
 
-       if (percpu_ref_init(&ca->ref, bch2_dev_ref_complete,
-                           0, GFP_KERNEL) ||
-           percpu_ref_init(&ca->io_ref, bch2_dev_io_ref_complete,
+#ifndef CONFIG_BCACHEFS_DEBUG
+       if (percpu_ref_init(&ca->ref, bch2_dev_ref_complete, 0, GFP_KERNEL))
+               goto err;
+#else
+       atomic_long_set(&ca->ref, 1);
+#endif
+
+       if (percpu_ref_init(&ca->io_ref, bch2_dev_io_ref_complete,
                            PERCPU_REF_INIT_DEAD, GFP_KERNEL) ||
            !(ca->sb_read_scratch = (void *) __get_free_page(GFP_KERNEL)) ||
            bch2_dev_buckets_alloc(c, ca) ||
@@ -1665,7 +1675,12 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
        rcu_assign_pointer(c->devs[ca->dev_idx], NULL);
        mutex_unlock(&c->sb_lock);
 
+#ifndef CONFIG_BCACHEFS_DEBUG
        percpu_ref_kill(&ca->ref);
+#else
+       ca->dying = true;
+       bch2_dev_put(ca);
+#endif
        wait_for_completion(&ca->ref_completion);
 
        bch2_dev_free(ca);