Commit b301105b authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: normalize_read_intent_locks

This is a new approach to avoiding the self deadlock we'd get if we
tried to take a write lock on a node while holding a read lock - we
simply upgrade the readers to intent.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
parent 8ee0134e
...@@ -48,14 +48,12 @@ static inline bool same_leaf_as_next(struct btree_trans *trans, ...@@ -48,14 +48,12 @@ static inline bool same_leaf_as_next(struct btree_trans *trans,
insert_l(&i[0])->b == insert_l(&i[1])->b; insert_l(&i[0])->b == insert_l(&i[1])->b;
} }
inline void bch2_btree_node_lock_for_insert(struct btree_trans *trans, static inline void bch2_btree_node_prep_for_write(struct btree_trans *trans,
struct btree_path *path, struct btree_path *path,
struct btree *b) struct btree *b)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
bch2_btree_node_lock_write(trans, path, b);
if (path->cached) if (path->cached)
return; return;
...@@ -71,6 +69,14 @@ inline void bch2_btree_node_lock_for_insert(struct btree_trans *trans, ...@@ -71,6 +69,14 @@ inline void bch2_btree_node_lock_for_insert(struct btree_trans *trans,
bch2_btree_init_next(trans, b); bch2_btree_init_next(trans, b);
} }
void bch2_btree_node_lock_for_insert(struct btree_trans *trans,
struct btree_path *path,
struct btree *b)
{
bch2_btree_node_lock_write(trans, path, b);
bch2_btree_node_prep_for_write(trans, path, b);
}
/* Inserting into a given leaf node (last stage of insert): */ /* Inserting into a given leaf node (last stage of insert): */
/* Handle overwrites and do insert, for non extents: */ /* Handle overwrites and do insert, for non extents: */
...@@ -495,6 +501,50 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, ...@@ -495,6 +501,50 @@ bch2_trans_commit_write_locked(struct btree_trans *trans,
return ret; return ret;
} }
static inline void upgrade_readers(struct btree_trans *trans, struct btree_path *path)
{
struct btree *b = path_l(path)->b;
do {
if (path->nodes_locked &&
path->nodes_locked != path->nodes_intent_locked)
BUG_ON(!bch2_btree_path_upgrade(trans, path, path->level + 1));
} while ((path = prev_btree_path(trans, path)) &&
path_l(path)->b == b);
}
/*
* Check for nodes that we have both read and intent locks on, and upgrade the
* readers to intent:
*/
static inline void normalize_read_intent_locks(struct btree_trans *trans)
{
struct btree_path *path;
unsigned i, nr_read = 0, nr_intent = 0;
trans_for_each_path_inorder(trans, path, i) {
struct btree_path *next = i + 1 < trans->nr_sorted
? trans->paths + trans->sorted[i + 1]
: NULL;
if (path->nodes_locked) {
if (path->nodes_intent_locked)
nr_intent++;
else
nr_read++;
}
if (!next || path_l(path)->b != path_l(next)->b) {
if (nr_read && nr_intent)
upgrade_readers(trans, path);
nr_read = nr_intent = 0;
}
}
bch2_trans_verify_locks(trans);
}
/* /*
* Get journal reservation, take write locks, and attempt to do btree update(s): * Get journal reservation, take write locks, and attempt to do btree update(s):
*/ */
...@@ -538,9 +588,6 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, ...@@ -538,9 +588,6 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans,
} }
} }
trans_for_each_update(trans, i)
BUG_ON(!btree_node_intent_locked(i->path, i->level));
ret = bch2_journal_preres_get(&c->journal, ret = bch2_journal_preres_get(&c->journal,
&trans->journal_preres, trans->journal_preres_u64s, &trans->journal_preres, trans->journal_preres_u64s,
JOURNAL_RES_GET_NONBLOCK| JOURNAL_RES_GET_NONBLOCK|
...@@ -586,12 +633,14 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, ...@@ -586,12 +633,14 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans,
} }
btree_insert_entry_checks(trans, i); btree_insert_entry_checks(trans, i);
} }
bch2_trans_verify_locks(trans);
normalize_read_intent_locks(trans);
trans_for_each_update(trans, i) trans_for_each_update(trans, i)
if (!same_leaf_as_prev(trans, i)) if (!same_leaf_as_prev(trans, i)) {
bch2_btree_node_lock_for_insert(trans, i->path, btree_node_lock_type(c, insert_l(i)->b, SIX_LOCK_write);
insert_l(i)->b); bch2_btree_node_prep_for_write(trans, i->path, insert_l(i)->b);
}
ret = bch2_trans_commit_write_locked(trans, stopped_at, trace_ip); ret = bch2_trans_commit_write_locked(trans, stopped_at, trace_ip);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment