diff options
Diffstat (limited to 'drivers/md/dm-stripe.c')
-rw-r--r-- | drivers/md/dm-stripe.c | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 969944a8aba2..7c5e2a0c3f2d 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 | ||
18 | struct stripe { | 19 | struct 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 | ||
23 | struct stripe_c { | 26 | struct 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 | ||
45 | static struct workqueue_struct *kstriped; | ||
46 | |||
47 | /* | ||
48 | * An event is triggered whenever a drive | ||
49 | * drops out of a stripe volume. | ||
50 | */ | ||
51 | static 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 | |||
36 | static inline struct stripe_c *alloc_context(unsigned int stripes) | 59 | static 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 | ||
@@ -213,13 +245,52 @@ static int stripe_status(struct dm_target *ti, | |||
213 | return 0; | 245 | return 0; |
214 | } | 246 | } |
215 | 247 | ||
248 | static int stripe_end_io(struct dm_target *ti, struct bio *bio, | ||
249 | int error, union map_info *map_context) | ||
250 | { | ||
251 | unsigned i; | ||
252 | char major_minor[16]; | ||
253 | struct stripe_c *sc = ti->private; | ||
254 | |||
255 | if (!error) | ||
256 | return 0; /* I/O complete */ | ||
257 | |||
258 | if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio)) | ||
259 | return error; | ||
260 | |||
261 | if (error == -EOPNOTSUPP) | ||
262 | return error; | ||
263 | |||
264 | memset(major_minor, 0, sizeof(major_minor)); | ||
265 | sprintf(major_minor, "%d:%d", | ||
266 | bio->bi_bdev->bd_disk->major, | ||
267 | bio->bi_bdev->bd_disk->first_minor); | ||
268 | |||
269 | /* | ||
270 | * Test to see which stripe drive triggered the event | ||
271 | * and increment error count for all stripes on that device. | ||
272 | * If the error count for a given device exceeds the threshold | ||
273 | * value we will no longer trigger any further events. | ||
274 | */ | ||
275 | for (i = 0; i < sc->stripes; i++) | ||
276 | if (!strcmp(sc->stripe[i].dev->name, major_minor)) { | ||
277 | atomic_inc(&(sc->stripe[i].error_count)); | ||
278 | if (atomic_read(&(sc->stripe[i].error_count)) < | ||
279 | DM_IO_ERROR_THRESHOLD) | ||
280 | queue_work(kstriped, &sc->kstriped_ws); | ||
281 | } | ||
282 | |||
283 | return error; | ||
284 | } | ||
285 | |||
216 | static struct target_type stripe_target = { | 286 | static struct target_type stripe_target = { |
217 | .name = "striped", | 287 | .name = "striped", |
218 | .version= {1, 0, 2}, | 288 | .version = {1, 1, 0}, |
219 | .module = THIS_MODULE, | 289 | .module = THIS_MODULE, |
220 | .ctr = stripe_ctr, | 290 | .ctr = stripe_ctr, |
221 | .dtr = stripe_dtr, | 291 | .dtr = stripe_dtr, |
222 | .map = stripe_map, | 292 | .map = stripe_map, |
293 | .end_io = stripe_end_io, | ||
223 | .status = stripe_status, | 294 | .status = stripe_status, |
224 | }; | 295 | }; |
225 | 296 | ||
@@ -231,6 +302,13 @@ int __init dm_stripe_init(void) | |||
231 | if (r < 0) | 302 | if (r < 0) |
232 | DMWARN("target registration failed"); | 303 | DMWARN("target registration failed"); |
233 | 304 | ||
305 | kstriped = create_singlethread_workqueue("kstriped"); | ||
306 | if (!kstriped) { | ||
307 | DMERR("failed to create workqueue kstriped"); | ||
308 | dm_unregister_target(&stripe_target); | ||
309 | return -ENOMEM; | ||
310 | } | ||
311 | |||
234 | return r; | 312 | return r; |
235 | } | 313 | } |
236 | 314 | ||
@@ -239,5 +317,7 @@ void dm_stripe_exit(void) | |||
239 | if (dm_unregister_target(&stripe_target)) | 317 | if (dm_unregister_target(&stripe_target)) |
240 | DMWARN("target unregistration failed"); | 318 | DMWARN("target unregistration failed"); |
241 | 319 | ||
320 | destroy_workqueue(kstriped); | ||
321 | |||
242 | return; | 322 | return; |
243 | } | 323 | } |