summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2016-02-12 17:46:17 -0900
committerKent Overstreet <kent.overstreet@gmail.com>2016-10-07 12:35:29 -0800
commit545395b2cc66d36c0b3056ea53474552cd3b1446 (patch)
tree85aaded54717d9b7968a73ba016f923e52c252c8
parent250a52ce9837d34d503aabe9f4708168689cca8d (diff)
bcache: fix a really obnoxious gc/btree split deadlock
-rw-r--r--drivers/md/bcache/btree_update.c27
1 files changed, 17 insertions, 10 deletions
diff --git a/drivers/md/bcache/btree_update.c b/drivers/md/bcache/btree_update.c
index f95465384eb6..9146d7409baa 100644
--- a/drivers/md/bcache/btree_update.c
+++ b/drivers/md/bcache/btree_update.c
@@ -1558,7 +1558,13 @@ static int bch_btree_split_leaf(struct btree_iter *iter, unsigned flags)
struct btree *b = iter->nodes[0];
struct btree_reserve *reserve;
struct async_split *as;
- int ret;
+ int ret = -EINTR;
+
+ /* Hack, because gc and splitting nodes doesn't mix yet: */
+ if (!down_read_trylock(&c->gc_lock)) {
+ bch_btree_iter_unlock(iter);
+ down_read(&c->gc_lock);
+ }
/*
* XXX: figure out how far we might need to split,
@@ -1566,26 +1572,27 @@ static int bch_btree_split_leaf(struct btree_iter *iter, unsigned flags)
*/
iter->locks_want = BTREE_MAX_DEPTH;
if (!bch_btree_iter_upgrade(iter))
- return -EINTR;
+ goto out_unlock;
reserve = bch_btree_reserve_get(c, b, iter, 0,
!(flags & BTREE_INSERT_NOFAIL));
- if (IS_ERR(reserve))
- return PTR_ERR(reserve);
+ if (IS_ERR(reserve)) {
+ ret = PTR_ERR(reserve);
+ goto out_unlock;
+ }
as = bch_async_split_alloc(b, iter);
if (!as) {
- bch_btree_reserve_put(c, reserve);
- return -EIO;
+ ret = -EIO;
+ goto out_put_reserve;
}
- /* Hack, because gc and splitting nodes doesn't mix yet: */
- down_read(&c->gc_lock);
ret = btree_split(b, iter, NULL, reserve, as);
- up_read(&c->gc_lock);
+out_put_reserve:
bch_btree_reserve_put(c, reserve);
-
+out_unlock:
+ up_read(&c->gc_lock);
return ret;
}