Skip to content

Commit e63ed0d

Browse files
James BottomleyJames Bottomley
authored andcommitted
[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 <[email protected]> Tested-by: Sarah Sharp <[email protected]> Cc: [email protected] # delay backport by 2 months for field testing Signed-off-by: James Bottomley <[email protected]>
1 parent 81b86d4 commit e63ed0d

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

drivers/scsi/scsi_scan.c

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,31 @@ static struct scsi_target *__scsi_find_target(struct device *parent,
370370
return found_starget;
371371
}
372372

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+
*/
382+
static 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+
393+
static void scsi_target_reap_ref_put(struct scsi_target *starget)
394+
{
395+
kref_put(&starget->reap_ref, scsi_target_reap_ref_release);
396+
}
397+
373398
/**
374399
* scsi_alloc_target - allocate a new or find an existing target
375400
* @parent: parent of the target (need not be a scsi host)
@@ -392,7 +417,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
392417
+ shost->transportt->target_size;
393418
struct scsi_target *starget;
394419
struct scsi_target *found_target;
395-
int error;
420+
int error, ref_got;
396421

397422
starget = kzalloc(size, GFP_KERNEL);
398423
if (!starget) {
@@ -401,7 +426,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
401426
}
402427
dev = &starget->dev;
403428
device_initialize(dev);
404-
starget->reap_ref = 1;
429+
kref_init(&starget->reap_ref);
405430
dev->parent = get_device(parent);
406431
dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id);
407432
dev->bus = &scsi_bus_type;
@@ -441,29 +466,36 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
441466
return starget;
442467

443468
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+
445476
spin_unlock_irqrestore(shost->host_lock, flags);
446-
if (found_target->state != STARGET_DEL) {
477+
if (ref_got) {
447478
put_device(dev);
448479
return found_target;
449480
}
450-
/* Unfortunately, we found a dying target; need to
451-
* wait until it's dead before we can get a new one */
481+
/*
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+
*/
452490
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);
454496
goto retry;
455497
}
456498

457-
static 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-
467499
/**
468500
* scsi_target_reap - check to see if target is in use and destroy if not
469501
* @starget: target to be checked
@@ -474,28 +506,11 @@ static void scsi_target_reap_usercontext(struct work_struct *work)
474506
*/
475507
void scsi_target_reap(struct scsi_target *starget)
476508
{
477-
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
478-
unsigned long flags;
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)
509+
BUG_ON(starget->state == STARGET_DEL);
510+
if (starget->state == STARGET_CREATED)
495511
scsi_target_destroy(starget);
496512
else
497-
execute_in_process_context(scsi_target_reap_usercontext,
498-
&starget->ew);
513+
scsi_target_reap_ref_put(starget);
499514
}
500515

501516
/**
@@ -1532,6 +1547,10 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
15321547
}
15331548
mutex_unlock(&shost->scan_mutex);
15341549
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+
*/
15351554
scsi_target_reap(starget);
15361555
put_device(&starget->dev);
15371556

@@ -1612,8 +1631,10 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
16121631

16131632
out_reap:
16141633
scsi_autopm_put_target(starget);
1615-
/* now determine if the target has any children at all
1616-
* and if not, nuke it */
1634+
/*
1635+
* paired with scsi_alloc_target(): determine if the target has
1636+
* any children at all and if not, nuke it
1637+
*/
16171638
scsi_target_reap(starget);
16181639

16191640
put_device(&starget->dev);

drivers/scsi/scsi_sysfs.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,17 +383,14 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
383383
{
384384
struct scsi_device *sdev;
385385
struct device *parent;
386-
struct scsi_target *starget;
387386
struct list_head *this, *tmp;
388387
unsigned long flags;
389388

390389
sdev = container_of(work, struct scsi_device, ew.work);
391390

392391
parent = sdev->sdev_gendev.parent;
393-
starget = to_scsi_target(parent);
394392

395393
spin_lock_irqsave(sdev->host->host_lock, flags);
396-
starget->reap_ref++;
397394
list_del(&sdev->siblings);
398395
list_del(&sdev->same_target_siblings);
399396
list_del(&sdev->starved_entry);
@@ -413,8 +410,6 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
413410
/* NULL queue means the device can't be used */
414411
sdev->request_queue = NULL;
415412

416-
scsi_target_reap(scsi_target(sdev));
417-
418413
kfree(sdev->inquiry);
419414
kfree(sdev);
420415

@@ -1071,6 +1066,13 @@ void __scsi_remove_device(struct scsi_device *sdev)
10711066
sdev->host->hostt->slave_destroy(sdev);
10721067
transport_destroy_device(dev);
10731068

1069+
/*
1070+
* Paired with the kref_get() in scsi_sysfs_initialize(). We have
1071+
* remoed sysfs visibility from the device, so make the target
1072+
* invisible if this was the last device underneath it.
1073+
*/
1074+
scsi_target_reap(scsi_target(sdev));
1075+
10741076
put_device(dev);
10751077
}
10761078

@@ -1133,7 +1135,7 @@ void scsi_remove_target(struct device *dev)
11331135
continue;
11341136
if (starget->dev.parent == dev || &starget->dev == dev) {
11351137
/* assuming new targets arrive at the end */
1136-
starget->reap_ref++;
1138+
kref_get(&starget->reap_ref);
11371139
spin_unlock_irqrestore(shost->host_lock, flags);
11381140
if (last)
11391141
scsi_target_reap(last);
@@ -1217,6 +1219,12 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
12171219
list_add_tail(&sdev->same_target_siblings, &starget->devices);
12181220
list_add_tail(&sdev->siblings, &shost->__devices);
12191221
spin_unlock_irqrestore(shost->host_lock, flags);
1222+
/*
1223+
* device can now only be removed via __scsi_remove_device() so hold
1224+
* the target. Target will be held in CREATED state until something
1225+
* beneath it becomes visible (in which case it moves to RUNNING)
1226+
*/
1227+
kref_get(&starget->reap_ref);
12201228
}
12211229

12221230
int scsi_is_sdev_device(const struct device *dev)

include/scsi/scsi_device.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ struct scsi_target {
269269
struct list_head siblings;
270270
struct list_head devices;
271271
struct device dev;
272-
unsigned int reap_ref; /* protected by the host lock */
272+
struct kref reap_ref; /* last put renders target invisible */
273273
unsigned int channel;
274274
unsigned int id; /* target id ... replace
275275
* scsi_device.id eventually */
@@ -296,7 +296,6 @@ struct scsi_target {
296296
#define SCSI_DEFAULT_TARGET_BLOCKED 3
297297

298298
char scsi_level;
299-
struct execute_work ew;
300299
enum scsi_target_state state;
301300
void *hostdata; /* available to low-level driver */
302301
unsigned long starget_data[0]; /* for the transport */

0 commit comments

Comments
 (0)