diff options
Diffstat (limited to 'drivers/block/aoe/aoedev.c')
-rw-r--r-- | drivers/block/aoe/aoedev.c | 166 |
1 files changed, 107 insertions, 59 deletions
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index f0c0c7416aed..3776715eb255 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #include "aoe.h" | 15 | #include "aoe.h" |
16 | 16 | ||
17 | static void dummy_timer(ulong); | 17 | static void dummy_timer(ulong); |
18 | static void aoedev_freedev(struct aoedev *); | ||
19 | static void freetgt(struct aoedev *d, struct aoetgt *t); | 18 | static void freetgt(struct aoedev *d, struct aoetgt *t); |
20 | static void skbpoolfree(struct aoedev *d); | 19 | static void skbpoolfree(struct aoedev *d); |
21 | 20 | ||
@@ -236,29 +235,6 @@ aoedev_downdev(struct aoedev *d) | |||
236 | set_capacity(d->gd, 0); | 235 | set_capacity(d->gd, 0); |
237 | } | 236 | } |
238 | 237 | ||
239 | static void | ||
240 | aoedev_freedev(struct aoedev *d) | ||
241 | { | ||
242 | struct aoetgt **t, **e; | ||
243 | |||
244 | cancel_work_sync(&d->work); | ||
245 | if (d->gd) { | ||
246 | aoedisk_rm_sysfs(d); | ||
247 | del_gendisk(d->gd); | ||
248 | put_disk(d->gd); | ||
249 | blk_cleanup_queue(d->blkq); | ||
250 | } | ||
251 | t = d->targets; | ||
252 | e = t + NTARGETS; | ||
253 | for (; t < e && *t; t++) | ||
254 | freetgt(d, *t); | ||
255 | if (d->bufpool) | ||
256 | mempool_destroy(d->bufpool); | ||
257 | skbpoolfree(d); | ||
258 | minor_free(d->sysminor); | ||
259 | kfree(d); | ||
260 | } | ||
261 | |||
262 | /* return whether the user asked for this particular | 238 | /* return whether the user asked for this particular |
263 | * device to be flushed | 239 | * device to be flushed |
264 | */ | 240 | */ |
@@ -283,17 +259,62 @@ user_req(char *s, size_t slen, struct aoedev *d) | |||
283 | return !strncmp(s, p, lim); | 259 | return !strncmp(s, p, lim); |
284 | } | 260 | } |
285 | 261 | ||
286 | int | 262 | static void |
287 | aoedev_flush(const char __user *str, size_t cnt) | 263 | freedev(struct aoedev *d) |
264 | { | ||
265 | struct aoetgt **t, **e; | ||
266 | int freeing = 0; | ||
267 | unsigned long flags; | ||
268 | |||
269 | spin_lock_irqsave(&d->lock, flags); | ||
270 | if (d->flags & DEVFL_TKILL | ||
271 | && !(d->flags & DEVFL_FREEING)) { | ||
272 | d->flags |= DEVFL_FREEING; | ||
273 | freeing = 1; | ||
274 | } | ||
275 | spin_unlock_irqrestore(&d->lock, flags); | ||
276 | if (!freeing) | ||
277 | return; | ||
278 | |||
279 | del_timer_sync(&d->timer); | ||
280 | if (d->gd) { | ||
281 | aoedisk_rm_sysfs(d); | ||
282 | del_gendisk(d->gd); | ||
283 | put_disk(d->gd); | ||
284 | blk_cleanup_queue(d->blkq); | ||
285 | } | ||
286 | t = d->targets; | ||
287 | e = t + NTARGETS; | ||
288 | for (; t < e && *t; t++) | ||
289 | freetgt(d, *t); | ||
290 | if (d->bufpool) | ||
291 | mempool_destroy(d->bufpool); | ||
292 | skbpoolfree(d); | ||
293 | minor_free(d->sysminor); | ||
294 | |||
295 | spin_lock_irqsave(&d->lock, flags); | ||
296 | d->flags |= DEVFL_FREED; | ||
297 | spin_unlock_irqrestore(&d->lock, flags); | ||
298 | } | ||
299 | |||
300 | enum flush_parms { | ||
301 | NOT_EXITING = 0, | ||
302 | EXITING = 1, | ||
303 | }; | ||
304 | |||
305 | static int | ||
306 | flush(const char __user *str, size_t cnt, int exiting) | ||
288 | { | 307 | { |
289 | ulong flags; | 308 | ulong flags; |
290 | struct aoedev *d, **dd; | 309 | struct aoedev *d, **dd; |
291 | struct aoedev *rmd = NULL; | ||
292 | char buf[16]; | 310 | char buf[16]; |
293 | int all = 0; | 311 | int all = 0; |
294 | int specified = 0; /* flush a specific device */ | 312 | int specified = 0; /* flush a specific device */ |
313 | unsigned int skipflags; | ||
314 | |||
315 | skipflags = DEVFL_GDALLOC | DEVFL_NEWSIZE | DEVFL_TKILL; | ||
295 | 316 | ||
296 | if (cnt >= 3) { | 317 | if (!exiting && cnt >= 3) { |
297 | if (cnt > sizeof buf) | 318 | if (cnt > sizeof buf) |
298 | cnt = sizeof buf; | 319 | cnt = sizeof buf; |
299 | if (copy_from_user(buf, str, cnt)) | 320 | if (copy_from_user(buf, str, cnt)) |
@@ -303,39 +324,71 @@ aoedev_flush(const char __user *str, size_t cnt) | |||
303 | specified = 1; | 324 | specified = 1; |
304 | } | 325 | } |
305 | 326 | ||
327 | flush_scheduled_work(); | ||
328 | /* pass one: without sleeping, do aoedev_downdev */ | ||
306 | spin_lock_irqsave(&devlist_lock, flags); | 329 | spin_lock_irqsave(&devlist_lock, flags); |
307 | dd = &devlist; | 330 | for (d = devlist; d; d = d->next) { |
308 | while ((d = *dd)) { | ||
309 | spin_lock(&d->lock); | 331 | spin_lock(&d->lock); |
310 | if (specified) { | 332 | if (exiting) { |
333 | /* unconditionally take each device down */ | ||
334 | } else if (specified) { | ||
311 | if (!user_req(buf, cnt, d)) | 335 | if (!user_req(buf, cnt, d)) |
312 | goto skip; | 336 | goto cont; |
313 | } else if ((!all && (d->flags & DEVFL_UP)) | 337 | } else if ((!all && (d->flags & DEVFL_UP)) |
314 | || (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE)) | 338 | || d->flags & skipflags |
315 | || d->nopen | 339 | || d->nopen |
316 | || d->ref) | 340 | || d->ref) |
317 | goto skip; | 341 | goto cont; |
318 | 342 | ||
319 | *dd = d->next; | ||
320 | aoedev_downdev(d); | 343 | aoedev_downdev(d); |
321 | d->flags |= DEVFL_TKILL; | 344 | d->flags |= DEVFL_TKILL; |
345 | cont: | ||
322 | spin_unlock(&d->lock); | 346 | spin_unlock(&d->lock); |
323 | d->next = rmd; | ||
324 | rmd = d; | ||
325 | continue; | ||
326 | skip: | ||
327 | spin_unlock(&d->lock); | ||
328 | dd = &d->next; | ||
329 | } | 347 | } |
330 | spin_unlock_irqrestore(&devlist_lock, flags); | 348 | spin_unlock_irqrestore(&devlist_lock, flags); |
331 | while ((d = rmd)) { | 349 | |
332 | rmd = d->next; | 350 | /* pass two: call freedev, which might sleep, |
333 | del_timer_sync(&d->timer); | 351 | * for aoedevs marked with DEVFL_TKILL |
334 | aoedev_freedev(d); /* must be able to sleep */ | 352 | */ |
353 | restart: | ||
354 | spin_lock_irqsave(&devlist_lock, flags); | ||
355 | for (d = devlist; d; d = d->next) { | ||
356 | spin_lock(&d->lock); | ||
357 | if (d->flags & DEVFL_TKILL | ||
358 | && !(d->flags & DEVFL_FREEING)) { | ||
359 | spin_unlock(&d->lock); | ||
360 | spin_unlock_irqrestore(&devlist_lock, flags); | ||
361 | freedev(d); | ||
362 | goto restart; | ||
363 | } | ||
364 | spin_unlock(&d->lock); | ||
335 | } | 365 | } |
366 | |||
367 | /* pass three: remove aoedevs marked with DEVFL_FREED */ | ||
368 | for (dd = &devlist, d = *dd; d; d = *dd) { | ||
369 | struct aoedev *doomed = NULL; | ||
370 | |||
371 | spin_lock(&d->lock); | ||
372 | if (d->flags & DEVFL_FREED) { | ||
373 | *dd = d->next; | ||
374 | doomed = d; | ||
375 | } else { | ||
376 | dd = &d->next; | ||
377 | } | ||
378 | spin_unlock(&d->lock); | ||
379 | kfree(doomed); | ||
380 | } | ||
381 | spin_unlock_irqrestore(&devlist_lock, flags); | ||
382 | |||
336 | return 0; | 383 | return 0; |
337 | } | 384 | } |
338 | 385 | ||
386 | int | ||
387 | aoedev_flush(const char __user *str, size_t cnt) | ||
388 | { | ||
389 | return flush(str, cnt, NOT_EXITING); | ||
390 | } | ||
391 | |||
339 | /* This has been confirmed to occur once with Tms=3*1000 due to the | 392 | /* This has been confirmed to occur once with Tms=3*1000 due to the |
340 | * driver changing link and not processing its transmit ring. The | 393 | * driver changing link and not processing its transmit ring. The |
341 | * problem is hard enough to solve by returning an error that I'm | 394 | * problem is hard enough to solve by returning an error that I'm |
@@ -388,7 +441,14 @@ aoedev_by_aoeaddr(ulong maj, int min, int do_alloc) | |||
388 | 441 | ||
389 | for (d=devlist; d; d=d->next) | 442 | for (d=devlist; d; d=d->next) |
390 | if (d->aoemajor == maj && d->aoeminor == min) { | 443 | if (d->aoemajor == maj && d->aoeminor == min) { |
444 | spin_lock(&d->lock); | ||
445 | if (d->flags & DEVFL_TKILL) { | ||
446 | spin_unlock(&d->lock); | ||
447 | d = NULL; | ||
448 | goto out; | ||
449 | } | ||
391 | d->ref++; | 450 | d->ref++; |
451 | spin_unlock(&d->lock); | ||
392 | break; | 452 | break; |
393 | } | 453 | } |
394 | if (d || !do_alloc || minor_get(&sysminor, maj, min) < 0) | 454 | if (d || !do_alloc || minor_get(&sysminor, maj, min) < 0) |
@@ -448,21 +508,9 @@ freetgt(struct aoedev *d, struct aoetgt *t) | |||
448 | void | 508 | void |
449 | aoedev_exit(void) | 509 | aoedev_exit(void) |
450 | { | 510 | { |
451 | struct aoedev *d; | 511 | flush_scheduled_work(); |
452 | ulong flags; | ||
453 | |||
454 | aoe_flush_iocq(); | 512 | aoe_flush_iocq(); |
455 | while ((d = devlist)) { | 513 | flush(NULL, 0, EXITING); |
456 | devlist = d->next; | ||
457 | |||
458 | spin_lock_irqsave(&d->lock, flags); | ||
459 | aoedev_downdev(d); | ||
460 | d->flags |= DEVFL_TKILL; | ||
461 | spin_unlock_irqrestore(&d->lock, flags); | ||
462 | |||
463 | del_timer_sync(&d->timer); | ||
464 | aoedev_freedev(d); | ||
465 | } | ||
466 | } | 514 | } |
467 | 515 | ||
468 | int __init | 516 | int __init |