162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * raid_class.c - implementation of a simple raid visualisation class
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This class is designed to allow raid attributes to be visualised and
862306a36Sopenharmony_ci * manipulated in a form independent of the underlying raid.  Ultimately this
962306a36Sopenharmony_ci * should work for both hardware and software raids.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/raid_class.h>
1762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
1862306a36Sopenharmony_ci#include <scsi/scsi_host.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define RAID_NUM_ATTRS	3
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct raid_internal {
2362306a36Sopenharmony_ci	struct raid_template r;
2462306a36Sopenharmony_ci	struct raid_function_template *f;
2562306a36Sopenharmony_ci	/* The actual attributes */
2662306a36Sopenharmony_ci	struct device_attribute private_attrs[RAID_NUM_ATTRS];
2762306a36Sopenharmony_ci	/* The array of null terminated pointers to attributes
2862306a36Sopenharmony_ci	 * needed by scsi_sysfs.c */
2962306a36Sopenharmony_ci	struct device_attribute *attrs[RAID_NUM_ATTRS + 1];
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct raid_component {
3362306a36Sopenharmony_ci	struct list_head node;
3462306a36Sopenharmony_ci	struct device dev;
3562306a36Sopenharmony_ci	int num;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define to_raid_internal(tmpl)	container_of(tmpl, struct raid_internal, r)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define tc_to_raid_internal(tcont) ({					\
4162306a36Sopenharmony_ci	struct raid_template *r =					\
4262306a36Sopenharmony_ci		container_of(tcont, struct raid_template, raid_attrs);	\
4362306a36Sopenharmony_ci	to_raid_internal(r);						\
4462306a36Sopenharmony_ci})
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define ac_to_raid_internal(acont) ({					\
4762306a36Sopenharmony_ci	struct transport_container *tc =				\
4862306a36Sopenharmony_ci		container_of(acont, struct transport_container, ac);	\
4962306a36Sopenharmony_ci	tc_to_raid_internal(tc);					\
5062306a36Sopenharmony_ci})
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define device_to_raid_internal(dev) ({				\
5362306a36Sopenharmony_ci	struct attribute_container *ac =				\
5462306a36Sopenharmony_ci		attribute_container_classdev_to_container(dev);	\
5562306a36Sopenharmony_ci	ac_to_raid_internal(ac);					\
5662306a36Sopenharmony_ci})
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int raid_match(struct attribute_container *cont, struct device *dev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	/* We have to look for every subsystem that could house
6262306a36Sopenharmony_ci	 * emulated RAID devices, so start with SCSI */
6362306a36Sopenharmony_ci	struct raid_internal *i = ac_to_raid_internal(cont);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SCSI) && scsi_is_sdev_device(dev)) {
6662306a36Sopenharmony_ci		struct scsi_device *sdev = to_scsi_device(dev);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		if (i->f->cookie != sdev->host->hostt)
6962306a36Sopenharmony_ci			return 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		return i->f->is_raid(dev);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	/* FIXME: look at other subsystems too */
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int raid_setup(struct transport_container *tc, struct device *dev,
7862306a36Sopenharmony_ci		       struct device *cdev)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct raid_data *rd;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	BUG_ON(dev_get_drvdata(cdev));
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rd = kzalloc(sizeof(*rd), GFP_KERNEL);
8562306a36Sopenharmony_ci	if (!rd)
8662306a36Sopenharmony_ci		return -ENOMEM;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	INIT_LIST_HEAD(&rd->component_list);
8962306a36Sopenharmony_ci	dev_set_drvdata(cdev, rd);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int raid_remove(struct transport_container *tc, struct device *dev,
9562306a36Sopenharmony_ci		       struct device *cdev)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct raid_data *rd = dev_get_drvdata(cdev);
9862306a36Sopenharmony_ci	struct raid_component *rc, *next;
9962306a36Sopenharmony_ci	dev_printk(KERN_ERR, dev, "RAID REMOVE\n");
10062306a36Sopenharmony_ci	dev_set_drvdata(cdev, NULL);
10162306a36Sopenharmony_ci	list_for_each_entry_safe(rc, next, &rd->component_list, node) {
10262306a36Sopenharmony_ci		list_del(&rc->node);
10362306a36Sopenharmony_ci		dev_printk(KERN_ERR, rc->dev.parent, "RAID COMPONENT REMOVE\n");
10462306a36Sopenharmony_ci		device_unregister(&rc->dev);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	dev_printk(KERN_ERR, dev, "RAID REMOVE DONE\n");
10762306a36Sopenharmony_ci	kfree(rd);
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(raid_class,
11262306a36Sopenharmony_ci			       "raid_devices",
11362306a36Sopenharmony_ci			       raid_setup,
11462306a36Sopenharmony_ci			       raid_remove,
11562306a36Sopenharmony_ci			       NULL);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct {
11862306a36Sopenharmony_ci	enum raid_state	value;
11962306a36Sopenharmony_ci	char		*name;
12062306a36Sopenharmony_ci} raid_states[] = {
12162306a36Sopenharmony_ci	{ RAID_STATE_UNKNOWN, "unknown" },
12262306a36Sopenharmony_ci	{ RAID_STATE_ACTIVE, "active" },
12362306a36Sopenharmony_ci	{ RAID_STATE_DEGRADED, "degraded" },
12462306a36Sopenharmony_ci	{ RAID_STATE_RESYNCING, "resyncing" },
12562306a36Sopenharmony_ci	{ RAID_STATE_OFFLINE, "offline" },
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const char *raid_state_name(enum raid_state state)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	int i;
13162306a36Sopenharmony_ci	char *name = NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(raid_states); i++) {
13462306a36Sopenharmony_ci		if (raid_states[i].value == state) {
13562306a36Sopenharmony_ci			name = raid_states[i].name;
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	return name;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic struct {
14362306a36Sopenharmony_ci	enum raid_level value;
14462306a36Sopenharmony_ci	char *name;
14562306a36Sopenharmony_ci} raid_levels[] = {
14662306a36Sopenharmony_ci	{ RAID_LEVEL_UNKNOWN, "unknown" },
14762306a36Sopenharmony_ci	{ RAID_LEVEL_LINEAR, "linear" },
14862306a36Sopenharmony_ci	{ RAID_LEVEL_0, "raid0" },
14962306a36Sopenharmony_ci	{ RAID_LEVEL_1, "raid1" },
15062306a36Sopenharmony_ci	{ RAID_LEVEL_10, "raid10" },
15162306a36Sopenharmony_ci	{ RAID_LEVEL_1E, "raid1e" },
15262306a36Sopenharmony_ci	{ RAID_LEVEL_3, "raid3" },
15362306a36Sopenharmony_ci	{ RAID_LEVEL_4, "raid4" },
15462306a36Sopenharmony_ci	{ RAID_LEVEL_5, "raid5" },
15562306a36Sopenharmony_ci	{ RAID_LEVEL_50, "raid50" },
15662306a36Sopenharmony_ci	{ RAID_LEVEL_6, "raid6" },
15762306a36Sopenharmony_ci	{ RAID_LEVEL_JBOD, "jbod" },
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const char *raid_level_name(enum raid_level level)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int i;
16362306a36Sopenharmony_ci	char *name = NULL;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(raid_levels); i++) {
16662306a36Sopenharmony_ci		if (raid_levels[i].value == level) {
16762306a36Sopenharmony_ci			name = raid_levels[i].name;
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return name;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define raid_attr_show_internal(attr, fmt, var, code)			\
17562306a36Sopenharmony_cistatic ssize_t raid_show_##attr(struct device *dev, 			\
17662306a36Sopenharmony_ci				struct device_attribute *attr, 		\
17762306a36Sopenharmony_ci				char *buf)				\
17862306a36Sopenharmony_ci{									\
17962306a36Sopenharmony_ci	struct raid_data *rd = dev_get_drvdata(dev);			\
18062306a36Sopenharmony_ci	code								\
18162306a36Sopenharmony_ci	return snprintf(buf, 20, #fmt "\n", var);			\
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#define raid_attr_ro_states(attr, states, code)				\
18562306a36Sopenharmony_ciraid_attr_show_internal(attr, %s, name,					\
18662306a36Sopenharmony_ci	const char *name;						\
18762306a36Sopenharmony_ci	code								\
18862306a36Sopenharmony_ci	name = raid_##states##_name(rd->attr);				\
18962306a36Sopenharmony_ci)									\
19062306a36Sopenharmony_cistatic DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci#define raid_attr_ro_internal(attr, code)				\
19462306a36Sopenharmony_ciraid_attr_show_internal(attr, %d, rd->attr, code)			\
19562306a36Sopenharmony_cistatic DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#define ATTR_CODE(attr)							\
19862306a36Sopenharmony_ci	struct raid_internal *i = device_to_raid_internal(dev);		\
19962306a36Sopenharmony_ci	if (i->f->get_##attr)						\
20062306a36Sopenharmony_ci		i->f->get_##attr(dev->parent);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci#define raid_attr_ro(attr)	raid_attr_ro_internal(attr, )
20362306a36Sopenharmony_ci#define raid_attr_ro_fn(attr)	raid_attr_ro_internal(attr, ATTR_CODE(attr))
20462306a36Sopenharmony_ci#define raid_attr_ro_state(attr)	raid_attr_ro_states(attr, attr, )
20562306a36Sopenharmony_ci#define raid_attr_ro_state_fn(attr)	raid_attr_ro_states(attr, attr, ATTR_CODE(attr))
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ciraid_attr_ro_state(level);
20962306a36Sopenharmony_ciraid_attr_ro_fn(resync);
21062306a36Sopenharmony_ciraid_attr_ro_state_fn(state);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistruct raid_template *
21362306a36Sopenharmony_ciraid_class_attach(struct raid_function_template *ft)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct raid_internal *i = kzalloc(sizeof(struct raid_internal),
21662306a36Sopenharmony_ci					  GFP_KERNEL);
21762306a36Sopenharmony_ci	int count = 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (unlikely(!i))
22062306a36Sopenharmony_ci		return NULL;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	i->f = ft;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	i->r.raid_attrs.ac.class = &raid_class.class;
22562306a36Sopenharmony_ci	i->r.raid_attrs.ac.match = raid_match;
22662306a36Sopenharmony_ci	i->r.raid_attrs.ac.attrs = &i->attrs[0];
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	attribute_container_register(&i->r.raid_attrs.ac);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	i->attrs[count++] = &dev_attr_level;
23162306a36Sopenharmony_ci	i->attrs[count++] = &dev_attr_resync;
23262306a36Sopenharmony_ci	i->attrs[count++] = &dev_attr_state;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	i->attrs[count] = NULL;
23562306a36Sopenharmony_ci	BUG_ON(count > RAID_NUM_ATTRS);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return &i->r;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ciEXPORT_SYMBOL(raid_class_attach);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid
24262306a36Sopenharmony_ciraid_class_release(struct raid_template *r)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct raid_internal *i = to_raid_internal(r);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	BUG_ON(attribute_container_unregister(&i->r.raid_attrs.ac));
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	kfree(i);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL(raid_class_release);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic __init int raid_init(void)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	return transport_class_register(&raid_class);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic __exit void raid_exit(void)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	transport_class_unregister(&raid_class);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ciMODULE_AUTHOR("James Bottomley");
26362306a36Sopenharmony_ciMODULE_DESCRIPTION("RAID device class");
26462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cimodule_init(raid_init);
26762306a36Sopenharmony_cimodule_exit(raid_exit);
26862306a36Sopenharmony_ci
269