提交 884d25cc 编写于 作者: J James Bottomley 提交者: James Bottomley

[SCSI] Fix refcount breakage with 'echo "1" > scan' when target already present

Spotted by: Dan Aloni <da-xx@monatomic.org>

The problem is there's inconsistent locking semantic usage of
scsi_alloc_target().  Two callers assume the target comes back with
reference unincremented and the third assumes its incremented.  Fix by
always making the reference incremented on return.  Also fix path in
target alloc that could consistently increment the parent lock.
Finally document scsi_alloc_target() so its callers know what the
expectations are.
Signed-off-by: NJames Bottomley <James.Bottomley@SteelEye.com>
上级 26dacd0c
...@@ -266,6 +266,18 @@ static struct scsi_target *__scsi_find_target(struct device *parent, ...@@ -266,6 +266,18 @@ static struct scsi_target *__scsi_find_target(struct device *parent,
return found_starget; return found_starget;
} }
/**
* scsi_alloc_target - allocate a new or find an existing target
* @parent: parent of the target (need not be a scsi host)
* @channel: target channel number (zero if no channels)
* @id: target id number
*
* Return an existing target if one exists, provided it hasn't already
* gone into STARGET_DEL state, otherwise allocate a new target.
*
* The target is returned with an incremented reference, so the caller
* is responsible for both reaping and doing a last put
*/
static struct scsi_target *scsi_alloc_target(struct device *parent, static struct scsi_target *scsi_alloc_target(struct device *parent,
int channel, uint id) int channel, uint id)
{ {
...@@ -331,14 +343,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, ...@@ -331,14 +343,15 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
return NULL; return NULL;
} }
} }
get_device(dev);
return starget; return starget;
found: found:
found_target->reap_ref++; found_target->reap_ref++;
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
put_device(parent);
if (found_target->state != STARGET_DEL) { if (found_target->state != STARGET_DEL) {
put_device(parent);
kfree(starget); kfree(starget);
return found_target; return found_target;
} }
...@@ -1341,7 +1354,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, ...@@ -1341,7 +1354,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
if (!starget) if (!starget)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
get_device(&starget->dev);
mutex_lock(&shost->scan_mutex); mutex_lock(&shost->scan_mutex);
if (scsi_host_scan_allowed(shost)) if (scsi_host_scan_allowed(shost))
scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
...@@ -1400,7 +1412,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, ...@@ -1400,7 +1412,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
if (!starget) if (!starget)
return; return;
get_device(&starget->dev);
if (lun != SCAN_WILD_CARD) { if (lun != SCAN_WILD_CARD) {
/* /*
* Scan for a specific host/chan/id/lun. * Scan for a specific host/chan/id/lun.
...@@ -1582,7 +1593,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) ...@@ -1582,7 +1593,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
if (sdev) { if (sdev) {
sdev->sdev_gendev.parent = get_device(&starget->dev); sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->borken = 0; sdev->borken = 0;
} } else
scsi_target_reap(starget);
put_device(&starget->dev); put_device(&starget->dev);
out: out:
mutex_unlock(&shost->scan_mutex); mutex_unlock(&shost->scan_mutex);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册