bch2_journal_pin_drop(&c->journal, &as->journal);
bch2_journal_pin_flush(&c->journal, &as->journal);
- BUG_ON((as->nr_new_nodes || as->nr_pending) &&
- !bch2_journal_error(&c->journal));;
+ BUG_ON(as->nr_new_nodes || as->nr_pending);
if (as->reserve)
bch2_btree_reserve_put(c, as->reserve);
+ list_del(&as->unwritten_list);
list_del(&as->list);
closure_debug_destroy(&as->cl);
static void btree_update_nodes_written(struct closure *cl)
{
struct btree_update *as = container_of(cl, struct btree_update, cl);
- struct btree *new_nodes[BTREE_MAX_DEPTH * 2 + GC_MERGE_NODES];
- unsigned nr_new_nodes;
+ struct btree *nodes_need_write[BTREE_MAX_DEPTH * 2 + GC_MERGE_NODES + 1];
+ unsigned nr_nodes_need_write;
struct journal_res res = { 0 };
struct bch_fs *c = as->c;
+ struct btree_root *r;
struct btree *b;
- struct bset *i;
int ret;
/*
mutex_lock(&c->btree_interior_update_lock);
as->nodes_written = true;
again:
- nr_new_nodes = 0;
+ nr_nodes_need_write = 0;
as = list_first_entry_or_null(&c->btree_interior_updates_unwritten,
struct btree_update, unwritten_list);
if (!as || !as->nodes_written) {
goto again;
}
- list_del(&as->unwritten_list);
-
ret = bch2_journal_res_get(&c->journal, &res, as->journal_u64s,
JOURNAL_RES_GET_NONBLOCK|
JOURNAL_RES_GET_RESERVED);
if (ret == -EAGAIN) {
unsigned u64s = as->journal_u64s;
- six_unlock_write(&b->c.lock);
- six_unlock_intent(&b->c.lock);
+ if (b) {
+ six_unlock_write(&b->c.lock);
+ six_unlock_intent(&b->c.lock);
+ }
mutex_unlock(&c->btree_interior_update_lock);
}
}
- if (ret) {
- BUG_ON(!bch2_journal_error(&c->journal));
- /* can't unblock btree writes */
- goto free_update;
- }
-
- {
+ if (!ret) {
struct journal_buf *buf = &c->journal.buf[res.idx];
struct jset_entry *entry = vstruct_idx(buf->data, res.offset);
res.offset += as->journal_u64s;
res.u64s -= as->journal_u64s;
memcpy_u64s(entry, as->journal_entries, as->journal_u64s);
+ } else {
+ /*
+ * On journal error we have to run most of the normal path so
+ * that shutdown works - unblocking btree node writes in
+ * particular and writing them if needed - except for
+ * journalling the update:
+ */
+
+ BUG_ON(!bch2_journal_error(&c->journal));
}
switch (as->mode) {
BUG();
case BTREE_INTERIOR_UPDATING_NODE:
/* @b is the node we did the final insert into: */
- BUG_ON(!res.ref);
+
+ /*
+ * On failure to get a journal reservation, we still have to
+ * unblock the write and allow most of the write path to happen
+ * so that shutdown works, but the i->journal_seq mechanism
+ * won't work to prevent the btree write from being visible (we
+ * didn't get a journal sequence number) - instead
+ * __bch2_btree_node_write() doesn't do the actual write if
+ * we're in journal error state:
+ */
list_del(&as->write_blocked_list);
- i = btree_bset_last(b);
- i->journal_seq = cpu_to_le64(
- max(res.seq,
- le64_to_cpu(i->journal_seq)));
+ if (!ret) {
+ struct bset *i = btree_bset_last(b);
+
+ i->journal_seq = cpu_to_le64(
+ max(res.seq,
+ le64_to_cpu(i->journal_seq)));
+
+ bch2_btree_add_journal_pin(c, b, res.seq);
+ }
+
+ nodes_need_write[nr_nodes_need_write++] = b;
- bch2_btree_add_journal_pin(c, b, res.seq);
+ six_unlock_write(&b->c.lock);
+ six_unlock_intent(&b->c.lock);
break;
case BTREE_INTERIOR_UPDATING_AS:
BUG_ON(b);
break;
- case BTREE_INTERIOR_UPDATING_ROOT: {
- struct btree_root *r = &c->btree_roots[as->btree_id];
+ case BTREE_INTERIOR_UPDATING_ROOT:
+ r = &c->btree_roots[as->btree_id];
BUG_ON(b);
mutex_unlock(&c->btree_root_lock);
break;
}
- }
bch2_journal_pin_drop(&c->journal, &as->journal);
bch2_journal_res_put(&c->journal, &res);
bch2_journal_preres_put(&c->journal, &as->journal_preres);
-free_update:
- /* Do btree write after dropping journal res: */
- if (b) {
- six_unlock_write(&b->c.lock);
- /*
- * b->write_blocked prevented it from being written, so
- * write it now if it needs to be written:
- */
- btree_node_write_if_need(c, b, SIX_LOCK_intent);
- six_unlock_intent(&b->c.lock);
- }
- if (!ret) {
- nr_new_nodes = as->nr_new_nodes;
- memcpy(new_nodes,
- as->new_nodes,
- as->nr_new_nodes * sizeof(struct btree *));
+ while (as->nr_new_nodes) {
+ b = as->new_nodes[--as->nr_new_nodes];
- while (as->nr_new_nodes) {
- struct btree *b = as->new_nodes[--as->nr_new_nodes];
+ BUG_ON(b->will_make_reachable != (unsigned long) as);
+ b->will_make_reachable = 0;
- BUG_ON(b->will_make_reachable != (unsigned long) as);
- b->will_make_reachable = 0;
- }
-
- while (as->nr_pending)
- bch2_btree_node_free_ondisk(c,
- &as->pending[--as->nr_pending], res.seq);
+ nodes_need_write[nr_nodes_need_write++] = b;
}
+ while (as->nr_pending)
+ bch2_btree_node_free_ondisk(c,
+ &as->pending[--as->nr_pending], res.seq);
+
__bch2_btree_update_free(as);
/*
* for flush_held_btree_writes() waiting on updates to flush or
* */
mutex_unlock(&c->btree_interior_update_lock);
- while (nr_new_nodes) {
- struct btree *b = new_nodes[--nr_new_nodes];
+ /* Do btree writes after dropping journal res/locks: */
+ while (nr_nodes_need_write) {
+ b = nodes_need_write[--nr_nodes_need_write];
+
btree_node_lock_type(c, b, SIX_LOCK_read);
bch2_btree_node_write_cond(c, b, btree_node_need_write(b));
six_unlock_read(&b->c.lock);
as->btree_id = id;
as->reserve = reserve;
INIT_LIST_HEAD(&as->write_blocked_list);
+ INIT_LIST_HEAD(&as->unwritten_list);
as->journal_preres = journal_preres;
bch2_keylist_init(&as->parent_keys, as->inline_keys);