diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-01-27 20:46:40 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-01-30 20:41:35 -0500 |
commit | c2c02d3afbf6ecf400eb9c71d366de0e710682c3 (patch) | |
tree | 7d29aa2dfa129d1a282a35d44963cb0577568da5 | |
parent | be06b3e44c36cac596a8e2193c8e5772c653a901 (diff) |
bcachefs: fix another deadlock
-rw-r--r-- | fs/bcachefs/btree_update_interior.c | 34 | ||||
-rw-r--r-- | fs/bcachefs/journal.c | 57 | ||||
-rw-r--r-- | fs/bcachefs/journal.h | 3 | ||||
-rw-r--r-- | fs/bcachefs/journal_types.h | 1 |
4 files changed, 77 insertions, 18 deletions
diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c index 4a75b2e8705c..2ce45bcecbbb 100644 --- a/fs/bcachefs/btree_update_interior.c +++ b/fs/bcachefs/btree_update_interior.c @@ -601,8 +601,7 @@ static void bch2_btree_update_free(struct btree_update *as) static void btree_update_nodes_reachable(struct closure *cl) { - struct btree_update *as = - container_of(cl, struct btree_update, cl); + struct btree_update *as = container_of(cl, struct btree_update, cl); struct bch_fs *c = as->c; bch2_journal_pin_drop(&c->journal, &as->journal); @@ -636,10 +635,26 @@ static void btree_update_nodes_reachable(struct closure *cl) bch2_btree_update_free(as); } +static void btree_update_wait_on_journal(struct closure *cl) +{ + struct btree_update *as = container_of(cl, struct btree_update, cl); + struct bch_fs *c = as->c; + int ret; + + ret = bch2_journal_open_seq_async(&c->journal, as->journal_seq, cl); + if (ret < 0) + goto err; + if (!ret) + continue_at(cl, btree_update_wait_on_journal, system_wq); + + bch2_journal_flush_seq_async(&c->journal, as->journal_seq, cl); +err: + continue_at(cl, btree_update_nodes_reachable, system_wq); +} + static void btree_update_nodes_written(struct closure *cl) { - struct btree_update *as = - container_of(cl, struct btree_update, cl); + struct btree_update *as = container_of(cl, struct btree_update, cl); struct bch_fs *c = as->c; struct btree *b; @@ -736,13 +751,10 @@ retry: */ bch2_journal_pin_drop(&c->journal, &as->journal); - /* - * And, do a journal write to write the pointer to the new root, - * then wait for it to complete before freeing the nodes we - * replaced: - */ - bch2_journal_meta_async(&c->journal, cl); - break; + as->journal_seq = bch2_journal_last_unwritten_seq(&c->journal); + + btree_update_wait_on_journal(cl); + return; } continue_at(cl, btree_update_nodes_reachable, system_wq); diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c index 5bfa7cd0ca4b..1870534df6d0 100644 --- a/fs/bcachefs/journal.c +++ b/fs/bcachefs/journal.c @@ -31,6 +31,12 @@ static void journal_pin_add_entry(struct journal *, struct journal_entry_pin *, journal_pin_flush_fn); +static inline void journal_wake(struct journal *j) +{ + wake_up(&j->wait); + closure_wake_up(&j->async_wait); +} + static inline struct journal_buf *journal_cur_buf(struct journal *j) { return j->buf + j->reservations.idx; @@ -1280,7 +1286,7 @@ void bch2_journal_halt(struct journal *j) } while ((v = atomic64_cmpxchg(&j->reservations.counter, old.v, new.v)) != old.v); - wake_up(&j->wait); + journal_wake(j); closure_wake_up(&journal_cur_buf(j)->wait); closure_wake_up(&journal_prev_buf(j)->wait); } @@ -1449,7 +1455,7 @@ static int journal_entry_open(struct journal *j) mod_delayed_work(system_freezable_wq, &j->write_work, msecs_to_jiffies(j->write_delay_ms)); - wake_up(&j->wait); + journal_wake(j); return 1; } @@ -1545,7 +1551,7 @@ int bch2_journal_replay(struct bch_fs *c, struct list_head *list) } if (atomic_dec_and_test(&j->replay_pin_list->count)) - wake_up(&j->wait); + journal_wake(j); } j->replay_pin_list = NULL; @@ -1712,7 +1718,7 @@ static void journal_reclaim_fast(struct journal *j) } if (popped) - wake_up(&j->wait); + journal_wake(j); } /* @@ -1741,7 +1747,7 @@ static inline void __journal_pin_add(struct journal *j, * If the journal is currently full, we might want to call flush_fn * immediately: */ - wake_up(&j->wait); + journal_wake(j); } static void journal_pin_add_entry(struct journal *j, @@ -1991,7 +1997,7 @@ static void journal_reclaim_work(struct work_struct *work) ja->last_idx = (ja->last_idx + 1) % ja->nr; spin_unlock(&j->lock); - wake_up(&j->wait); + journal_wake(j); } /* @@ -2231,7 +2237,7 @@ out: &j->reservations.counter); closure_wake_up(&w->wait); - wake_up(&j->wait); + journal_wake(j); if (test_bit(JOURNAL_NEED_WRITE, &j->flags)) mod_delayed_work(system_freezable_wq, &j->write_work, 0); @@ -2536,6 +2542,43 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res, return ret < 0 ? ret : 0; } +u64 bch2_journal_last_unwritten_seq(struct journal *j) +{ + u64 seq; + + spin_lock(&j->lock); + seq = atomic64_read(&j->seq); + if (j->reservations.prev_buf_unwritten) + seq--; + spin_unlock(&j->lock); + + return seq; +} + +int bch2_journal_open_seq_async(struct journal *j, u64 seq, struct closure *parent) +{ + int ret; + + spin_lock(&j->lock); + BUG_ON(seq > atomic64_read(&j->seq)); + + if (seq < atomic64_read(&j->seq) || + journal_entry_is_open(j)) { + spin_unlock(&j->lock); + return 1; + } + + ret = journal_entry_open(j); + if (!ret) + closure_wait(&j->async_wait, parent); + spin_unlock(&j->lock); + + if (!ret) + journal_reclaim_work(&j->reclaim_work.work); + + return ret; +} + void bch2_journal_wait_on_seq(struct journal *j, u64 seq, struct closure *parent) { spin_lock(&j->lock); diff --git a/fs/bcachefs/journal.h b/fs/bcachefs/journal.h index ebb846438915..9461d2d8a23e 100644 --- a/fs/bcachefs/journal.h +++ b/fs/bcachefs/journal.h @@ -354,6 +354,9 @@ out: return 0; } +u64 bch2_journal_last_unwritten_seq(struct journal *); +int bch2_journal_open_seq_async(struct journal *, u64, struct closure *); + void bch2_journal_wait_on_seq(struct journal *, u64, struct closure *); void bch2_journal_flush_seq_async(struct journal *, u64, struct closure *); void bch2_journal_flush_async(struct journal *, struct closure *); diff --git a/fs/bcachefs/journal_types.h b/fs/bcachefs/journal_types.h index 52fc7a6ca27d..e39b18f27058 100644 --- a/fs/bcachefs/journal_types.h +++ b/fs/bcachefs/journal_types.h @@ -140,6 +140,7 @@ struct journal { /* Used when waiting because the journal was full */ wait_queue_head_t wait; + struct closure_waitlist async_wait; struct closure io; struct delayed_work write_work; |