[[!format c """ struct btree_insert_multi { struct btree_iter iter; struct bkey_i *k; }; static void multi_lock_write(struct btree_insert_multi *m, unsigned nr) { struct btree *b = m[nr].iter.nodes[0]; unsigned i; for (i = 0; i < nr; i++) if (b == m[i].iter.nodes[0]) return; /* already locked */ btree_node_lock_for_insert(b, &m[nr].iter); } static void multi_unlock_write(struct btree_insert_multi *m, unsigned nr) { struct btree *b = m[nr].iter.nodes[0]; unsigned i; for (i = 0; i < nr; i++) if (b == m[i].iter.nodes[0]) return; /* already locked */ btree_node_unlock_write(b, &m[nr].iter); } int bch_btree_insert_at_multi(struct btree_insert_multi *m, unsigned nr, u64 *journal_seq, unsigned flags) { struct cache_set *c = m[0].iter.c; struct journal_res res = { 0, 0 }; struct btree_insert_multi *i; struct btree_iter *split; unsigned u64s = 0; for (i = m; i < m + nr; i++) u64s += jset_u64s(i->k->k.u64s); retry: if (bch_journal_res_get(&c->journal, &res, u64s, u64s)) return -EIO; for (i = m; i < m + nr; i++) { struct btree *b = i->iter.nodes[0]; multi_lock_write(m, i - m); if (!__have_enough_space(b, i->k->k.u64s)) goto split; } for (i = m; i < m + nr; i++) BUG_ON(!btree_insert_key(&i->iter, i->iter.nodes[0], &i->iter.node_iters[0], &keylist_single(i->k), NULL, &res, journal_seq, flags)); do { multi_unlock_write(m, --i - m); } while (i != m); bch_journal_res_put(&c->journal, &res); for (i = m; i < m + nr; i++) bch_btree_node_write_lazy(i->iter.nodes[0], &i->iter); return 0; split: split = &i->iter; do { multi_unlock_write(m, i - m); } while (i-- != m); /* * XXX: Do we need to drop our journal res for the split? */ bch_journal_res_put(&c->journal, &res); { struct btree *b = split->nodes[0]; struct btree_split_state state; int ret; closure_init_stack(&state.stack_cl); bch_keylist_init(&state.parent_keys, state.inline_keys, ARRAY_SIZE(state.inline_keys)); /* * XXX: figure out how far we might need to split, * instead of locking/reserving all the way to the root: */ split->locks_want = BTREE_MAX_DEPTH; if (!bch_btree_iter_upgrade(split)) return -EINTR; state.reserve = bch_btree_reserve_get(c, b, split, 0, !(flags & BTREE_INSERT_NOFAIL)); if (IS_ERR(state.reserve)) return PTR_ERR(state.reserve); ret = btree_split(b, split, NULL, flags, &state); bch_btree_reserve_put(c, state.reserve); if (ret) return ret; goto retry; } } int bch_dirent_rename(struct cache_set *c, u64 src_dir, const struct qstr *src_name, u64 dst_dir, const struct qstr *dst_name, u64 *journal_seq, bool overwriting) { struct btree_insert_multi trans[2]; struct btree_iter *src_iter = &trans[0].iter; struct btree_iter *dst_iter = &trans[1].iter; struct bkey_s_c k; struct bkey_s_c_dirent src; struct bkey_i_dirent *dst; struct bkey_i delete; struct keylist keys; int ret; dst = dirent_create_key(&keys, 0, dst_name, 0); if (!dst) return -ENOMEM; bch_btree_iter_init_intent(src_iter, c, BTREE_ID_DIRENTS, POS(src_dir, bch_dirent_hash(src_name))); bch_btree_iter_init_intent(dst_iter, c, BTREE_ID_DIRENTS, POS(dst_dir, bch_dirent_hash(dst_name))); bch_btree_iter_link(src_iter, dst_iter); do { k = __dirent_find(src_iter, src_dir, src_name); if (IS_ERR(k.k)) { ret = PTR_ERR(k.k); goto err; } src = bkey_s_c_to_dirent(k); bkey_init(&delete.k); delete.k.p = src.k->p; delete.k.type = BCH_DIRENT_WHITEOUT; k = overwriting ? __dirent_find(dst_iter, dst_dir, dst_name) : __dirent_find_hole(dst_iter, dst_dir, dst_name); if (IS_ERR(k.k)) { ret = PTR_ERR(k.k); goto err; } dst->v.d_inum = src.v->d_inum; dst->v.d_type = src.v->d_type; dst->k.p = k.k->p; trans[0].k = &delete; trans[1].k = &dst->k_i; ret = bch_btree_insert_at_multi(trans, ARRAY_SIZE(trans), journal_seq, 0); bch_btree_iter_unlock(src_iter); bch_btree_iter_unlock(dst_iter); } while (ret == -EINTR); bch_keylist_free(&keys); return 0; err: ret = bch_btree_iter_unlock(src_iter) ?: ret; ret = bch_btree_iter_unlock(dst_iter) ?: ret; bch_keylist_free(&keys); return ret; } """]]