aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-stripe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md/dm-stripe.c')
-rw-r--r--drivers/md/dm-stripe.c105
1 files changed, 103 insertions, 2 deletions
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 969944a8aba2..4de90ab3968b 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -14,10 +14,13 @@
14#include <linux/log2.h> 14#include <linux/log2.h>
15 15
16#define DM_MSG_PREFIX "striped" 16#define DM_MSG_PREFIX "striped"
17#define DM_IO_ERROR_THRESHOLD 15
17 18
18struct stripe { 19struct stripe {
19 struct dm_dev *dev; 20 struct dm_dev *dev;
20 sector_t physical_start; 21 sector_t physical_start;
22
23 atomic_t error_count;
21}; 24};
22 25
23struct stripe_c { 26struct stripe_c {
@@ -30,9 +33,29 @@ struct stripe_c {
30 uint32_t chunk_shift; 33 uint32_t chunk_shift;
31 sector_t chunk_mask; 34 sector_t chunk_mask;
32 35
36 /* Needed for handling events */
37 struct dm_target *ti;
38
39 /* Work struct used for triggering events*/
40 struct work_struct kstriped_ws;
41
33 struct stripe stripe[0]; 42 struct stripe stripe[0];
34}; 43};
35 44
45static struct workqueue_struct *kstriped;
46
47/*
48 * An event is triggered whenever a drive
49 * drops out of a stripe volume.
50 */
51static void trigger_event(struct work_struct *work)
52{
53 struct stripe_c *sc = container_of(work, struct stripe_c, kstriped_ws);
54
55 dm_table_event(sc->ti->table);
56
57}
58
36static inline struct stripe_c *alloc_context(unsigned int stripes) 59static inline struct stripe_c *alloc_context(unsigned int stripes)
37{ 60{
38 size_t len; 61 size_t len;
@@ -63,6 +86,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
63 return -ENXIO; 86 return -ENXIO;
64 87
65 sc->stripe[stripe].physical_start = start; 88 sc->stripe[stripe].physical_start = start;
89
66 return 0; 90 return 0;
67} 91}
68 92
@@ -135,6 +159,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
135 return -ENOMEM; 159 return -ENOMEM;
136 } 160 }
137 161
162 INIT_WORK(&sc->kstriped_ws, trigger_event);
163
164 /* Set pointer to dm target; used in trigger_event */
165 sc->ti = ti;
166
138 sc->stripes = stripes; 167 sc->stripes = stripes;
139 sc->stripe_width = width; 168 sc->stripe_width = width;
140 ti->split_io = chunk_size; 169 ti->split_io = chunk_size;
@@ -158,9 +187,11 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
158 kfree(sc); 187 kfree(sc);
159 return r; 188 return r;
160 } 189 }
190 atomic_set(&(sc->stripe[i].error_count), 0);
161 } 191 }
162 192
163 ti->private = sc; 193 ti->private = sc;
194
164 return 0; 195 return 0;
165} 196}
166 197
@@ -172,6 +203,7 @@ static void stripe_dtr(struct dm_target *ti)
172 for (i = 0; i < sc->stripes; i++) 203 for (i = 0; i < sc->stripes; i++)
173 dm_put_device(ti, sc->stripe[i].dev); 204 dm_put_device(ti, sc->stripe[i].dev);
174 205
206 flush_workqueue(kstriped);
175 kfree(sc); 207 kfree(sc);
176} 208}
177 209
@@ -190,16 +222,37 @@ static int stripe_map(struct dm_target *ti, struct bio *bio,
190 return DM_MAPIO_REMAPPED; 222 return DM_MAPIO_REMAPPED;
191} 223}
192 224
225/*
226 * Stripe status:
227 *
228 * INFO
229 * #stripes [stripe_name <stripe_name>] [group word count]
230 * [error count 'A|D' <error count 'A|D'>]
231 *
232 * TABLE
233 * #stripes [stripe chunk size]
234 * [stripe_name physical_start <stripe_name physical_start>]
235 *
236 */
237
193static int stripe_status(struct dm_target *ti, 238static int stripe_status(struct dm_target *ti,
194 status_type_t type, char *result, unsigned int maxlen) 239 status_type_t type, char *result, unsigned int maxlen)
195{ 240{
196 struct stripe_c *sc = (struct stripe_c *) ti->private; 241 struct stripe_c *sc = (struct stripe_c *) ti->private;
242 char buffer[sc->stripes + 1];
197 unsigned int sz = 0; 243 unsigned int sz = 0;
198 unsigned int i; 244 unsigned int i;
199 245
200 switch (type) { 246 switch (type) {
201 case STATUSTYPE_INFO: 247 case STATUSTYPE_INFO:
202 result[0] = '\0'; 248 DMEMIT("%d ", sc->stripes);
249 for (i = 0; i < sc->stripes; i++) {
250 DMEMIT("%s ", sc->stripe[i].dev->name);
251 buffer[i] = atomic_read(&(sc->stripe[i].error_count)) ?
252 'D' : 'A';
253 }
254 buffer[i] = '\0';
255 DMEMIT("1 %s", buffer);
203 break; 256 break;
204 257
205 case STATUSTYPE_TABLE: 258 case STATUSTYPE_TABLE:
@@ -213,13 +266,52 @@ static int stripe_status(struct dm_target *ti,
213 return 0; 266 return 0;
214} 267}
215 268
269static int stripe_end_io(struct dm_target *ti, struct bio *bio,
270 int error, union map_info *map_context)
271{
272 unsigned i;
273 char major_minor[16];
274 struct stripe_c *sc = ti->private;
275
276 if (!error)
277 return 0; /* I/O complete */
278
279 if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
280 return error;
281
282 if (error == -EOPNOTSUPP)
283 return error;
284
285 memset(major_minor, 0, sizeof(major_minor));
286 sprintf(major_minor, "%d:%d",
287 bio->bi_bdev->bd_disk->major,
288 bio->bi_bdev->bd_disk->first_minor);
289
290 /*
291 * Test to see which stripe drive triggered the event
292 * and increment error count for all stripes on that device.
293 * If the error count for a given device exceeds the threshold
294 * value we will no longer trigger any further events.
295 */
296 for (i = 0; i < sc->stripes; i++)
297 if (!strcmp(sc->stripe[i].dev->name, major_minor)) {
298 atomic_inc(&(sc->stripe[i].error_count));
299 if (atomic_read(&(sc->stripe[i].error_count)) <
300 DM_IO_ERROR_THRESHOLD)
301 queue_work(kstriped, &sc->kstriped_ws);
302 }
303
304 return error;
305}
306
216static struct target_type stripe_target = { 307static struct target_type stripe_target = {
217 .name = "striped", 308 .name = "striped",
218 .version= {1, 0, 2}, 309 .version = {1, 1, 0},
219 .module = THIS_MODULE, 310 .module = THIS_MODULE,
220 .ctr = stripe_ctr, 311 .ctr = stripe_ctr,
221 .dtr = stripe_dtr, 312 .dtr = stripe_dtr,
222 .map = stripe_map, 313 .map = stripe_map,
314 .end_io = stripe_end_io,
223 .status = stripe_status, 315 .status = stripe_status,
224}; 316};
225 317
@@ -231,6 +323,13 @@ int __init dm_stripe_init(void)
231 if (r < 0) 323 if (r < 0)
232 DMWARN("target registration failed"); 324 DMWARN("target registration failed");
233 325
326 kstriped = create_singlethread_workqueue("kstriped");
327 if (!kstriped) {
328 DMERR("failed to create workqueue kstriped");
329 dm_unregister_target(&stripe_target);
330 return -ENOMEM;
331 }
332
234 return r; 333 return r;
235} 334}
236 335
@@ -239,5 +338,7 @@ void dm_stripe_exit(void)
239 if (dm_unregister_target(&stripe_target)) 338 if (dm_unregister_target(&stripe_target))
240 DMWARN("target unregistration failed"); 339 DMWARN("target unregistration failed");
241 340
341 destroy_workqueue(kstriped);
342
242 return; 343 return;
243} 344}