18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * raid_class.c - implementation of a simple raid visualisation class
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This class is designed to allow raid attributes to be visualised and
88c2ecf20Sopenharmony_ci * manipulated in a form independent of the underlying raid.  Ultimately this
98c2ecf20Sopenharmony_ci * should work for both hardware and software raids.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/list.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/raid_class.h>
178c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
188c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define RAID_NUM_ATTRS	3
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct raid_internal {
238c2ecf20Sopenharmony_ci	struct raid_template r;
248c2ecf20Sopenharmony_ci	struct raid_function_template *f;
258c2ecf20Sopenharmony_ci	/* The actual attributes */
268c2ecf20Sopenharmony_ci	struct device_attribute private_attrs[RAID_NUM_ATTRS];
278c2ecf20Sopenharmony_ci	/* The array of null terminated pointers to attributes
288c2ecf20Sopenharmony_ci	 * needed by scsi_sysfs.c */
298c2ecf20Sopenharmony_ci	struct device_attribute *attrs[RAID_NUM_ATTRS + 1];
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct raid_component {
338c2ecf20Sopenharmony_ci	struct list_head node;
348c2ecf20Sopenharmony_ci	struct device dev;
358c2ecf20Sopenharmony_ci	int num;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define to_raid_internal(tmpl)	container_of(tmpl, struct raid_internal, r)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define tc_to_raid_internal(tcont) ({					\
418c2ecf20Sopenharmony_ci	struct raid_template *r =					\
428c2ecf20Sopenharmony_ci		container_of(tcont, struct raid_template, raid_attrs);	\
438c2ecf20Sopenharmony_ci	to_raid_internal(r);						\
448c2ecf20Sopenharmony_ci})
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define ac_to_raid_internal(acont) ({					\
478c2ecf20Sopenharmony_ci	struct transport_container *tc =				\
488c2ecf20Sopenharmony_ci		container_of(acont, struct transport_container, ac);	\
498c2ecf20Sopenharmony_ci	tc_to_raid_internal(tc);					\
508c2ecf20Sopenharmony_ci})
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define device_to_raid_internal(dev) ({				\
538c2ecf20Sopenharmony_ci	struct attribute_container *ac =				\
548c2ecf20Sopenharmony_ci		attribute_container_classdev_to_container(dev);	\
558c2ecf20Sopenharmony_ci	ac_to_raid_internal(ac);					\
568c2ecf20Sopenharmony_ci})
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int raid_match(struct attribute_container *cont, struct device *dev)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	/* We have to look for every subsystem that could house
628c2ecf20Sopenharmony_ci	 * emulated RAID devices, so start with SCSI */
638c2ecf20Sopenharmony_ci	struct raid_internal *i = ac_to_raid_internal(cont);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_SCSI) && scsi_is_sdev_device(dev)) {
668c2ecf20Sopenharmony_ci		struct scsi_device *sdev = to_scsi_device(dev);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		if (i->f->cookie != sdev->host->hostt)
698c2ecf20Sopenharmony_ci			return 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		return i->f->is_raid(dev);
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	/* FIXME: look at other subsystems too */
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int raid_setup(struct transport_container *tc, struct device *dev,
788c2ecf20Sopenharmony_ci		       struct device *cdev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct raid_data *rd;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	BUG_ON(dev_get_drvdata(cdev));
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	rd = kzalloc(sizeof(*rd), GFP_KERNEL);
858c2ecf20Sopenharmony_ci	if (!rd)
868c2ecf20Sopenharmony_ci		return -ENOMEM;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rd->component_list);
898c2ecf20Sopenharmony_ci	dev_set_drvdata(cdev, rd);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int raid_remove(struct transport_container *tc, struct device *dev,
958c2ecf20Sopenharmony_ci		       struct device *cdev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct raid_data *rd = dev_get_drvdata(cdev);
988c2ecf20Sopenharmony_ci	struct raid_component *rc, *next;
998c2ecf20Sopenharmony_ci	dev_printk(KERN_ERR, dev, "RAID REMOVE\n");
1008c2ecf20Sopenharmony_ci	dev_set_drvdata(cdev, NULL);
1018c2ecf20Sopenharmony_ci	list_for_each_entry_safe(rc, next, &rd->component_list, node) {
1028c2ecf20Sopenharmony_ci		list_del(&rc->node);
1038c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, rc->dev.parent, "RAID COMPONENT REMOVE\n");
1048c2ecf20Sopenharmony_ci		device_unregister(&rc->dev);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	dev_printk(KERN_ERR, dev, "RAID REMOVE DONE\n");
1078c2ecf20Sopenharmony_ci	kfree(rd);
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(raid_class,
1128c2ecf20Sopenharmony_ci			       "raid_devices",
1138c2ecf20Sopenharmony_ci			       raid_setup,
1148c2ecf20Sopenharmony_ci			       raid_remove,
1158c2ecf20Sopenharmony_ci			       NULL);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic const struct {
1188c2ecf20Sopenharmony_ci	enum raid_state	value;
1198c2ecf20Sopenharmony_ci	char		*name;
1208c2ecf20Sopenharmony_ci} raid_states[] = {
1218c2ecf20Sopenharmony_ci	{ RAID_STATE_UNKNOWN, "unknown" },
1228c2ecf20Sopenharmony_ci	{ RAID_STATE_ACTIVE, "active" },
1238c2ecf20Sopenharmony_ci	{ RAID_STATE_DEGRADED, "degraded" },
1248c2ecf20Sopenharmony_ci	{ RAID_STATE_RESYNCING, "resyncing" },
1258c2ecf20Sopenharmony_ci	{ RAID_STATE_OFFLINE, "offline" },
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const char *raid_state_name(enum raid_state state)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int i;
1318c2ecf20Sopenharmony_ci	char *name = NULL;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(raid_states); i++) {
1348c2ecf20Sopenharmony_ci		if (raid_states[i].value == state) {
1358c2ecf20Sopenharmony_ci			name = raid_states[i].name;
1368c2ecf20Sopenharmony_ci			break;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	return name;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic struct {
1438c2ecf20Sopenharmony_ci	enum raid_level value;
1448c2ecf20Sopenharmony_ci	char *name;
1458c2ecf20Sopenharmony_ci} raid_levels[] = {
1468c2ecf20Sopenharmony_ci	{ RAID_LEVEL_UNKNOWN, "unknown" },
1478c2ecf20Sopenharmony_ci	{ RAID_LEVEL_LINEAR, "linear" },
1488c2ecf20Sopenharmony_ci	{ RAID_LEVEL_0, "raid0" },
1498c2ecf20Sopenharmony_ci	{ RAID_LEVEL_1, "raid1" },
1508c2ecf20Sopenharmony_ci	{ RAID_LEVEL_10, "raid10" },
1518c2ecf20Sopenharmony_ci	{ RAID_LEVEL_1E, "raid1e" },
1528c2ecf20Sopenharmony_ci	{ RAID_LEVEL_3, "raid3" },
1538c2ecf20Sopenharmony_ci	{ RAID_LEVEL_4, "raid4" },
1548c2ecf20Sopenharmony_ci	{ RAID_LEVEL_5, "raid5" },
1558c2ecf20Sopenharmony_ci	{ RAID_LEVEL_50, "raid50" },
1568c2ecf20Sopenharmony_ci	{ RAID_LEVEL_6, "raid6" },
1578c2ecf20Sopenharmony_ci	{ RAID_LEVEL_JBOD, "jbod" },
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const char *raid_level_name(enum raid_level level)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	int i;
1638c2ecf20Sopenharmony_ci	char *name = NULL;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(raid_levels); i++) {
1668c2ecf20Sopenharmony_ci		if (raid_levels[i].value == level) {
1678c2ecf20Sopenharmony_ci			name = raid_levels[i].name;
1688c2ecf20Sopenharmony_ci			break;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	return name;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define raid_attr_show_internal(attr, fmt, var, code)			\
1758c2ecf20Sopenharmony_cistatic ssize_t raid_show_##attr(struct device *dev, 			\
1768c2ecf20Sopenharmony_ci				struct device_attribute *attr, 		\
1778c2ecf20Sopenharmony_ci				char *buf)				\
1788c2ecf20Sopenharmony_ci{									\
1798c2ecf20Sopenharmony_ci	struct raid_data *rd = dev_get_drvdata(dev);			\
1808c2ecf20Sopenharmony_ci	code								\
1818c2ecf20Sopenharmony_ci	return snprintf(buf, 20, #fmt "\n", var);			\
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#define raid_attr_ro_states(attr, states, code)				\
1858c2ecf20Sopenharmony_ciraid_attr_show_internal(attr, %s, name,					\
1868c2ecf20Sopenharmony_ci	const char *name;						\
1878c2ecf20Sopenharmony_ci	code								\
1888c2ecf20Sopenharmony_ci	name = raid_##states##_name(rd->attr);				\
1898c2ecf20Sopenharmony_ci)									\
1908c2ecf20Sopenharmony_cistatic DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci#define raid_attr_ro_internal(attr, code)				\
1948c2ecf20Sopenharmony_ciraid_attr_show_internal(attr, %d, rd->attr, code)			\
1958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#define ATTR_CODE(attr)							\
1988c2ecf20Sopenharmony_ci	struct raid_internal *i = device_to_raid_internal(dev);		\
1998c2ecf20Sopenharmony_ci	if (i->f->get_##attr)						\
2008c2ecf20Sopenharmony_ci		i->f->get_##attr(dev->parent);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci#define raid_attr_ro(attr)	raid_attr_ro_internal(attr, )
2038c2ecf20Sopenharmony_ci#define raid_attr_ro_fn(attr)	raid_attr_ro_internal(attr, ATTR_CODE(attr))
2048c2ecf20Sopenharmony_ci#define raid_attr_ro_state(attr)	raid_attr_ro_states(attr, attr, )
2058c2ecf20Sopenharmony_ci#define raid_attr_ro_state_fn(attr)	raid_attr_ro_states(attr, attr, ATTR_CODE(attr))
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciraid_attr_ro_state(level);
2098c2ecf20Sopenharmony_ciraid_attr_ro_fn(resync);
2108c2ecf20Sopenharmony_ciraid_attr_ro_state_fn(state);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistruct raid_template *
2138c2ecf20Sopenharmony_ciraid_class_attach(struct raid_function_template *ft)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct raid_internal *i = kzalloc(sizeof(struct raid_internal),
2168c2ecf20Sopenharmony_ci					  GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	int count = 0;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (unlikely(!i))
2208c2ecf20Sopenharmony_ci		return NULL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	i->f = ft;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	i->r.raid_attrs.ac.class = &raid_class.class;
2258c2ecf20Sopenharmony_ci	i->r.raid_attrs.ac.match = raid_match;
2268c2ecf20Sopenharmony_ci	i->r.raid_attrs.ac.attrs = &i->attrs[0];
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	attribute_container_register(&i->r.raid_attrs.ac);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	i->attrs[count++] = &dev_attr_level;
2318c2ecf20Sopenharmony_ci	i->attrs[count++] = &dev_attr_resync;
2328c2ecf20Sopenharmony_ci	i->attrs[count++] = &dev_attr_state;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	i->attrs[count] = NULL;
2358c2ecf20Sopenharmony_ci	BUG_ON(count > RAID_NUM_ATTRS);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return &i->r;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(raid_class_attach);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_civoid
2428c2ecf20Sopenharmony_ciraid_class_release(struct raid_template *r)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct raid_internal *i = to_raid_internal(r);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	BUG_ON(attribute_container_unregister(&i->r.raid_attrs.ac));
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	kfree(i);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(raid_class_release);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic __init int raid_init(void)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	return transport_class_register(&raid_class);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic __exit void raid_exit(void)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	transport_class_unregister(&raid_class);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Bottomley");
2638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RAID device class");
2648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cimodule_init(raid_init);
2678c2ecf20Sopenharmony_cimodule_exit(raid_exit);
2688c2ecf20Sopenharmony_ci
269