diff options
-rw-r--r-- | drivers/scsi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 2 | ||||
-rw-r--r-- | drivers/scsi/raid_class.c | 250 | ||||
-rw-r--r-- | include/linux/raid_class.h | 59 |
4 files changed, 317 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 @@ | |||
1 | menu "SCSI device support" | 1 | menu "SCSI device support" |
2 | 2 | ||
3 | config RAID_ATTRS | ||
4 | tristate "RAID Transport Class" | ||
5 | default n | ||
6 | ---help--- | ||
7 | Provides RAID | ||
8 | |||
3 | config SCSI | 9 | config 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 | ||
23 | obj-$(CONFIG_SCSI) += scsi_mod.o | 23 | obj-$(CONFIG_SCSI) += scsi_mod.o |
24 | 24 | ||
25 | obj-$(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 | |||
13 | struct 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 | |||
23 | struct 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 | |||
50 | static 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 | |||
68 | static 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 | |||
86 | static 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 | |||
103 | static DECLARE_TRANSPORT_CLASS(raid_class, | ||
104 | "raid_devices", | ||
105 | raid_setup, | ||
106 | raid_remove, | ||
107 | NULL); | ||
108 | |||
109 | static 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 | |||
119 | static 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) \ | ||
135 | static 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) \ | ||
143 | raid_attr_show_internal(attr, %s, name, \ | ||
144 | const char *name; \ | ||
145 | code \ | ||
146 | name = raid_##states##_name(rd->attr); \ | ||
147 | ) \ | ||
148 | static CLASS_DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL) | ||
149 | |||
150 | |||
151 | #define raid_attr_ro_internal(attr, code) \ | ||
152 | raid_attr_show_internal(attr, %d, rd->attr, code) \ | ||
153 | static 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 | |||
164 | raid_attr_ro(level); | ||
165 | raid_attr_ro_fn(resync); | ||
166 | raid_attr_ro_state(state); | ||
167 | |||
168 | void 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 | } | ||
190 | EXPORT_SYMBOL(raid_component_add); | ||
191 | |||
192 | struct raid_template * | ||
193 | raid_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 | } | ||
221 | EXPORT_SYMBOL(raid_class_attach); | ||
222 | |||
223 | void | ||
224 | raid_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 | } | ||
232 | EXPORT_SYMBOL(raid_class_release); | ||
233 | |||
234 | static __init int raid_init(void) | ||
235 | { | ||
236 | return transport_class_register(&raid_class); | ||
237 | } | ||
238 | |||
239 | static __exit void raid_exit(void) | ||
240 | { | ||
241 | transport_class_unregister(&raid_class); | ||
242 | } | ||
243 | |||
244 | MODULE_AUTHOR("James Bottomley"); | ||
245 | MODULE_DESCRIPTION("RAID device class"); | ||
246 | MODULE_LICENSE("GPL"); | ||
247 | |||
248 | module_init(raid_init); | ||
249 | module_exit(raid_exit); | ||
250 | |||
diff --git a/include/linux/raid_class.h b/include/linux/raid_class.h new file mode 100644 index 000000000000..a71123c28272 --- /dev/null +++ b/include/linux/raid_class.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | */ | ||
3 | #include <linux/transport_class.h> | ||
4 | |||
5 | struct raid_template { | ||
6 | struct transport_container raid_attrs; | ||
7 | }; | ||
8 | |||
9 | struct raid_function_template { | ||
10 | void *cookie; | ||
11 | int (*is_raid)(struct device *); | ||
12 | void (*get_resync)(struct device *); | ||
13 | void (*get_state)(struct device *); | ||
14 | }; | ||
15 | |||
16 | enum raid_state { | ||
17 | RAID_ACTIVE = 1, | ||
18 | RAID_DEGRADED, | ||
19 | RAID_RESYNCING, | ||
20 | RAID_OFFLINE, | ||
21 | }; | ||
22 | |||
23 | struct raid_data { | ||
24 | struct list_head component_list; | ||
25 | int component_count; | ||
26 | int level; | ||
27 | enum raid_state state; | ||
28 | int resync; | ||
29 | }; | ||
30 | |||
31 | #define DEFINE_RAID_ATTRIBUTE(type, attr) \ | ||
32 | static inline void \ | ||
33 | raid_set_##attr(struct raid_template *r, struct device *dev, type value) { \ | ||
34 | struct class_device *cdev = \ | ||
35 | attribute_container_find_class_device(&r->raid_attrs.ac, dev);\ | ||
36 | struct raid_data *rd; \ | ||
37 | BUG_ON(!cdev); \ | ||
38 | rd = class_get_devdata(cdev); \ | ||
39 | rd->attr = value; \ | ||
40 | } \ | ||
41 | static inline type \ | ||
42 | raid_get_##attr(struct raid_template *r, struct device *dev) { \ | ||
43 | struct class_device *cdev = \ | ||
44 | attribute_container_find_class_device(&r->raid_attrs.ac, dev);\ | ||
45 | struct raid_data *rd; \ | ||
46 | BUG_ON(!cdev); \ | ||
47 | rd = class_get_devdata(cdev); \ | ||
48 | return rd->attr; \ | ||
49 | } | ||
50 | |||
51 | DEFINE_RAID_ATTRIBUTE(int, level) | ||
52 | DEFINE_RAID_ATTRIBUTE(int, resync) | ||
53 | DEFINE_RAID_ATTRIBUTE(enum raid_state, state) | ||
54 | |||
55 | struct raid_template *raid_class_attach(struct raid_function_template *); | ||
56 | void raid_class_release(struct raid_template *); | ||
57 | |||
58 | void raid_component_add(struct raid_template *, struct device *, | ||
59 | struct device *); | ||