616 lines · 12.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include "bcachefs.h"#include "disk_groups.h"#include "sb-members.h"#include "super-io.h" #include <linux/sort.h> static int group_cmp(const void *_l, const void *_r){	const struct bch_disk_group *l = _l;	const struct bch_disk_group *r = _r; 	return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -		(BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:		((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -		 (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:		strncmp(l->label, r->label, sizeof(l->label));} static int bch2_sb_disk_groups_validate(struct bch_sb *sb, struct bch_sb_field *f,				enum bch_validate_flags flags, struct printbuf *err){	struct bch_sb_field_disk_groups *groups =		field_to_type(f, disk_groups);	struct bch_disk_group *g, *sorted = NULL;	unsigned nr_groups = disk_groups_nr(groups);	unsigned i, len;	int ret = 0; 	for (i = 0; i < sb->nr_devices; i++) {		struct bch_member m = bch2_sb_member_get(sb, i);		unsigned group_id; 		if (!BCH_MEMBER_GROUP(&m))			continue; 		group_id = BCH_MEMBER_GROUP(&m) - 1; 		if (group_id >= nr_groups) {			prt_printf(err, "disk %u has invalid label %u (have %u)",				   i, group_id, nr_groups);			return -BCH_ERR_invalid_sb_disk_groups;		} 		if (BCH_GROUP_DELETED(&groups->entries[group_id])) {			prt_printf(err, "disk %u has deleted label %u", i, group_id);			return -BCH_ERR_invalid_sb_disk_groups;		}	} 	if (!nr_groups)		return 0; 	for (i = 0; i < nr_groups; i++) {		g = groups->entries + i; 		if (BCH_GROUP_DELETED(g))			continue; 		len = strnlen(g->label, sizeof(g->label));		if (!len) {			prt_printf(err, "label %u empty", i);			return -BCH_ERR_invalid_sb_disk_groups;		}	} 	sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);	if (!sorted)		return -BCH_ERR_ENOMEM_disk_groups_validate; 	memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));	sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL); 	for (g = sorted; g + 1 < sorted + nr_groups; g++)		if (!BCH_GROUP_DELETED(g) &&		    !group_cmp(&g[0], &g[1])) {			prt_printf(err, "duplicate label %llu.%.*s",			       BCH_GROUP_PARENT(g),			       (int) sizeof(g->label), g->label);			ret = -BCH_ERR_invalid_sb_disk_groups;			goto err;		}err:	kfree(sorted);	return ret;} void bch2_disk_groups_to_text(struct printbuf *out, struct bch_fs *c){	out->atomic++;	rcu_read_lock(); 	struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);	if (!g)		goto out; 	for (unsigned i = 0; i < g->nr; i++) {		if (i)			prt_printf(out, " "); 		if (g->entries[i].deleted) {			prt_printf(out, "[deleted]");			continue;		} 		prt_printf(out, "[parent %d devs", g->entries[i].parent);		for_each_member_device_rcu(c, ca, &g->entries[i].devs)			prt_printf(out, " %s", ca->name);		prt_printf(out, "]");	} out:	rcu_read_unlock();	out->atomic--;} static void bch2_sb_disk_groups_to_text(struct printbuf *out,					struct bch_sb *sb,					struct bch_sb_field *f){	struct bch_sb_field_disk_groups *groups =		field_to_type(f, disk_groups);	struct bch_disk_group *g;	unsigned nr_groups = disk_groups_nr(groups); 	for (g = groups->entries;	     g < groups->entries + nr_groups;	     g++) {		if (g != groups->entries)			prt_printf(out, " "); 		if (BCH_GROUP_DELETED(g))			prt_printf(out, "[deleted]");		else			prt_printf(out, "[parent %llu name %s]",			       BCH_GROUP_PARENT(g), g->label);	}} const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {	.validate	= bch2_sb_disk_groups_validate,	.to_text	= bch2_sb_disk_groups_to_text}; int bch2_sb_disk_groups_to_cpu(struct bch_fs *c){	struct bch_sb_field_disk_groups *groups;	struct bch_disk_groups_cpu *cpu_g, *old_g;	unsigned i, g, nr_groups; 	lockdep_assert_held(&c->sb_lock); 	groups		= bch2_sb_field_get(c->disk_sb.sb, disk_groups);	nr_groups	= disk_groups_nr(groups); 	if (!groups)		return 0; 	cpu_g = kzalloc(struct_size(cpu_g, entries, nr_groups), GFP_KERNEL);	if (!cpu_g)		return -BCH_ERR_ENOMEM_disk_groups_to_cpu; 	cpu_g->nr = nr_groups; 	for (i = 0; i < nr_groups; i++) {		struct bch_disk_group *src	= &groups->entries[i];		struct bch_disk_group_cpu *dst	= &cpu_g->entries[i]; 		dst->deleted	= BCH_GROUP_DELETED(src);		dst->parent	= BCH_GROUP_PARENT(src);		memcpy(dst->label, src->label, sizeof(dst->label));	} 	for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {		struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, i);		struct bch_disk_group_cpu *dst; 		if (!bch2_member_alive(&m))			continue; 		g = BCH_MEMBER_GROUP(&m);		while (g) {			dst = &cpu_g->entries[g - 1];			__set_bit(i, dst->devs.d);			g = dst->parent;		}	} 	old_g = rcu_dereference_protected(c->disk_groups,				lockdep_is_held(&c->sb_lock));	rcu_assign_pointer(c->disk_groups, cpu_g);	if (old_g)		kfree_rcu(old_g, rcu); 	return 0;} const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target){	struct target t = target_decode(target);	struct bch_devs_mask *devs; 	rcu_read_lock(); 	switch (t.type) {	case TARGET_NULL:		devs = NULL;		break;	case TARGET_DEV: {		struct bch_dev *ca = t.dev < c->sb.nr_devices			? rcu_dereference(c->devs[t.dev])			: NULL;		devs = ca ? &ca->self : NULL;		break;	}	case TARGET_GROUP: {		struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups); 		devs = g && t.group < g->nr && !g->entries[t.group].deleted			? &g->entries[t.group].devs			: NULL;		break;	}	default:		BUG();	} 	rcu_read_unlock(); 	return devs;} bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target){	struct target t = target_decode(target); 	switch (t.type) {	case TARGET_NULL:		return false;	case TARGET_DEV:		return dev == t.dev;	case TARGET_GROUP: {		struct bch_disk_groups_cpu *g;		const struct bch_devs_mask *m;		bool ret; 		rcu_read_lock();		g = rcu_dereference(c->disk_groups);		m = g && t.group < g->nr && !g->entries[t.group].deleted			? &g->entries[t.group].devs			: NULL; 		ret = m ? test_bit(dev, m->d) : false;		rcu_read_unlock(); 		return ret;	}	default:		BUG();	}} static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,				  unsigned parent,				  const char *name, unsigned namelen){	unsigned i, nr_groups = disk_groups_nr(groups); 	if (!namelen || namelen > BCH_SB_LABEL_SIZE)		return -EINVAL; 	for (i = 0; i < nr_groups; i++) {		struct bch_disk_group *g = groups->entries + i; 		if (BCH_GROUP_DELETED(g))			continue; 		if (!BCH_GROUP_DELETED(g) &&		    BCH_GROUP_PARENT(g) == parent &&		    strnlen(g->label, sizeof(g->label)) == namelen &&		    !memcmp(name, g->label, namelen))			return i;	} 	return -1;} static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,				 const char *name, unsigned namelen){	struct bch_sb_field_disk_groups *groups =		bch2_sb_field_get(sb->sb, disk_groups);	unsigned i, nr_groups = disk_groups_nr(groups);	struct bch_disk_group *g; 	if (!namelen || namelen > BCH_SB_LABEL_SIZE)		return -EINVAL; 	for (i = 0;	     i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);	     i++)		; 	if (i == nr_groups) {		unsigned u64s =			(sizeof(struct bch_sb_field_disk_groups) +			 sizeof(struct bch_disk_group) * (nr_groups + 1)) /			sizeof(u64); 		groups = bch2_sb_field_resize(sb, disk_groups, u64s);		if (!groups)			return -BCH_ERR_ENOSPC_disk_label_add; 		nr_groups = disk_groups_nr(groups);	} 	BUG_ON(i >= nr_groups); 	g = &groups->entries[i]; 	memcpy(g->label, name, namelen);	if (namelen < sizeof(g->label))		g->label[namelen] = '\0';	SET_BCH_GROUP_DELETED(g, 0);	SET_BCH_GROUP_PARENT(g, parent);	SET_BCH_GROUP_DATA_ALLOWED(g, ~0); 	return i;} int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name){	struct bch_sb_field_disk_groups *groups =		bch2_sb_field_get(sb->sb, disk_groups);	int v = -1; 	do {		const char *next = strchrnul(name, '.');		unsigned len = next - name; 		if (*next == '.')			next++; 		v = __bch2_disk_group_find(groups, v + 1, name, len);		name = next;	} while (*name && v >= 0); 	return v;} int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name){	struct bch_sb_field_disk_groups *groups;	unsigned parent = 0;	int v = -1; 	do {		const char *next = strchrnul(name, '.');		unsigned len = next - name; 		if (*next == '.')			next++; 		groups = bch2_sb_field_get(sb->sb, disk_groups); 		v = __bch2_disk_group_find(groups, parent, name, len);		if (v < 0)			v = __bch2_disk_group_add(sb, parent, name, len);		if (v < 0)			return v; 		parent = v + 1;		name = next;	} while (*name && v >= 0); 	return v;} void bch2_disk_path_to_text(struct printbuf *out, struct bch_fs *c, unsigned v){	struct bch_disk_groups_cpu *groups;	struct bch_disk_group_cpu *g;	unsigned nr = 0;	u16 path[32]; 	out->atomic++;	rcu_read_lock();	groups = rcu_dereference(c->disk_groups);	if (!groups)		goto invalid; 	while (1) {		if (nr == ARRAY_SIZE(path))			goto invalid; 		if (v >= groups->nr)			goto invalid; 		g = groups->entries + v; 		if (g->deleted)			goto invalid; 		path[nr++] = v; 		if (!g->parent)			break; 		v = g->parent - 1;	} 	while (nr) {		v = path[--nr];		g = groups->entries + v; 		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);		if (nr)			prt_printf(out, ".");	}out:	rcu_read_unlock();	out->atomic--;	return;invalid:	prt_printf(out, "invalid label %u", v);	goto out;} void bch2_disk_path_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v){	struct bch_sb_field_disk_groups *groups =		bch2_sb_field_get(sb, disk_groups);	struct bch_disk_group *g;	unsigned nr = 0;	u16 path[32]; 	while (1) {		if (nr == ARRAY_SIZE(path))			goto inval; 		if (v >= disk_groups_nr(groups))			goto inval; 		g = groups->entries + v; 		if (BCH_GROUP_DELETED(g))			goto inval; 		path[nr++] = v; 		if (!BCH_GROUP_PARENT(g))			break; 		v = BCH_GROUP_PARENT(g) - 1;	} 	while (nr) {		v = path[--nr];		g = groups->entries + v; 		prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);		if (nr)			prt_printf(out, ".");	}	return;inval:	prt_printf(out, "invalid label %u", v);} int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name){	struct bch_member *mi;	int ret, v = -1; 	if (!strlen(name) || !strcmp(name, "none"))		return 0; 	v = bch2_disk_path_find_or_create(&c->disk_sb, name);	if (v < 0)		return v; 	ret = bch2_sb_disk_groups_to_cpu(c);	if (ret)		return ret; 	mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);	SET_BCH_MEMBER_GROUP(mi, v + 1);	return 0;} int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name){	int ret; 	mutex_lock(&c->sb_lock);	ret = __bch2_dev_group_set(c, ca, name) ?:		bch2_write_super(c);	mutex_unlock(&c->sb_lock); 

Lines 1–500 of 616.

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