diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2015-08-14 22:24:34 -0800 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2016-10-07 12:34:44 -0800 |
commit | 3b4484c3888dd4132fffd589be059c66122dbc58 (patch) | |
tree | e75c448e0013a72a6066d27c129e81ebe9e5d3be | |
parent | 05672b45c40c4b5e280828bbc8dd597cbc5b3edf (diff) |
bcache: Fix an accounting bug
-rw-r--r-- | drivers/md/bcache/btree_update.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/md/bcache/btree_update.c b/drivers/md/bcache/btree_update.c index f8c0fc2f0767..c6feb4c33eb8 100644 --- a/drivers/md/bcache/btree_update.c +++ b/drivers/md/bcache/btree_update.c @@ -123,19 +123,32 @@ static void bch_pending_btree_node_free_insert_done(struct cache_set *c, found: d->index_update_done = true; + /* + * We're dropping @k from the btree, but it's still live until the index + * update is persistent so we need to keep a reference around for mark + * and sweep to find - that's primarily what the btree_node_pending_free + * list is for. + * + * So here (when we set index_update_done = true), we're moving an + * existing reference to a different part of the larger "gc keyspace" - + * and the new position comes after the old position, since GC marks the + * pending free list after it walks the btree. + * + * If we move the reference while mark and sweep is _between_ the old + * and the new position, mark and sweep will see the reference twice and + * it'll get double accounted - so check for that here and subtract to + * cancel out one of mark and sweep's markings if necessary: + */ + if ((b ? !gc_will_visit_node(c, b) : !gc_will_visit_root(c, id)) && gc_will_visit(c, GC_PHASE_PENDING_DELETE, POS_MIN, 0)) bch_mark_pointers(c, NULL, bkey_i_to_s_c_extent(&d->key), - CACHE_BTREE_NODE_SIZE(&c->sb), + -CACHE_BTREE_NODE_SIZE(&c->sb), false, true, true); - /* - * XXX; check gc position and mark/unmark as needed - */ - mutex_unlock(&c->btree_node_pending_free_lock); } |