Commit 4167b4cd authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Fix a workqueue deadlock

writes running out of a workqueue (via dio path) could block and prevent
other writes from calling bch2_write_index() and completing.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent f36dff28
...@@ -604,7 +604,9 @@ static void bch2_write_index(struct closure *cl) ...@@ -604,7 +604,9 @@ static void bch2_write_index(struct closure *cl)
__bch2_write_index(op); __bch2_write_index(op);
if (!op->error && (op->flags & BCH_WRITE_FLUSH)) { if (!(op->flags & BCH_WRITE_DONE)) {
continue_at(cl, __bch2_write, index_update_wq(op));
} else if (!op->error && (op->flags & BCH_WRITE_FLUSH)) {
bch2_journal_flush_seq_async(&c->journal, bch2_journal_flush_seq_async(&c->journal,
*op_journal_seq(op), *op_journal_seq(op),
cl); cl);
...@@ -1104,8 +1106,15 @@ static void __bch2_write(struct closure *cl) ...@@ -1104,8 +1106,15 @@ static void __bch2_write(struct closure *cl)
if (ret < 0) if (ret < 0)
goto err; goto err;
if (ret) if (ret) {
skip_put = false; skip_put = false;
} else {
/*
* for the skip_put optimization this has to be set
* before we submit the bio:
*/
op->flags |= BCH_WRITE_DONE;
}
bio->bi_end_io = bch2_write_endio; bio->bi_end_io = bch2_write_endio;
bio->bi_private = &op->cl; bio->bi_private = &op->cl;
...@@ -1128,16 +1137,30 @@ static void __bch2_write(struct closure *cl) ...@@ -1128,16 +1137,30 @@ static void __bch2_write(struct closure *cl)
return; return;
err: err:
op->error = ret; op->error = ret;
op->flags |= BCH_WRITE_DONE;
continue_at(cl, bch2_write_index, index_update_wq(op)); continue_at(cl, bch2_write_index, index_update_wq(op));
return; return;
flush_io: flush_io:
/*
* If the write can't all be submitted at once, we generally want to
* block synchronously as that signals backpressure to the caller.
*
* However, if we're running out of a workqueue, we can't block here
* because we'll be blocking other work items from completing:
*/
if (current->flags & PF_WQ_WORKER) {
continue_at(cl, bch2_write_index, index_update_wq(op));
return;
}
closure_sync(cl); closure_sync(cl);
if (!bch2_keylist_empty(&op->insert_keys)) { if (!bch2_keylist_empty(&op->insert_keys)) {
__bch2_write_index(op); __bch2_write_index(op);
if (op->error) { if (op->error) {
op->flags |= BCH_WRITE_DONE;
continue_at_nobarrier(cl, bch2_write_done, NULL); continue_at_nobarrier(cl, bch2_write_done, NULL);
return; return;
} }
...@@ -1183,6 +1206,8 @@ static void bch2_write_data_inline(struct bch_write_op *op, unsigned data_len) ...@@ -1183,6 +1206,8 @@ static void bch2_write_data_inline(struct bch_write_op *op, unsigned data_len)
bch2_keylist_push(&op->insert_keys); bch2_keylist_push(&op->insert_keys);
op->flags |= BCH_WRITE_WROTE_DATA_INLINE; op->flags |= BCH_WRITE_WROTE_DATA_INLINE;
op->flags |= BCH_WRITE_DONE;
continue_at_nobarrier(cl, bch2_write_index, NULL); continue_at_nobarrier(cl, bch2_write_index, NULL);
return; return;
err: err:
......
...@@ -40,6 +40,7 @@ enum bch_write_flags { ...@@ -40,6 +40,7 @@ enum bch_write_flags {
/* Internal: */ /* Internal: */
BCH_WRITE_JOURNAL_SEQ_PTR = (1 << 10), BCH_WRITE_JOURNAL_SEQ_PTR = (1 << 10),
BCH_WRITE_SKIP_CLOSURE_PUT = (1 << 11), BCH_WRITE_SKIP_CLOSURE_PUT = (1 << 11),
BCH_WRITE_DONE = (1 << 12),
}; };
static inline u64 *op_journal_seq(struct bch_write_op *op) static inline u64 *op_journal_seq(struct bch_write_op *op)
......
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