diff options
author | Coly Li | 2019-11-13 16:03:15 +0800 |
---|---|---|
committer | Jens Axboe | 2019-11-13 15:42:49 -0700 |
commit | c0e0954e909c17b43d176ab219fc598964616ae6 (patch) | |
tree | 0f16001bfb90ec3539dc6fa0498d2d4a61b1c1d9 | |
parent | e2a7b9f4a19d3ecb8833ecba9e111e118e17f36c (diff) |
bcache: fix fifo index swapping condition in journal_pin_cmp()
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>
-rw-r--r-- | drivers/md/bcache/btree.c | 26 | ||||
-rw-r--r-- | drivers/md/bcache/journal.h | 4 |
2 files changed, 26 insertions, 4 deletions
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index ba434d9ac720..00523cd1db80 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -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); diff --git a/drivers/md/bcache/journal.h b/drivers/md/bcache/journal.h index f2ea34d5f431..06b3eaab7d16 100644 --- a/drivers/md/bcache/journal.h +++ b/drivers/md/bcache/journal.h @@ -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) \ |