aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_scan.c
diff options
context:
space:
mode:
authorJames Bottomley <JBottomley@Parallels.com>2014-01-21 10:00:50 -0500
committerJames Bottomley <JBottomley@Parallels.com>2014-03-15 13:18:59 -0400
commite63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 (patch)
tree64f3dd3c794d009538993445d8039f04407dc593 /drivers/scsi/scsi_scan.c
parent81b86d4d275244ad7e134e95cbcfee49198da854 (diff)
[SCSI] fix our current target reap infrastructure
This patch eliminates the reap_ref and replaces it with a proper kref. On last put of this kref, the target is removed from visibility in sysfs. The final call to scsi_target_reap() for the device is done from __scsi_remove_device() and only if the device was made visible. This ensures that the target disappears as soon as the last device is gone rather than waiting until final release of the device (which is often too long). Reviewed-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@vger.kernel.org # delay backport by 2 months for field testing Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/scsi_scan.c')
-rw-r--r--drivers/scsi/scsi_scan.c99
1 files changed, 60 insertions, 39 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 307a81137607..5fad646ee6e5 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -371,6 +371,31 @@ static struct scsi_target *__scsi_find_target(struct device *parent,
371} 371}
372 372
373/** 373/**
374 * scsi_target_reap_ref_release - remove target from visibility
375 * @kref: the reap_ref in the target being released
376 *
377 * Called on last put of reap_ref, which is the indication that no device
378 * under this target is visible anymore, so render the target invisible in
379 * sysfs. Note: we have to be in user context here because the target reaps
380 * should be done in places where the scsi device visibility is being removed.
381 */
382static void scsi_target_reap_ref_release(struct kref *kref)
383{
384 struct scsi_target *starget
385 = container_of(kref, struct scsi_target, reap_ref);
386
387 transport_remove_device(&starget->dev);
388 device_del(&starget->dev);
389 starget->state = STARGET_DEL;
390 scsi_target_destroy(starget);
391}
392
393static void scsi_target_reap_ref_put(struct scsi_target *starget)
394{
395 kref_put(&starget->reap_ref, scsi_target_reap_ref_release);
396}
397
398/**
374 * scsi_alloc_target - allocate a new or find an existing target 399 * scsi_alloc_target - allocate a new or find an existing target
375 * @parent: parent of the target (need not be a scsi host) 400 * @parent: parent of the target (need not be a scsi host)
376 * @channel: target channel number (zero if no channels) 401 * @channel: target channel number (zero if no channels)
@@ -392,7 +417,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
392 + shost->transportt->target_size; 417 + shost->transportt->target_size;
393 struct scsi_target *starget; 418 struct scsi_target *starget;
394 struct scsi_target *found_target; 419 struct scsi_target *found_target;
395 int error; 420 int error, ref_got;
396 421
397 starget = kzalloc(size, GFP_KERNEL); 422 starget = kzalloc(size, GFP_KERNEL);
398 if (!starget) { 423 if (!starget) {
@@ -401,7 +426,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
401 } 426 }
402 dev = &starget->dev; 427 dev = &starget->dev;
403 device_initialize(dev); 428 device_initialize(dev);
404 starget->reap_ref = 1; 429 kref_init(&starget->reap_ref);
405 dev->parent = get_device(parent); 430 dev->parent = get_device(parent);
406 dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); 431 dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id);
407 dev->bus = &scsi_bus_type; 432 dev->bus = &scsi_bus_type;
@@ -441,29 +466,36 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
441 return starget; 466 return starget;
442 467
443 found: 468 found:
444 found_target->reap_ref++; 469 /*
470 * release routine already fired if kref is zero, so if we can still
471 * take the reference, the target must be alive. If we can't, it must
472 * be dying and we need to wait for a new target
473 */
474 ref_got = kref_get_unless_zero(&found_target->reap_ref);
475
445 spin_unlock_irqrestore(shost->host_lock, flags); 476 spin_unlock_irqrestore(shost->host_lock, flags);
446 if (found_target->state != STARGET_DEL) { 477 if (ref_got) {
447 put_device(dev); 478 put_device(dev);
448 return found_target; 479 return found_target;
449 } 480 }
450 /* Unfortunately, we found a dying target; need to 481 /*
451 * wait until it's dead before we can get a new one */ 482 * Unfortunately, we found a dying target; need to wait until it's
483 * dead before we can get a new one. There is an anomaly here. We
484 * *should* call scsi_target_reap() to balance the kref_get() of the
485 * reap_ref above. However, since the target being released, it's
486 * already invisible and the reap_ref is irrelevant. If we call
487 * scsi_target_reap() we might spuriously do another device_del() on
488 * an already invisible target.
489 */
452 put_device(&found_target->dev); 490 put_device(&found_target->dev);
453 flush_scheduled_work(); 491 /*
492 * length of time is irrelevant here, we just want to yield the CPU
493 * for a tick to avoid busy waiting for the target to die.
494 */
495 msleep(1);
454 goto retry; 496 goto retry;
455} 497}
456 498
457static void scsi_target_reap_usercontext(struct work_struct *work)
458{
459 struct scsi_target *starget =
460 container_of(work, struct scsi_target, ew.work);
461
462 transport_remove_device(&starget->dev);
463 device_del(&starget->dev);
464 scsi_target_destroy(starget);
465}
466
467/** 499/**
468 * scsi_target_reap - check to see if target is in use and destroy if not 500 * scsi_target_reap - check to see if target is in use and destroy if not
469 * @starget: target to be checked 501 * @starget: target to be checked
@@ -474,28 +506,11 @@ static void scsi_target_reap_usercontext(struct work_struct *work)
474 */ 506 */
475void scsi_target_reap(struct scsi_target *starget) 507void scsi_target_reap(struct scsi_target *starget)
476{ 508{
477 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 509 BUG_ON(starget->state == STARGET_DEL);
478 unsigned long flags; 510 if (starget->state == STARGET_CREATED)
479 enum scsi_target_state state;
480 int empty = 0;
481
482 spin_lock_irqsave(shost->host_lock, flags);
483 state = starget->state;
484 if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
485 empty = 1;
486 starget->state = STARGET_DEL;
487 }
488 spin_unlock_irqrestore(shost->host_lock, flags);
489
490 if (!empty)
491 return;
492
493 BUG_ON(state == STARGET_DEL);
494 if (state == STARGET_CREATED)
495 scsi_target_destroy(starget); 511 scsi_target_destroy(starget);
496 else 512 else
497 execute_in_process_context(scsi_target_reap_usercontext, 513 scsi_target_reap_ref_put(starget);
498 &starget->ew);
499} 514}
500 515
501/** 516/**
@@ -1532,6 +1547,10 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
1532 } 1547 }
1533 mutex_unlock(&shost->scan_mutex); 1548 mutex_unlock(&shost->scan_mutex);
1534 scsi_autopm_put_target(starget); 1549 scsi_autopm_put_target(starget);
1550 /*
1551 * paired with scsi_alloc_target(). Target will be destroyed unless
1552 * scsi_probe_and_add_lun made an underlying device visible
1553 */
1535 scsi_target_reap(starget); 1554 scsi_target_reap(starget);
1536 put_device(&starget->dev); 1555 put_device(&starget->dev);
1537 1556
@@ -1612,8 +1631,10 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
1612 1631
1613 out_reap: 1632 out_reap:
1614 scsi_autopm_put_target(starget); 1633 scsi_autopm_put_target(starget);
1615 /* now determine if the target has any children at all 1634 /*
1616 * and if not, nuke it */ 1635 * paired with scsi_alloc_target(): determine if the target has
1636 * any children at all and if not, nuke it
1637 */
1617 scsi_target_reap(starget); 1638 scsi_target_reap(starget);
1618 1639
1619 put_device(&starget->dev); 1640 put_device(&starget->dev);