/* * When deleting, check if we need to emit a whiteout (because we're overwriting * something in an ancestor snapshot)
*/ staticint need_whiteout_for_snapshot(struct btree_trans *trans, enum btree_id btree_id, struct bpos pos)
{ struct btree_iter iter; struct bkey_s_c k;
u32 snapshot = pos.snapshot; int ret;
if (!bch2_snapshot_parent(trans->c, pos.snapshot)) return 0;
/* * If we're going to be splitting a compressed extent, note it * so that __bch2_trans_commit() can increase our disk * reservation:
*/ if (nr_splits > 1 &&
(compressed_sectors = bch2_bkey_sectors_compressed(old)))
trans->extra_disk_res += compressed_sectors * (nr_splits - 1);
if (front_split) {
update = bch2_bkey_make_mut_noupdate(trans, old); if ((ret = PTR_ERR_OR_ZERO(update))) return ret;
bch2_cut_back(new_start, update);
ret = bch2_insert_snapshot_whiteouts(trans, btree_id,
old.k->p, update->k.p) ?:
bch2_btree_insert_nonextent(trans, btree_id, update,
BTREE_UPDATE_internal_snapshot_node|flags); if (ret) return ret;
}
/* If we're overwriting in a different snapshot - middle split: */ if (middle_split) {
update = bch2_bkey_make_mut_noupdate(trans, old); if ((ret = PTR_ERR_OR_ZERO(update))) return ret;
bch2_trans_iter_init(trans, &iter, btree_id, bkey_start_pos(&insert->k),
BTREE_ITER_intent|
BTREE_ITER_with_updates|
BTREE_ITER_not_extents);
k = bch2_btree_iter_peek_max(trans, &iter, POS(insert->k.p.inode, U64_MAX)); if ((ret = bkey_err(k))) goto err; if (!k.k) goto out;
if (bkey_eq(k.k->p, bkey_start_pos(&insert->k))) { if (bch2_bkey_maybe_mergable(k.k, &insert->k)) {
ret = extent_front_merge(trans, &iter, k, &insert, flags); if (ret) goto err;
}
goto next;
}
while (bkey_gt(insert->k.p, bkey_start_pos(k.k))) { bool done = bkey_lt(insert->k.p, k.k->p);
ret = bch2_trans_update_extent_overwrite(trans, &iter, flags, k, bkey_i_to_s_c(insert)); if (ret) goto err;
if (done) goto out;
next:
bch2_btree_iter_advance(trans, &iter);
k = bch2_btree_iter_peek_max(trans, &iter, POS(insert->k.p.inode, U64_MAX)); if ((ret = bkey_err(k))) goto err; if (!k.k) goto out;
}
if (bch2_bkey_maybe_mergable(&insert->k, k.k)) {
ret = extent_back_merge(trans, &iter, insert, k); if (ret) goto err;
}
out: if (!bkey_deleted(&insert->k))
ret = bch2_btree_insert_nonextent(trans, btree_id, insert, flags);
err:
bch2_trans_iter_exit(trans, &iter);
return ret;
}
static noinline int flush_new_cached_update(struct btree_trans *trans, struct btree_insert_entry *i, enum btree_iter_update_trigger_flags flags, unsignedlong ip)
{ struct bkey k; int ret;
btree_path_idx_t path_idx =
bch2_path_get(trans, i->btree_id, i->old_k.p, 1, 0,
BTREE_ITER_intent, _THIS_IP_);
ret = bch2_btree_path_traverse(trans, path_idx, 0); if (ret) goto out;
/* * The old key in the insert entry might actually refer to an existing * key in the btree that has been deleted from cache and not yet * flushed. Check for this and skip the flush so we don't run triggers * against a stale key.
*/
bch2_btree_path_peek_slot_exact(btree_path, &k); if (!bkey_deleted(&k)) goto out;
/* * Pending updates are kept sorted: first, find position of new update, * then delete/trim any updates the new update overwrites:
*/ for (i = trans->updates; i < trans->updates + trans->nr_updates; i++) {
cmp = btree_insert_entry_cmp(&n, i); if (cmp <= 0) break;
}
bool overwrite = !cmp && i < trans->updates + trans->nr_updates;
if (overwrite) {
EBUG_ON(i->insert_trigger_run || i->overwrite_trigger_run);
/* * If a key is present in the key cache, it must also exist in the * btree - this is necessary for cache coherency. When iterating over * a btree that's cached in the key cache, the btree iter code checks * the key cache - but the key has to exist in the btree for that to * work:
*/ if (path->cached && !i->old_btree_u64s) return flush_new_cached_update(trans, i, flags, ip);
/* * This could probably be more efficient for extents:
*/
/* * For extents, iter.pos won't necessarily be the same as * bkey_start_pos(k.k) (for non extents they always will be the * same). It's important that we delete starting from iter.pos * because the range we want to delete could start in the middle * of k. * * (bch2_btree_iter_peek() does guarantee that iter.pos >= * bkey_start_pos(k.k)).
*/ delete.k.p = iter.pos;
if (iter.flags & BTREE_ITER_is_extents)
bch2_key_resize(&delete.k,
bpos_min(end, k.k->p).offset -
iter.pos.offset);
ret = bch2_trans_update(trans, &iter, &delete, update_flags) ?:
bch2_trans_commit(trans, &disk_res, journal_seq,
BCH_TRANS_COMMIT_no_enospc);
bch2_disk_reservation_put(trans->c, &disk_res);
err: /* * the bch2_trans_begin() call is in a weird place because we * need to call it after every transaction commit, to avoid path * overflow, but don't want to call it if the delete operation * is a no-op and we have no work to do:
*/
bch2_trans_begin(trans);
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
ret = 0; if (ret) break;
}
bch2_trans_iter_exit(trans, &iter);
return ret ?: trans_was_restarted(trans, restart_count);
}
/* * bch_btree_delete_range - delete everything within a given range * * Range is a half open interval - [start, end)
*/ int bch2_btree_delete_range(struct bch_fs *c, enum btree_id id, struct bpos start, struct bpos end, unsigned update_flags,
u64 *journal_seq)
{ int ret = bch2_trans_run(c,
bch2_btree_delete_range_trans(trans, id, start, end,
update_flags, journal_seq)); if (ret == -BCH_ERR_transaction_restart_nested)
ret = 0; return ret;
}
int bch2_btree_bit_mod_iter(struct btree_trans *trans, struct btree_iter *iter, bool set)
{ struct bkey_i *k = bch2_trans_kmalloc(trans, sizeof(*k)); int ret = PTR_ERR_OR_ZERO(k); if (ret) return ret;
bkey_init(&k->k);
k->k.type = set ? KEY_TYPE_set : KEY_TYPE_deleted;
k->k.p = iter->pos; if (iter->flags & BTREE_ITER_is_extents)
bch2_key_resize(&k->k, 1);
int bch2_trans_log_msg(struct btree_trans *trans, struct printbuf *buf)
{ int ret = buf->allocation_failure ? -BCH_ERR_ENOMEM_trans_log_msg : 0; if (ret) return ret;
/* * Use for logging messages during recovery to enable reserved space and avoid * blocking.
*/
__printf(2, 3) int bch2_journal_log_msg(struct bch_fs *c, constchar *fmt, ...)
{
va_list args; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.