bcache: fix fifo index swapping condition in journal_pin_cmp()
authorColy Li <colyli@suse.de>
Wed, 13 Nov 2019 08:03:15 +0000 (16:03 +0800)
committerJens Axboe <axboe@kernel.dk>
Wed, 13 Nov 2019 22:42:49 +0000 (15:42 -0700)
Fifo structure journal.pin is implemented by a cycle buffer, if the back
index reaches highest location of the cycle buffer, it will be swapped
to 0. Once the swapping happens, it means a smaller fifo index might be
associated to a newer journal entry. So the btree node with oldest
journal entry won't be selected in bch_btree_leaf_dirty() to reference
the dirty B+tree leaf node. This problem may cause bcache journal won't
protect unflushed oldest B+tree dirty leaf node in power failure, and
this B+tree leaf node is possible to beinconsistent after reboot from
power failure.

This patch fixes the fifo index comparing logic in journal_pin_cmp(),
to avoid potential corrupted B+tree leaf node when the back index of
journal pin is swapped.

Signed-off-by: Coly Li <colyli@suse.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/md/bcache/btree.c
drivers/md/bcache/journal.h

index ba434d9..00523cd 100644 (file)
@@ -528,6 +528,32 @@ static void btree_node_write_work(struct work_struct *w)
        mutex_unlock(&b->write_lock);
 }
 
+/* return true if journal pin 'l' is newer than 'r' */
+static bool journal_pin_cmp(struct cache_set *c,
+                           atomic_t *l,
+                           atomic_t *r)
+{
+       int l_idx, r_idx, f_idx, b_idx;
+       bool ret = false;
+
+       l_idx = fifo_idx(&(c)->journal.pin, (l));
+       r_idx = fifo_idx(&(c)->journal.pin, (r));
+       f_idx = (c)->journal.pin.front;
+       b_idx = (c)->journal.pin.back;
+
+       if (l_idx > r_idx)
+               ret = true;
+       /* in case fifo back pointer is swapped */
+       if (b_idx < f_idx) {
+               if (l_idx <= b_idx && r_idx >= f_idx)
+                       ret = true;
+               else if (l_idx >= f_idx && r_idx <= b_idx)
+                       ret = false;
+       }
+
+       return ret;
+}
+
 static void bch_btree_leaf_dirty(struct btree *b, atomic_t *journal_ref)
 {
        struct bset *i = btree_bset_last(b);
index f2ea34d..06b3eaa 100644 (file)
@@ -157,10 +157,6 @@ struct journal_device {
 };
 
 #define BTREE_FLUSH_NR 8
-
-#define journal_pin_cmp(c, l, r)                               \
-       (fifo_idx(&(c)->journal.pin, (l)) > fifo_idx(&(c)->journal.pin, (r)))
-
 #define JOURNAL_PIN    20000
 
 #define journal_full(j)                                                \