summaryrefslogtreecommitdiff
path: root/drivers/md/bcache/btree.c
diff options
context:
space:
mode:
authorColy Li <colyli@suse.de>2019-11-13 11:03:15 +0300
committerJens Axboe <axboe@kernel.dk>2019-11-14 01:42:49 +0300
commitc0e0954e909c17b43d176ab219fc598964616ae6 (patch)
tree0f16001bfb90ec3539dc6fa0498d2d4a61b1c1d9 /drivers/md/bcache/btree.c
parente2a7b9f4a19d3ecb8833ecba9e111e118e17f36c (diff)
downloadlinux-c0e0954e909c17b43d176ab219fc598964616ae6.tar.xz
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>
Diffstat (limited to 'drivers/md/bcache/btree.c')
-rw-r--r--drivers/md/bcache/btree.c26
1 files changed, 26 insertions, 0 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);