brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 5df8de0 Raw
617 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "bcachefs.h"3#include "disk_groups.h"4#include "sb-members.h"5#include "super-io.h"6 7#include <linux/sort.h>8 9static int group_cmp(const void *_l, const void *_r)10{11	const struct bch_disk_group *l = _l;12	const struct bch_disk_group *r = _r;13 14	return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -15		(BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:16		((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -17		 (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:18		strncmp(l->label, r->label, sizeof(l->label));19}20 21static int bch2_sb_disk_groups_validate(struct bch_sb *sb, struct bch_sb_field *f,22				enum bch_validate_flags flags, struct printbuf *err)23{24	struct bch_sb_field_disk_groups *groups =25		field_to_type(f, disk_groups);26	struct bch_disk_group *g, *sorted = NULL;27	unsigned nr_groups = disk_groups_nr(groups);28	unsigned i, len;29	int ret = 0;30 31	for (i = 0; i < sb->nr_devices; i++) {32		struct bch_member m = bch2_sb_member_get(sb, i);33		unsigned group_id;34 35		if (!BCH_MEMBER_GROUP(&m))36			continue;37 38		group_id = BCH_MEMBER_GROUP(&m) - 1;39 40		if (group_id >= nr_groups) {41			prt_printf(err, "disk %u has invalid label %u (have %u)",42				   i, group_id, nr_groups);43			return -BCH_ERR_invalid_sb_disk_groups;44		}45 46		if (BCH_GROUP_DELETED(&groups->entries[group_id])) {47			prt_printf(err, "disk %u has deleted label %u", i, group_id);48			return -BCH_ERR_invalid_sb_disk_groups;49		}50	}51 52	if (!nr_groups)53		return 0;54 55	for (i = 0; i < nr_groups; i++) {56		g = groups->entries + i;57 58		if (BCH_GROUP_DELETED(g))59			continue;60 61		len = strnlen(g->label, sizeof(g->label));62		if (!len) {63			prt_printf(err, "label %u empty", i);64			return -BCH_ERR_invalid_sb_disk_groups;65		}66	}67 68	sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);69	if (!sorted)70		return -BCH_ERR_ENOMEM_disk_groups_validate;71 72	memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));73	sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);74 75	for (g = sorted; g + 1 < sorted + nr_groups; g++)76		if (!BCH_GROUP_DELETED(g) &&77		    !group_cmp(&g[0], &g[1])) {78			prt_printf(err, "duplicate label %llu.%.*s",79			       BCH_GROUP_PARENT(g),80			       (int) sizeof(g->label), g->label);81			ret = -BCH_ERR_invalid_sb_disk_groups;82			goto err;83		}84err:85	kfree(sorted);86	return ret;87}88 89void bch2_disk_groups_to_text(struct printbuf *out, struct bch_fs *c)90{91	out->atomic++;92	rcu_read_lock();93 94	struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);95	if (!g)96		goto out;97 98	for (unsigned i = 0; i < g->nr; i++) {99		if (i)100			prt_printf(out, " ");101 102		if (g->entries[i].deleted) {103			prt_printf(out, "[deleted]");104			continue;105		}106 107		prt_printf(out, "[parent %d devs", g->entries[i].parent);108		for_each_member_device_rcu(c, ca, &g->entries[i].devs)109			prt_printf(out, " %s", ca->name);110		prt_printf(out, "]");111	}112 113out:114	rcu_read_unlock();115	out->atomic--;116}117 118static void bch2_sb_disk_groups_to_text(struct printbuf *out,119					struct bch_sb *sb,120					struct bch_sb_field *f)121{122	struct bch_sb_field_disk_groups *groups =123		field_to_type(f, disk_groups);124	struct bch_disk_group *g;125	unsigned nr_groups = disk_groups_nr(groups);126 127	for (g = groups->entries;128	     g < groups->entries + nr_groups;129	     g++) {130		if (g != groups->entries)131			prt_printf(out, " ");132 133		if (BCH_GROUP_DELETED(g))134			prt_printf(out, "[deleted]");135		else136			prt_printf(out, "[parent %llu name %s]",137			       BCH_GROUP_PARENT(g), g->label);138	}139}140 141const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {142	.validate	= bch2_sb_disk_groups_validate,143	.to_text	= bch2_sb_disk_groups_to_text144};145 146int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)147{148	struct bch_sb_field_disk_groups *groups;149	struct bch_disk_groups_cpu *cpu_g, *old_g;150	unsigned i, g, nr_groups;151 152	lockdep_assert_held(&c->sb_lock);153 154	groups		= bch2_sb_field_get(c->disk_sb.sb, disk_groups);155	nr_groups	= disk_groups_nr(groups);156 157	if (!groups)158		return 0;159 160	cpu_g = kzalloc(struct_size(cpu_g, entries, nr_groups), GFP_KERNEL);161	if (!cpu_g)162		return -BCH_ERR_ENOMEM_disk_groups_to_cpu;163 164	cpu_g->nr = nr_groups;165 166	for (i = 0; i < nr_groups; i++) {167		struct bch_disk_group *src	= &groups->entries[i];168		struct bch_disk_group_cpu *dst	= &cpu_g->entries[i];169 170		dst->deleted	= BCH_GROUP_DELETED(src);171		dst->parent	= BCH_GROUP_PARENT(src);172		memcpy(dst->label, src->label, sizeof(dst->label));173	}174 175	for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {176		struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, i);177		struct bch_disk_group_cpu *dst;178 179		if (!bch2_member_alive(&m))180			continue;181 182		g = BCH_MEMBER_GROUP(&m);183		while (g) {184			dst = &cpu_g->entries[g - 1];185			__set_bit(i, dst->devs.d);186			g = dst->parent;187		}188	}189 190	old_g = rcu_dereference_protected(c->disk_groups,191				lockdep_is_held(&c->sb_lock));192	rcu_assign_pointer(c->disk_groups, cpu_g);193	if (old_g)194		kfree_rcu(old_g, rcu);195 196	return 0;197}198 199const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)200{201	struct target t = target_decode(target);202	struct bch_devs_mask *devs;203 204	rcu_read_lock();205 206	switch (t.type) {207	case TARGET_NULL:208		devs = NULL;209		break;210	case TARGET_DEV: {211		struct bch_dev *ca = t.dev < c->sb.nr_devices212			? rcu_dereference(c->devs[t.dev])213			: NULL;214		devs = ca ? &ca->self : NULL;215		break;216	}217	case TARGET_GROUP: {218		struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);219 220		devs = g && t.group < g->nr && !g->entries[t.group].deleted221			? &g->entries[t.group].devs222			: NULL;223		break;224	}225	default:226		BUG();227	}228 229	rcu_read_unlock();230 231	return devs;232}233 234bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)235{236	struct target t = target_decode(target);237 238	switch (t.type) {239	case TARGET_NULL:240		return false;241	case TARGET_DEV:242		return dev == t.dev;243	case TARGET_GROUP: {244		struct bch_disk_groups_cpu *g;245		const struct bch_devs_mask *m;246		bool ret;247 248		rcu_read_lock();249		g = rcu_dereference(c->disk_groups);250		m = g && t.group < g->nr && !g->entries[t.group].deleted251			? &g->entries[t.group].devs252			: NULL;253 254		ret = m ? test_bit(dev, m->d) : false;255		rcu_read_unlock();256 257		return ret;258	}259	default:260		BUG();261	}262}263 264static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,265				  unsigned parent,266				  const char *name, unsigned namelen)267{268	unsigned i, nr_groups = disk_groups_nr(groups);269 270	if (!namelen || namelen > BCH_SB_LABEL_SIZE)271		return -EINVAL;272 273	for (i = 0; i < nr_groups; i++) {274		struct bch_disk_group *g = groups->entries + i;275 276		if (BCH_GROUP_DELETED(g))277			continue;278 279		if (!BCH_GROUP_DELETED(g) &&280		    BCH_GROUP_PARENT(g) == parent &&281		    strnlen(g->label, sizeof(g->label)) == namelen &&282		    !memcmp(name, g->label, namelen))283			return i;284	}285 286	return -1;287}288 289static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,290				 const char *name, unsigned namelen)291{292	struct bch_sb_field_disk_groups *groups =293		bch2_sb_field_get(sb->sb, disk_groups);294	unsigned i, nr_groups = disk_groups_nr(groups);295	struct bch_disk_group *g;296 297	if (!namelen || namelen > BCH_SB_LABEL_SIZE)298		return -EINVAL;299 300	for (i = 0;301	     i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);302	     i++)303		;304 305	if (i == nr_groups) {306		unsigned u64s =307			(sizeof(struct bch_sb_field_disk_groups) +308			 sizeof(struct bch_disk_group) * (nr_groups + 1)) /309			sizeof(u64);310 311		groups = bch2_sb_field_resize(sb, disk_groups, u64s);312		if (!groups)313			return -BCH_ERR_ENOSPC_disk_label_add;314 315		nr_groups = disk_groups_nr(groups);316	}317 318	BUG_ON(i >= nr_groups);319 320	g = &groups->entries[i];321 322	memcpy(g->label, name, namelen);323	if (namelen < sizeof(g->label))324		g->label[namelen] = '\0';325	SET_BCH_GROUP_DELETED(g, 0);326	SET_BCH_GROUP_PARENT(g, parent);327	SET_BCH_GROUP_DATA_ALLOWED(g, ~0);328 329	return i;330}331 332int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)333{334	struct bch_sb_field_disk_groups *groups =335		bch2_sb_field_get(sb->sb, disk_groups);336	int v = -1;337 338	do {339		const char *next = strchrnul(name, '.');340		unsigned len = next - name;341 342		if (*next == '.')343			next++;344 345		v = __bch2_disk_group_find(groups, v + 1, name, len);346		name = next;347	} while (*name && v >= 0);348 349	return v;350}351 352int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)353{354	struct bch_sb_field_disk_groups *groups;355	unsigned parent = 0;356	int v = -1;357 358	do {359		const char *next = strchrnul(name, '.');360		unsigned len = next - name;361 362		if (*next == '.')363			next++;364 365		groups = bch2_sb_field_get(sb->sb, disk_groups);366 367		v = __bch2_disk_group_find(groups, parent, name, len);368		if (v < 0)369			v = __bch2_disk_group_add(sb, parent, name, len);370		if (v < 0)371			return v;372 373		parent = v + 1;374		name = next;375	} while (*name && v >= 0);376 377	return v;378}379 380void bch2_disk_path_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)381{382	struct bch_disk_groups_cpu *groups;383	struct bch_disk_group_cpu *g;384	unsigned nr = 0;385	u16 path[32];386 387	out->atomic++;388	rcu_read_lock();389	groups = rcu_dereference(c->disk_groups);390	if (!groups)391		goto invalid;392 393	while (1) {394		if (nr == ARRAY_SIZE(path))395			goto invalid;396 397		if (v >= groups->nr)398			goto invalid;399 400		g = groups->entries + v;401 402		if (g->deleted)403			goto invalid;404 405		path[nr++] = v;406 407		if (!g->parent)408			break;409 410		v = g->parent - 1;411	}412 413	while (nr) {414		v = path[--nr];415		g = groups->entries + v;416 417		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);418		if (nr)419			prt_printf(out, ".");420	}421out:422	rcu_read_unlock();423	out->atomic--;424	return;425invalid:426	prt_printf(out, "invalid label %u", v);427	goto out;428}429 430void bch2_disk_path_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)431{432	struct bch_sb_field_disk_groups *groups =433		bch2_sb_field_get(sb, disk_groups);434	struct bch_disk_group *g;435	unsigned nr = 0;436	u16 path[32];437 438	while (1) {439		if (nr == ARRAY_SIZE(path))440			goto inval;441 442		if (v >= disk_groups_nr(groups))443			goto inval;444 445		g = groups->entries + v;446 447		if (BCH_GROUP_DELETED(g))448			goto inval;449 450		path[nr++] = v;451 452		if (!BCH_GROUP_PARENT(g))453			break;454 455		v = BCH_GROUP_PARENT(g) - 1;456	}457 458	while (nr) {459		v = path[--nr];460		g = groups->entries + v;461 462		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);463		if (nr)464			prt_printf(out, ".");465	}466	return;467inval:468	prt_printf(out, "invalid label %u", v);469}470 471int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)472{473	struct bch_member *mi;474	int ret, v = -1;475 476	if (!strlen(name) || !strcmp(name, "none"))477		return 0;478 479	v = bch2_disk_path_find_or_create(&c->disk_sb, name);480	if (v < 0)481		return v;482 483	ret = bch2_sb_disk_groups_to_cpu(c);484	if (ret)485		return ret;486 487	mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);488	SET_BCH_MEMBER_GROUP(mi, v + 1);489	return 0;490}491 492int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)493{494	int ret;495 496	mutex_lock(&c->sb_lock);497	ret = __bch2_dev_group_set(c, ca, name) ?:498		bch2_write_super(c);499	mutex_unlock(&c->sb_lock);500 501	return ret;502}503 504int bch2_opt_target_parse(struct bch_fs *c, const char *val, u64 *res,505			  struct printbuf *err)506{507	struct bch_dev *ca;508	int g;509 510	if (!val)511		return -EINVAL;512 513	if (!c)514		return -BCH_ERR_option_needs_open_fs;515 516	if (!strlen(val) || !strcmp(val, "none")) {517		*res = 0;518		return 0;519	}520 521	/* Is it a device? */522	ca = bch2_dev_lookup(c, val);523	if (!IS_ERR(ca)) {524		*res = dev_to_target(ca->dev_idx);525		bch2_dev_put(ca);526		return 0;527	}528 529	mutex_lock(&c->sb_lock);530	g = bch2_disk_path_find(&c->disk_sb, val);531	mutex_unlock(&c->sb_lock);532 533	if (g >= 0) {534		*res = group_to_target(g);535		return 0;536	}537 538	return -EINVAL;539}540 541void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)542{543	struct target t = target_decode(v);544 545	switch (t.type) {546	case TARGET_NULL:547		prt_printf(out, "none");548		break;549	case TARGET_DEV: {550		struct bch_dev *ca;551 552		out->atomic++;553		rcu_read_lock();554		ca = t.dev < c->sb.nr_devices555			? rcu_dereference(c->devs[t.dev])556			: NULL;557 558		if (ca && percpu_ref_tryget(&ca->io_ref)) {559			prt_printf(out, "/dev/%s", ca->name);560			percpu_ref_put(&ca->io_ref);561		} else if (ca) {562			prt_printf(out, "offline device %u", t.dev);563		} else {564			prt_printf(out, "invalid device %u", t.dev);565		}566 567		rcu_read_unlock();568		out->atomic--;569		break;570	}571	case TARGET_GROUP:572		bch2_disk_path_to_text(out, c, t.group);573		break;574	default:575		BUG();576	}577}578 579static void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)580{581	struct target t = target_decode(v);582 583	switch (t.type) {584	case TARGET_NULL:585		prt_printf(out, "none");586		break;587	case TARGET_DEV: {588		struct bch_member m = bch2_sb_member_get(sb, t.dev);589 590		if (bch2_member_exists(sb, t.dev)) {591			prt_printf(out, "Device ");592			pr_uuid(out, m.uuid.b);593			prt_printf(out, " (%u)", t.dev);594		} else {595			prt_printf(out, "Bad device %u", t.dev);596		}597		break;598	}599	case TARGET_GROUP:600		bch2_disk_path_to_text_sb(out, sb, t.group);601		break;602	default:603		BUG();604	}605}606 607void bch2_opt_target_to_text(struct printbuf *out,608			     struct bch_fs *c,609			     struct bch_sb *sb,610			     u64 v)611{612	if (c)613		bch2_target_to_text(out, c, v);614	else615		bch2_target_to_text_sb(out, sb, v);616}617