1157 lines · 31.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h"#include "alloc_foreground.h"#include "btree_gc.h"#include "btree_io.h"#include "btree_iter.h"#include "btree_journal_iter.h"#include "btree_key_cache.h"#include "btree_update_interior.h"#include "btree_write_buffer.h"#include "buckets.h"#include "disk_accounting.h"#include "errcode.h"#include "error.h"#include "journal.h"#include "journal_io.h"#include "journal_reclaim.h"#include "replicas.h"#include "snapshot.h" #include <linux/prefetch.h> static const char * const trans_commit_flags_strs[] = {#define x(n, ...) #n,	BCH_TRANS_COMMIT_FLAGS()#undef x	NULL}; void bch2_trans_commit_flags_to_text(struct printbuf *out, enum bch_trans_commit_flags flags){	enum bch_watermark watermark = flags & BCH_WATERMARK_MASK; 	prt_printf(out, "watermark=%s", bch2_watermarks[watermark]); 	flags >>= BCH_WATERMARK_BITS;	if (flags) {		prt_char(out, ' ');		bch2_prt_bitflags(out, trans_commit_flags_strs, flags);	}} static void verify_update_old_key(struct btree_trans *trans, struct btree_insert_entry *i){#ifdef CONFIG_BCACHEFS_DEBUG	struct bch_fs *c = trans->c;	struct bkey u;	struct bkey_s_c k = bch2_btree_path_peek_slot_exact(trans->paths + i->path, &u); 	if (unlikely(trans->journal_replay_not_finished)) {		struct bkey_i *j_k =			bch2_journal_keys_peek_slot(c, i->btree_id, i->level, i->k->k.p); 		if (j_k)			k = bkey_i_to_s_c(j_k);	} 	u = *k.k;	u.needs_whiteout = i->old_k.needs_whiteout; 	BUG_ON(memcmp(&i->old_k, &u, sizeof(struct bkey)));	BUG_ON(i->old_v != k.v);#endif} static inline struct btree_path_level *insert_l(struct btree_trans *trans, struct btree_insert_entry *i){	return (trans->paths + i->path)->l + i->level;} static inline bool same_leaf_as_prev(struct btree_trans *trans,				     struct btree_insert_entry *i){	return i != trans->updates &&		insert_l(trans, &i[0])->b == insert_l(trans, &i[-1])->b;} static inline bool same_leaf_as_next(struct btree_trans *trans,				     struct btree_insert_entry *i){	return i + 1 < trans->updates + trans->nr_updates &&		insert_l(trans, &i[0])->b == insert_l(trans, &i[1])->b;} inline void bch2_btree_node_prep_for_write(struct btree_trans *trans,					   struct btree_path *path,					   struct btree *b){	struct bch_fs *c = trans->c; 	if (unlikely(btree_node_just_written(b)) &&	    bch2_btree_post_write_cleanup(c, b))		bch2_trans_node_reinit_iter(trans, b); 	/*	 * If the last bset has been written, or if it's gotten too big - start	 * a new bset to insert into:	 */	if (want_new_bset(c, b))		bch2_btree_init_next(trans, b);} static noinline int trans_lock_write_fail(struct btree_trans *trans, struct btree_insert_entry *i){	while (--i >= trans->updates) {		if (same_leaf_as_prev(trans, i))			continue; 		bch2_btree_node_unlock_write(trans, trans->paths + i->path, insert_l(trans, i)->b);	} 	trace_and_count(trans->c, trans_restart_would_deadlock_write, trans);	return btree_trans_restart(trans, BCH_ERR_transaction_restart_would_deadlock_write);} static inline int bch2_trans_lock_write(struct btree_trans *trans){	EBUG_ON(trans->write_locked); 	trans_for_each_update(trans, i) {		if (same_leaf_as_prev(trans, i))			continue; 		if (bch2_btree_node_lock_write(trans, trans->paths + i->path, &insert_l(trans, i)->b->c))			return trans_lock_write_fail(trans, i); 		if (!i->cached)			bch2_btree_node_prep_for_write(trans, trans->paths + i->path, insert_l(trans, i)->b);	} 	trans->write_locked = true;	return 0;} static inline void bch2_trans_unlock_write(struct btree_trans *trans){	if (likely(trans->write_locked)) {		trans_for_each_update(trans, i)			if (btree_node_locked_type(trans->paths + i->path, i->level) ==			    BTREE_NODE_WRITE_LOCKED)				bch2_btree_node_unlock_write_inlined(trans,						trans->paths + i->path, insert_l(trans, i)->b);		trans->write_locked = false;	}} /* Inserting into a given leaf node (last stage of insert): */ /* Handle overwrites and do insert, for non extents: */bool bch2_btree_bset_insert_key(struct btree_trans *trans,				struct btree_path *path,				struct btree *b,				struct btree_node_iter *node_iter,				struct bkey_i *insert){	struct bkey_packed *k;	unsigned clobber_u64s = 0, new_u64s = 0; 	EBUG_ON(btree_node_just_written(b));	EBUG_ON(bset_written(b, btree_bset_last(b)));	EBUG_ON(bkey_deleted(&insert->k) && bkey_val_u64s(&insert->k));	EBUG_ON(bpos_lt(insert->k.p, b->data->min_key));	EBUG_ON(bpos_gt(insert->k.p, b->data->max_key));	EBUG_ON(insert->k.u64s > bch2_btree_keys_u64s_remaining(b));	EBUG_ON(!b->c.level && !bpos_eq(insert->k.p, path->pos)); 	k = bch2_btree_node_iter_peek_all(node_iter, b);	if (k && bkey_cmp_left_packed(b, k, &insert->k.p))		k = NULL; 	/* @k is the key being overwritten/deleted, if any: */	EBUG_ON(k && bkey_deleted(k)); 	/* Deleting, but not found? nothing to do: */	if (bkey_deleted(&insert->k) && !k)		return false; 	if (bkey_deleted(&insert->k)) {		/* Deleting: */		btree_account_key_drop(b, k);		k->type = KEY_TYPE_deleted; 		if (k->needs_whiteout)			push_whiteout(b, insert->k.p);		k->needs_whiteout = false; 		if (k >= btree_bset_last(b)->start) {			clobber_u64s = k->u64s;			bch2_bset_delete(b, k, clobber_u64s);			goto fix_iter;		} else {			bch2_btree_path_fix_key_modified(trans, b, k);		} 		return true;	} 	if (k) {		/* Overwriting: */		btree_account_key_drop(b, k);		k->type = KEY_TYPE_deleted; 		insert->k.needs_whiteout = k->needs_whiteout;		k->needs_whiteout = false; 		if (k >= btree_bset_last(b)->start) {			clobber_u64s = k->u64s;			goto overwrite;		} else {			bch2_btree_path_fix_key_modified(trans, b, k);		}	} 	k = bch2_btree_node_iter_bset_pos(node_iter, b, bset_tree_last(b));overwrite:	bch2_bset_insert(b, k, insert, clobber_u64s);	new_u64s = k->u64s;fix_iter:	if (clobber_u64s != new_u64s)		bch2_btree_node_iter_fix(trans, path, b, node_iter, k,					 clobber_u64s, new_u64s);	return true;} static int __btree_node_flush(struct journal *j, struct journal_entry_pin *pin,			       unsigned i, u64 seq){	struct bch_fs *c = container_of(j, struct bch_fs, journal);	struct btree_write *w = container_of(pin, struct btree_write, journal);	struct btree *b = container_of(w, struct btree, writes[i]);	struct btree_trans *trans = bch2_trans_get(c);	unsigned long old, new;	unsigned idx = w - b->writes; 	btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_read); 	old = READ_ONCE(b->flags);	do {		new = old; 		if (!(old & (1 << BTREE_NODE_dirty)) ||		    !!(old & (1 << BTREE_NODE_write_idx)) != idx ||		    w->journal.seq != seq)			break; 		new &= ~BTREE_WRITE_TYPE_MASK;		new |= BTREE_WRITE_journal_reclaim;		new |= 1 << BTREE_NODE_need_write;	} while (!try_cmpxchg(&b->flags, &old, new)); 	btree_node_write_if_need(c, b, SIX_LOCK_read);	six_unlock_read(&b->c.lock); 	bch2_trans_put(trans);	return 0;} int bch2_btree_node_flush0(struct journal *j, struct journal_entry_pin *pin, u64 seq){	return __btree_node_flush(j, pin, 0, seq);} int bch2_btree_node_flush1(struct journal *j, struct journal_entry_pin *pin, u64 seq){	return __btree_node_flush(j, pin, 1, seq);} inline void bch2_btree_add_journal_pin(struct bch_fs *c,				       struct btree *b, u64 seq){	struct btree_write *w = btree_current_write(b); 	bch2_journal_pin_add(&c->journal, seq, &w->journal,			     btree_node_write_idx(b) == 0			     ? bch2_btree_node_flush0			     : bch2_btree_node_flush1);} /** * bch2_btree_insert_key_leaf() - insert a key one key into a leaf node * @trans:		btree transaction object * @path:		path pointing to @insert's pos * @insert:		key to insert * @journal_seq:	sequence number of journal reservation */inline void bch2_btree_insert_key_leaf(struct btree_trans *trans,				       struct btree_path *path,				       struct bkey_i *insert,				       u64 journal_seq){	struct bch_fs *c = trans->c;	struct btree *b = path_l(path)->b;	struct bset_tree *t = bset_tree_last(b);	struct bset *i = bset(b, t);	int old_u64s = bset_u64s(t);	int old_live_u64s = b->nr.live_u64s;	int live_u64s_added, u64s_added; 	if (unlikely(!bch2_btree_bset_insert_key(trans, path, b,					&path_l(path)->iter, insert)))		return; 	i->journal_seq = cpu_to_le64(max(journal_seq, le64_to_cpu(i->journal_seq))); 	bch2_btree_add_journal_pin(c, b, journal_seq); 	if (unlikely(!btree_node_dirty(b))) {		EBUG_ON(test_bit(BCH_FS_clean_shutdown, &c->flags));		set_btree_node_dirty_acct(c, b);	} 	live_u64s_added = (int) b->nr.live_u64s - old_live_u64s;	u64s_added = (int) bset_u64s(t) - old_u64s; 	if (b->sib_u64s[0] != U16_MAX && live_u64s_added < 0)		b->sib_u64s[0] = max(0, (int) b->sib_u64s[0] + live_u64s_added);	if (b->sib_u64s[1] != U16_MAX && live_u64s_added < 0)		b->sib_u64s[1] = max(0, (int) b->sib_u64s[1] + live_u64s_added); 	if (u64s_added > live_u64s_added &&	    bch2_maybe_compact_whiteouts(c, b))		bch2_trans_node_reinit_iter(trans, b);} /* Cached btree updates: */ /* Normal update interface: */ static inline void btree_insert_entry_checks(struct btree_trans *trans,					     struct btree_insert_entry *i){	struct btree_path *path = trans->paths + i->path; 	BUG_ON(!bpos_eq(i->k->k.p, path->pos));	BUG_ON(i->cached	!= path->cached);	BUG_ON(i->level		!= path->level);	BUG_ON(i->btree_id	!= path->btree_id);	EBUG_ON(!i->level &&		btree_type_has_snapshots(i->btree_id) &&		!(i->flags & BTREE_UPDATE_internal_snapshot_node) &&		test_bit(JOURNAL_replay_done, &trans->c->journal.flags) &&		i->k->k.p.snapshot &&		bch2_snapshot_is_internal_node(trans->c, i->k->k.p.snapshot) > 0);} static __always_inline int bch2_trans_journal_res_get(struct btree_trans *trans,						      unsigned flags){	return bch2_journal_res_get(&trans->c->journal, &trans->journal_res,				    trans->journal_u64s, flags);} #define JSET_ENTRY_LOG_U64s		4 static noinline void journal_transaction_name(struct btree_trans *trans){	struct bch_fs *c = trans->c;	struct journal *j = &c->journal;	struct jset_entry *entry =		bch2_journal_add_entry(j, &trans->journal_res,				       BCH_JSET_ENTRY_log, 0, 0,				       JSET_ENTRY_LOG_U64s);	struct jset_entry_log *l =		container_of(entry, struct jset_entry_log, entry); 	strncpy(l->d, trans->fn, JSET_ENTRY_LOG_U64s * sizeof(u64));} static inline int btree_key_can_insert(struct btree_trans *trans,				       struct btree *b, unsigned u64s){	if (!bch2_btree_node_insert_fits(b, u64s))		return -BCH_ERR_btree_insert_btree_node_full; 	return 0;} noinline static intbtree_key_can_insert_cached_slowpath(struct btree_trans *trans, unsigned flags,				     struct btree_path *path, unsigned new_u64s){	struct bkey_cached *ck = (void *) path->l[0].b;	struct bkey_i *new_k;	int ret; 	bch2_trans_unlock_write(trans);	bch2_trans_unlock(trans); 	new_k = kmalloc(new_u64s * sizeof(u64), GFP_KERNEL);	if (!new_k) {		bch_err(trans->c, "error allocating memory for key cache key, btree %s u64s %u",			bch2_btree_id_str(path->btree_id), new_u64s);		return -BCH_ERR_ENOMEM_btree_key_cache_insert;	} 	ret =   bch2_trans_relock(trans) ?:		bch2_trans_lock_write(trans);	if (unlikely(ret)) {		kfree(new_k);		return ret;	} 	memcpy(new_k, ck->k, ck->u64s * sizeof(u64)); 	trans_for_each_update(trans, i)		if (i->old_v == &ck->k->v)			i->old_v = &new_k->v; 	kfree(ck->k);	ck->u64s	= new_u64s;	ck->k		= new_k;	return 0;} static int btree_key_can_insert_cached(struct btree_trans *trans, unsigned flags,				       struct btree_path *path, unsigned u64s){	struct bch_fs *c = trans->c;	struct bkey_cached *ck = (void *) path->l[0].b;	unsigned new_u64s;	struct bkey_i *new_k;	unsigned watermark = flags & BCH_WATERMARK_MASK; 	EBUG_ON(path->level); 	if (watermark < BCH_WATERMARK_reclaim &&	    !test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&	    bch2_btree_key_cache_must_wait(c))		return -BCH_ERR_btree_insert_need_journal_reclaim; 	/*	 * bch2_varint_decode can read past the end of the buffer by at most 7	 * bytes (it won't be used):	 */	u64s += 1; 	if (u64s <= ck->u64s)		return 0; 	new_u64s	= roundup_pow_of_two(u64s);	new_k		= krealloc(ck->k, new_u64s * sizeof(u64), GFP_NOWAIT|__GFP_NOWARN);	if (unlikely(!new_k))		return btree_key_can_insert_cached_slowpath(trans, flags, path, new_u64s); 	trans_for_each_update(trans, i)		if (i->old_v == &ck->k->v)			i->old_v = &new_k->v; 	ck->u64s	= new_u64s;	ck->k		= new_k;	return 0;} /* Triggers: */ static int run_one_mem_trigger(struct btree_trans *trans,			       struct btree_insert_entry *i,			       unsigned flags){	verify_update_old_key(trans, i); 	if (unlikely(flags & BTREE_TRIGGER_norun))		return 0; 	struct bkey_s_c old = { &i->old_k, i->old_v };	struct bkey_i *new = i->k;	const struct bkey_ops *old_ops = bch2_bkey_type_ops(old.k->type);	const struct bkey_ops *new_ops = bch2_bkey_type_ops(i->k->k.type); 	if (old_ops->trigger == new_ops->trigger)		return bch2_key_trigger(trans, i->btree_id, i->level,				old, bkey_i_to_s(new),				BTREE_TRIGGER_insert|BTREE_TRIGGER_overwrite|flags);	else		return bch2_key_trigger_new(trans, i->btree_id, i->level,				bkey_i_to_s(new), flags) ?:		       bch2_key_trigger_old(trans, i->btree_id, i->level,				old, flags);} static int run_one_trans_trigger(struct btree_trans *trans, struct btree_insert_entry *i,				 bool overwrite){	verify_update_old_key(trans, i); 	if ((i->flags & BTREE_TRIGGER_norun) ||	    !btree_node_type_has_trans_triggers(i->bkey_type))		return 0; 	/*	 * Transactional triggers create new btree_insert_entries, so we can't	 * pass them a pointer to a btree_insert_entry, that memory is going to	 * move:	 */	struct bkey old_k = i->old_k;	struct bkey_s_c old = { &old_k, i->old_v };	const struct bkey_ops *old_ops = bch2_bkey_type_ops(old.k->type);	const struct bkey_ops *new_ops = bch2_bkey_type_ops(i->k->k.type);	unsigned flags = i->flags|BTREE_TRIGGER_transactional;

Lines 1–500 of 1157.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t