aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2005-08-16 19:27:34 -0400
committerJames Bottomley <jejb@titanic.(none)>2005-08-30 23:48:51 -0400
commit61a7afa2c476a3be261cf88a95b0dea0c3bd29d4 (patch)
tree68c2724e2dbda8a8581592583af0d538b63db244 /drivers
parent2b7d6a8cb9718fc1d9e826201b64909c44a915f4 (diff)
[SCSI] embryonic RAID class
The idea behind a RAID class is to provide a uniform interface to all RAID subsystems (both hardware and software) in the kernel. To do that, I've made this class a transport class that's entirely subsystem independent (although the matching routines have to match per subsystem, as you'll see looking at the code). I put it in the scsi subdirectory purely because I needed somewhere to play with it, but it's not a scsi specific module. I used a fusion raid card as the test bed for this; with that kind of card, this is the type of class output you get: jejb@titanic> ls -l /sys/class/raid_devices/20\:0\:0\:0/ total 0 lrwxrwxrwx 1 root root 0 Aug 16 17:21 component-0 -> ../../../devices/pci0000:80/0000:80:04.0/host20/target20:1:0/20:1:0:0/ lrwxrwxrwx 1 root root 0 Aug 16 17:21 component-1 -> ../../../devices/pci0000:80/0000:80:04.0/host20/target20:1:1/20:1:1:0/ lrwxrwxrwx 1 root root 0 Aug 16 17:21 device -> ../../../devices/pci0000:80/0000:80:04.0/host20/target20:0:0/20:0:0:0/ -r--r--r-- 1 root root 16384 Aug 16 17:21 level -r--r--r-- 1 root root 16384 Aug 16 17:21 resync -r--r--r-- 1 root root 16384 Aug 16 17:21 state So it's really simple: for a SCSI device representing a hardware raid, it shows the raid level, the array state, the resync % complete (if the state is resyncing) and the underlying components of the RAID (these are exposed in fusion on the virtual channel 1). As you can see, this type of information can be exported by almost anything, including software raid. The more difficult trick, of course, is going to be getting it to perform configuration type actions with writable attributes. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/Kconfig6
-rw-r--r--drivers/scsi/Makefile2
-rw-r--r--drivers/scsi/raid_class.c250
3 files changed, 258 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 96df148ed969..68adc3cc8ad2 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1,5 +1,11 @@
1menu "SCSI device support" 1menu "SCSI device support"
2 2
3config RAID_ATTRS
4 tristate "RAID Transport Class"
5 default n
6 ---help---
7 Provides RAID
8
3config SCSI 9config SCSI
4 tristate "SCSI device support" 10 tristate "SCSI device support"
5 ---help--- 11 ---help---
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 3746fb9fa2f5..85f9e6bb34b9 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -22,6 +22,8 @@ subdir-$(CONFIG_PCMCIA) += pcmcia
22 22
23obj-$(CONFIG_SCSI) += scsi_mod.o 23obj-$(CONFIG_SCSI) += scsi_mod.o
24 24
25obj-$(CONFIG_RAID_ATTRS) += raid_class.o
26
25# --- NOTE ORDERING HERE --- 27# --- NOTE ORDERING HERE ---
26# For kernel non-modular link, transport attributes need to 28# For kernel non-modular link, transport attributes need to
27# be initialised before drivers 29# be initialised before drivers
diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c
new file mode 100644
index 000000000000..f1ea5027865f
--- /dev/null
+++ b/drivers/scsi/raid_class.c
@@ -0,0 +1,250 @@
1/*
2 * RAID Attributes
3 */
4#include <linux/init.h>
5#include <linux/module.h>
6#include <linux/list.h>
7#include <linux/raid_class.h>
8#include <scsi/scsi_device.h>
9#include <scsi/scsi_host.h>
10
11#define RAID_NUM_ATTRS 3
12
13struct raid_internal {
14 struct raid_template r;
15 struct raid_function_template *f;
16 /* The actual attributes */
17 struct class_device_attribute private_attrs[RAID_NUM_ATTRS];
18 /* The array of null terminated pointers to attributes
19 * needed by scsi_sysfs.c */
20 struct class_device_attribute *attrs[RAID_NUM_ATTRS + 1];
21};
22
23struct raid_component {
24 struct list_head node;
25 struct device *dev;
26 int num;
27};
28
29#define to_raid_internal(tmpl) container_of(tmpl, struct raid_internal, r)
30
31#define tc_to_raid_internal(tcont) ({ \
32 struct raid_template *r = \
33 container_of(tcont, struct raid_template, raid_attrs); \
34 to_raid_internal(r); \
35})
36
37#define ac_to_raid_internal(acont) ({ \
38 struct transport_container *tc = \
39 container_of(acont, struct transport_container, ac); \
40 tc_to_raid_internal(tc); \
41})
42
43#define class_device_to_raid_internal(cdev) ({ \
44 struct attribute_container *ac = \
45 attribute_container_classdev_to_container(cdev); \
46 ac_to_raid_internal(ac); \
47})
48
49
50static int raid_match(struct attribute_container *cont, struct device *dev)
51{
52 /* We have to look for every subsystem that could house
53 * emulated RAID devices, so start with SCSI */
54 struct raid_internal *i = ac_to_raid_internal(cont);
55
56 if (scsi_is_sdev_device(dev)) {
57 struct scsi_device *sdev = to_scsi_device(dev);
58
59 if (i->f->cookie != sdev->host->hostt)
60 return 0;
61
62 return i->f->is_raid(dev);
63 }
64 /* FIXME: look at other subsystems too */
65 return 0;
66}
67
68static int raid_setup(struct transport_container *tc, struct device *dev,
69 struct class_device *cdev)
70{
71 struct raid_data *rd;
72
73 BUG_ON(class_get_devdata(cdev));
74
75 rd = kmalloc(sizeof(*rd), GFP_KERNEL);
76 if (!rd)
77 return -ENOMEM;
78
79 memset(rd, 0, sizeof(*rd));
80 INIT_LIST_HEAD(&rd->component_list);
81 class_set_devdata(cdev, rd);
82
83 return 0;
84}
85
86static int raid_remove(struct transport_container *tc, struct device *dev,
87 struct class_device *cdev)
88{
89 struct raid_data *rd = class_get_devdata(cdev);
90 struct raid_component *rc, *next;
91 class_set_devdata(cdev, NULL);
92 list_for_each_entry_safe(rc, next, &rd->component_list, node) {
93 char buf[40];
94 snprintf(buf, sizeof(buf), "component-%d", rc->num);
95 list_del(&rc->node);
96 sysfs_remove_link(&cdev->kobj, buf);
97 kfree(rc);
98 }
99 kfree(class_get_devdata(cdev));
100 return 0;
101}
102
103static DECLARE_TRANSPORT_CLASS(raid_class,
104 "raid_devices",
105 raid_setup,
106 raid_remove,
107 NULL);
108
109static struct {
110 enum raid_state value;
111 char *name;
112} raid_states[] = {
113 { RAID_ACTIVE, "active" },
114 { RAID_DEGRADED, "degraded" },
115 { RAID_RESYNCING, "resyncing" },
116 { RAID_OFFLINE, "offline" },
117};
118
119static const char *raid_state_name(enum raid_state state)
120{
121 int i;
122 char *name = NULL;
123
124 for (i = 0; i < sizeof(raid_states)/sizeof(raid_states[0]); i++) {
125 if (raid_states[i].value == state) {
126 name = raid_states[i].name;
127 break;
128 }
129 }
130 return name;
131}
132
133
134#define raid_attr_show_internal(attr, fmt, var, code) \
135static ssize_t raid_show_##attr(struct class_device *cdev, char *buf) \
136{ \
137 struct raid_data *rd = class_get_devdata(cdev); \
138 code \
139 return snprintf(buf, 20, #fmt "\n", var); \
140}
141
142#define raid_attr_ro_states(attr, states, code) \
143raid_attr_show_internal(attr, %s, name, \
144 const char *name; \
145 code \
146 name = raid_##states##_name(rd->attr); \
147) \
148static CLASS_DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
149
150
151#define raid_attr_ro_internal(attr, code) \
152raid_attr_show_internal(attr, %d, rd->attr, code) \
153static CLASS_DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL)
154
155#define ATTR_CODE(attr) \
156 struct raid_internal *i = class_device_to_raid_internal(cdev); \
157 if (i->f->get_##attr) \
158 i->f->get_##attr(cdev->dev);
159
160#define raid_attr_ro(attr) raid_attr_ro_internal(attr, )
161#define raid_attr_ro_fn(attr) raid_attr_ro_internal(attr, ATTR_CODE(attr))
162#define raid_attr_ro_state(attr) raid_attr_ro_states(attr, attr, ATTR_CODE(attr))
163
164raid_attr_ro(level);
165raid_attr_ro_fn(resync);
166raid_attr_ro_state(state);
167
168void raid_component_add(struct raid_template *r,struct device *raid_dev,
169 struct device *component_dev)
170{
171 struct class_device *cdev =
172 attribute_container_find_class_device(&r->raid_attrs.ac,
173 raid_dev);
174 struct raid_component *rc;
175 struct raid_data *rd = class_get_devdata(cdev);
176 char buf[40];
177
178 rc = kmalloc(sizeof(*rc), GFP_KERNEL);
179 if (!rc)
180 return;
181
182 INIT_LIST_HEAD(&rc->node);
183 rc->dev = component_dev;
184 rc->num = rd->component_count++;
185
186 snprintf(buf, sizeof(buf), "component-%d", rc->num);
187 list_add_tail(&rc->node, &rd->component_list);
188 sysfs_create_link(&cdev->kobj, &component_dev->kobj, buf);
189}
190EXPORT_SYMBOL(raid_component_add);
191
192struct raid_template *
193raid_class_attach(struct raid_function_template *ft)
194{
195 struct raid_internal *i = kmalloc(sizeof(struct raid_internal),
196 GFP_KERNEL);
197 int count = 0;
198
199 if (unlikely(!i))
200 return NULL;
201
202 memset(i, 0, sizeof(*i));
203
204 i->f = ft;
205
206 i->r.raid_attrs.ac.class = &raid_class.class;
207 i->r.raid_attrs.ac.match = raid_match;
208 i->r.raid_attrs.ac.attrs = &i->attrs[0];
209
210 attribute_container_register(&i->r.raid_attrs.ac);
211
212 i->attrs[count++] = &class_device_attr_level;
213 i->attrs[count++] = &class_device_attr_resync;
214 i->attrs[count++] = &class_device_attr_state;
215
216 i->attrs[count] = NULL;
217 BUG_ON(count > RAID_NUM_ATTRS);
218
219 return &i->r;
220}
221EXPORT_SYMBOL(raid_class_attach);
222
223void
224raid_class_release(struct raid_template *r)
225{
226 struct raid_internal *i = to_raid_internal(r);
227
228 attribute_container_unregister(&i->r.raid_attrs.ac);
229
230 kfree(i);
231}
232EXPORT_SYMBOL(raid_class_release);
233
234static __init int raid_init(void)
235{
236 return transport_class_register(&raid_class);
237}
238
239static __exit void raid_exit(void)
240{
241 transport_class_unregister(&raid_class);
242}
243
244MODULE_AUTHOR("James Bottomley");
245MODULE_DESCRIPTION("RAID device class");
246MODULE_LICENSE("GPL");
247
248module_init(raid_init);
249module_exit(raid_exit);
250