2553 lines · 64.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include "bcachefs.h"#include "alloc_background.h"#include "alloc_foreground.h"#include "backpointers.h"#include "bkey_buf.h"#include "btree_cache.h"#include "btree_io.h"#include "btree_key_cache.h"#include "btree_update.h"#include "btree_update_interior.h"#include "btree_gc.h"#include "btree_write_buffer.h"#include "buckets.h"#include "buckets_waiting_for_journal.h"#include "clock.h"#include "debug.h"#include "disk_accounting.h"#include "ec.h"#include "error.h"#include "lru.h"#include "recovery.h"#include "trace.h"#include "varint.h" #include <linux/kthread.h>#include <linux/math64.h>#include <linux/random.h>#include <linux/rculist.h>#include <linux/rcupdate.h>#include <linux/sched/task.h>#include <linux/sort.h>#include <linux/jiffies.h> static void bch2_discard_one_bucket_fast(struct bch_dev *, u64); /* Persistent alloc info: */ static const unsigned BCH_ALLOC_V1_FIELD_BYTES[] = {#define x(name, bits) [BCH_ALLOC_FIELD_V1_##name] = bits / 8,	BCH_ALLOC_FIELDS_V1()#undef x}; struct bkey_alloc_unpacked {	u64		journal_seq;	u8		gen;	u8		oldest_gen;	u8		data_type;	bool		need_discard:1;	bool		need_inc_gen:1;#define x(_name, _bits)	u##_bits _name;	BCH_ALLOC_FIELDS_V2()#undef  x}; static inline u64 alloc_field_v1_get(const struct bch_alloc *a,				     const void **p, unsigned field){	unsigned bytes = BCH_ALLOC_V1_FIELD_BYTES[field];	u64 v; 	if (!(a->fields & (1 << field)))		return 0; 	switch (bytes) {	case 1:		v = *((const u8 *) *p);		break;	case 2:		v = le16_to_cpup(*p);		break;	case 4:		v = le32_to_cpup(*p);		break;	case 8:		v = le64_to_cpup(*p);		break;	default:		BUG();	} 	*p += bytes;	return v;} static void bch2_alloc_unpack_v1(struct bkey_alloc_unpacked *out,				 struct bkey_s_c k){	const struct bch_alloc *in = bkey_s_c_to_alloc(k).v;	const void *d = in->data;	unsigned idx = 0; 	out->gen = in->gen; #define x(_name, _bits) out->_name = alloc_field_v1_get(in, &d, idx++);	BCH_ALLOC_FIELDS_V1()#undef  x} static int bch2_alloc_unpack_v2(struct bkey_alloc_unpacked *out,				struct bkey_s_c k){	struct bkey_s_c_alloc_v2 a = bkey_s_c_to_alloc_v2(k);	const u8 *in = a.v->data;	const u8 *end = bkey_val_end(a);	unsigned fieldnr = 0;	int ret;	u64 v; 	out->gen	= a.v->gen;	out->oldest_gen	= a.v->oldest_gen;	out->data_type	= a.v->data_type; #define x(_name, _bits)							\	if (fieldnr < a.v->nr_fields) {					\		ret = bch2_varint_decode_fast(in, end, &v);		\		if (ret < 0)						\			return ret;					\		in += ret;						\	} else {							\		v = 0;							\	}								\	out->_name = v;							\	if (v != out->_name)						\		return -1;						\	fieldnr++; 	BCH_ALLOC_FIELDS_V2()#undef  x	return 0;} static int bch2_alloc_unpack_v3(struct bkey_alloc_unpacked *out,				struct bkey_s_c k){	struct bkey_s_c_alloc_v3 a = bkey_s_c_to_alloc_v3(k);	const u8 *in = a.v->data;	const u8 *end = bkey_val_end(a);	unsigned fieldnr = 0;	int ret;	u64 v; 	out->gen	= a.v->gen;	out->oldest_gen	= a.v->oldest_gen;	out->data_type	= a.v->data_type;	out->need_discard = BCH_ALLOC_V3_NEED_DISCARD(a.v);	out->need_inc_gen = BCH_ALLOC_V3_NEED_INC_GEN(a.v);	out->journal_seq = le64_to_cpu(a.v->journal_seq); #define x(_name, _bits)							\	if (fieldnr < a.v->nr_fields) {					\		ret = bch2_varint_decode_fast(in, end, &v);		\		if (ret < 0)						\			return ret;					\		in += ret;						\	} else {							\		v = 0;							\	}								\	out->_name = v;							\	if (v != out->_name)						\		return -1;						\	fieldnr++; 	BCH_ALLOC_FIELDS_V2()#undef  x	return 0;} static struct bkey_alloc_unpacked bch2_alloc_unpack(struct bkey_s_c k){	struct bkey_alloc_unpacked ret = { .gen	= 0 }; 	switch (k.k->type) {	case KEY_TYPE_alloc:		bch2_alloc_unpack_v1(&ret, k);		break;	case KEY_TYPE_alloc_v2:		bch2_alloc_unpack_v2(&ret, k);		break;	case KEY_TYPE_alloc_v3:		bch2_alloc_unpack_v3(&ret, k);		break;	} 	return ret;} static unsigned bch_alloc_v1_val_u64s(const struct bch_alloc *a){	unsigned i, bytes = offsetof(struct bch_alloc, data); 	for (i = 0; i < ARRAY_SIZE(BCH_ALLOC_V1_FIELD_BYTES); i++)		if (a->fields & (1 << i))			bytes += BCH_ALLOC_V1_FIELD_BYTES[i]; 	return DIV_ROUND_UP(bytes, sizeof(u64));} int bch2_alloc_v1_validate(struct bch_fs *c, struct bkey_s_c k,			   enum bch_validate_flags flags){	struct bkey_s_c_alloc a = bkey_s_c_to_alloc(k);	int ret = 0; 	/* allow for unknown fields */	bkey_fsck_err_on(bkey_val_u64s(a.k) < bch_alloc_v1_val_u64s(a.v),			 c, alloc_v1_val_size_bad,			 "incorrect value size (%zu < %u)",			 bkey_val_u64s(a.k), bch_alloc_v1_val_u64s(a.v));fsck_err:	return ret;} int bch2_alloc_v2_validate(struct bch_fs *c, struct bkey_s_c k,			   enum bch_validate_flags flags){	struct bkey_alloc_unpacked u;	int ret = 0; 	bkey_fsck_err_on(bch2_alloc_unpack_v2(&u, k),			 c, alloc_v2_unpack_error,			 "unpack error");fsck_err:	return ret;} int bch2_alloc_v3_validate(struct bch_fs *c, struct bkey_s_c k,			   enum bch_validate_flags flags){	struct bkey_alloc_unpacked u;	int ret = 0; 	bkey_fsck_err_on(bch2_alloc_unpack_v3(&u, k),			 c, alloc_v2_unpack_error,			 "unpack error");fsck_err:	return ret;} int bch2_alloc_v4_validate(struct bch_fs *c, struct bkey_s_c k,			   enum bch_validate_flags flags){	struct bch_alloc_v4 a;	int ret = 0; 	bkey_val_copy(&a, bkey_s_c_to_alloc_v4(k)); 	bkey_fsck_err_on(alloc_v4_u64s_noerror(&a) > bkey_val_u64s(k.k),			 c, alloc_v4_val_size_bad,			 "bad val size (%u > %zu)",			 alloc_v4_u64s_noerror(&a), bkey_val_u64s(k.k)); 	bkey_fsck_err_on(!BCH_ALLOC_V4_BACKPOINTERS_START(&a) &&			 BCH_ALLOC_V4_NR_BACKPOINTERS(&a),			 c, alloc_v4_backpointers_start_bad,			 "invalid backpointers_start"); 	bkey_fsck_err_on(alloc_data_type(a, a.data_type) != a.data_type,			 c, alloc_key_data_type_bad,			 "invalid data type (got %u should be %u)",			 a.data_type, alloc_data_type(a, a.data_type)); 	for (unsigned i = 0; i < 2; i++)		bkey_fsck_err_on(a.io_time[i] > LRU_TIME_MAX,				 c, alloc_key_io_time_bad,				 "invalid io_time[%s]: %llu, max %llu",				 i == READ ? "read" : "write",				 a.io_time[i], LRU_TIME_MAX); 	unsigned stripe_sectors = BCH_ALLOC_V4_BACKPOINTERS_START(&a) * sizeof(u64) >		offsetof(struct bch_alloc_v4, stripe_sectors)		? a.stripe_sectors		: 0; 	switch (a.data_type) {	case BCH_DATA_free:	case BCH_DATA_need_gc_gens:	case BCH_DATA_need_discard:		bkey_fsck_err_on(stripe_sectors ||				 a.dirty_sectors ||				 a.cached_sectors ||				 a.stripe,				 c, alloc_key_empty_but_have_data,				 "empty data type free but have data %u.%u.%u %u",				 stripe_sectors,				 a.dirty_sectors,				 a.cached_sectors,				 a.stripe);		break;	case BCH_DATA_sb:	case BCH_DATA_journal:	case BCH_DATA_btree:	case BCH_DATA_user:	case BCH_DATA_parity:		bkey_fsck_err_on(!a.dirty_sectors &&				 !stripe_sectors,				 c, alloc_key_dirty_sectors_0,				 "data_type %s but dirty_sectors==0",				 bch2_data_type_str(a.data_type));		break;	case BCH_DATA_cached:		bkey_fsck_err_on(!a.cached_sectors ||				 a.dirty_sectors ||				 stripe_sectors ||				 a.stripe,				 c, alloc_key_cached_inconsistency,				 "data type inconsistency"); 		bkey_fsck_err_on(!a.io_time[READ] &&				 c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs,				 c, alloc_key_cached_but_read_time_zero,				 "cached bucket with read_time == 0");		break;	case BCH_DATA_stripe:		break;	}fsck_err:	return ret;} void bch2_alloc_v4_swab(struct bkey_s k){	struct bch_alloc_v4 *a = bkey_s_to_alloc_v4(k).v;	struct bch_backpointer *bp, *bps; 	a->journal_seq		= swab64(a->journal_seq);	a->flags		= swab32(a->flags);	a->dirty_sectors	= swab32(a->dirty_sectors);	a->cached_sectors	= swab32(a->cached_sectors);	a->io_time[0]		= swab64(a->io_time[0]);	a->io_time[1]		= swab64(a->io_time[1]);	a->stripe		= swab32(a->stripe);	a->nr_external_backpointers = swab32(a->nr_external_backpointers);	a->stripe_sectors	= swab32(a->stripe_sectors); 	bps = alloc_v4_backpointers(a);	for (bp = bps; bp < bps + BCH_ALLOC_V4_NR_BACKPOINTERS(a); bp++) {		bp->bucket_offset	= swab40(bp->bucket_offset);		bp->bucket_len		= swab32(bp->bucket_len);		bch2_bpos_swab(&bp->pos);	}} void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c k){	struct bch_alloc_v4 _a;	const struct bch_alloc_v4 *a = bch2_alloc_to_v4(k, &_a);	struct bch_dev *ca = c ? bch2_dev_bucket_tryget_noerror(c, k.k->p) : NULL; 	prt_newline(out);	printbuf_indent_add(out, 2); 	prt_printf(out, "gen %u oldest_gen %u data_type ", a->gen, a->oldest_gen);	bch2_prt_data_type(out, a->data_type);	prt_newline(out);	prt_printf(out, "journal_seq       %llu\n",	a->journal_seq);	prt_printf(out, "need_discard      %llu\n",	BCH_ALLOC_V4_NEED_DISCARD(a));	prt_printf(out, "need_inc_gen      %llu\n",	BCH_ALLOC_V4_NEED_INC_GEN(a));	prt_printf(out, "dirty_sectors     %u\n",	a->dirty_sectors);	prt_printf(out, "stripe_sectors    %u\n",	a->stripe_sectors);	prt_printf(out, "cached_sectors    %u\n",	a->cached_sectors);	prt_printf(out, "stripe            %u\n",	a->stripe);	prt_printf(out, "stripe_redundancy %u\n",	a->stripe_redundancy);	prt_printf(out, "io_time[READ]     %llu\n",	a->io_time[READ]);	prt_printf(out, "io_time[WRITE]    %llu\n",	a->io_time[WRITE]); 	if (ca)		prt_printf(out, "fragmentation     %llu\n",	alloc_lru_idx_fragmentation(*a, ca));	prt_printf(out, "bp_start          %llu\n", BCH_ALLOC_V4_BACKPOINTERS_START(a));	printbuf_indent_sub(out, 2); 	bch2_dev_put(ca);} void __bch2_alloc_to_v4(struct bkey_s_c k, struct bch_alloc_v4 *out){	if (k.k->type == KEY_TYPE_alloc_v4) {		void *src, *dst; 		*out = *bkey_s_c_to_alloc_v4(k).v; 		src = alloc_v4_backpointers(out);		SET_BCH_ALLOC_V4_BACKPOINTERS_START(out, BCH_ALLOC_V4_U64s);		dst = alloc_v4_backpointers(out); 		if (src < dst)			memset(src, 0, dst - src); 		SET_BCH_ALLOC_V4_NR_BACKPOINTERS(out, 0);	} else {		struct bkey_alloc_unpacked u = bch2_alloc_unpack(k); 		*out = (struct bch_alloc_v4) {			.journal_seq		= u.journal_seq,			.flags			= u.need_discard,			.gen			= u.gen,			.oldest_gen		= u.oldest_gen,			.data_type		= u.data_type,			.stripe_redundancy	= u.stripe_redundancy,			.dirty_sectors		= u.dirty_sectors,			.cached_sectors		= u.cached_sectors,			.io_time[READ]		= u.read_time,			.io_time[WRITE]		= u.write_time,			.stripe			= u.stripe,		}; 		SET_BCH_ALLOC_V4_BACKPOINTERS_START(out, BCH_ALLOC_V4_U64s);	}} static noinline struct bkey_i_alloc_v4 *__bch2_alloc_to_v4_mut(struct btree_trans *trans, struct bkey_s_c k){	struct bkey_i_alloc_v4 *ret; 	ret = bch2_trans_kmalloc(trans, max(bkey_bytes(k.k), sizeof(struct bkey_i_alloc_v4)));	if (IS_ERR(ret))		return ret; 	if (k.k->type == KEY_TYPE_alloc_v4) {		void *src, *dst; 		bkey_reassemble(&ret->k_i, k); 		src = alloc_v4_backpointers(&ret->v);		SET_BCH_ALLOC_V4_BACKPOINTERS_START(&ret->v, BCH_ALLOC_V4_U64s);		dst = alloc_v4_backpointers(&ret->v); 		if (src < dst)			memset(src, 0, dst - src); 		SET_BCH_ALLOC_V4_NR_BACKPOINTERS(&ret->v, 0);		set_alloc_v4_u64s(ret);	} else {		bkey_alloc_v4_init(&ret->k_i);		ret->k.p = k.k->p;		bch2_alloc_to_v4(k, &ret->v);	}	return ret;} static inline struct bkey_i_alloc_v4 *bch2_alloc_to_v4_mut_inlined(struct btree_trans *trans, struct bkey_s_c k){	struct bkey_s_c_alloc_v4 a; 	if (likely(k.k->type == KEY_TYPE_alloc_v4) &&	    ((a = bkey_s_c_to_alloc_v4(k), true) &&	     BCH_ALLOC_V4_NR_BACKPOINTERS(a.v) == 0))		return bch2_bkey_make_mut_noupdate_typed(trans, k, alloc_v4); 	return __bch2_alloc_to_v4_mut(trans, k);} struct bkey_i_alloc_v4 *bch2_alloc_to_v4_mut(struct btree_trans *trans, struct bkey_s_c k){	return bch2_alloc_to_v4_mut_inlined(trans, k);} struct bkey_i_alloc_v4 *bch2_trans_start_alloc_update_noupdate(struct btree_trans *trans, struct btree_iter *iter,				       struct bpos pos){	struct bkey_s_c k = bch2_bkey_get_iter(trans, iter, BTREE_ID_alloc, pos,					       BTREE_ITER_with_updates|					       BTREE_ITER_cached|					       BTREE_ITER_intent);	int ret = bkey_err(k);	if (unlikely(ret))		return ERR_PTR(ret); 	struct bkey_i_alloc_v4 *a = bch2_alloc_to_v4_mut_inlined(trans, k);	ret = PTR_ERR_OR_ZERO(a);	if (unlikely(ret))		goto err;	return a;err:	bch2_trans_iter_exit(trans, iter);	return ERR_PTR(ret);} __flattenstruct bkey_i_alloc_v4 *bch2_trans_start_alloc_update(struct btree_trans *trans, struct bpos pos,						      enum btree_iter_update_trigger_flags flags){	struct btree_iter iter;	struct bkey_i_alloc_v4 *a = bch2_trans_start_alloc_update_noupdate(trans, &iter, pos);	int ret = PTR_ERR_OR_ZERO(a);	if (ret)		return ERR_PTR(ret); 	ret = bch2_trans_update(trans, &iter, &a->k_i, flags);	bch2_trans_iter_exit(trans, &iter);	return unlikely(ret) ? ERR_PTR(ret) : a;} static struct bpos alloc_gens_pos(struct bpos pos, unsigned *offset){	*offset = pos.offset & KEY_TYPE_BUCKET_GENS_MASK; 

Lines 1–500 of 2553.