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