Restart scsi_device_lookup_by_target
PATCH 1/1 Restart scsi_device_lookup_by_target
Hi all,
currently we have this:
struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget,
uint lun)
{
struct scsi_device *sdev;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
sdev = __scsi_device_lookup_by_target(starget, lun);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
return sdev;
}now consider an sdev list with two entries for LUN 0, the first being in SDEV_DEL and the second being in SDEV_RUNNING.
scsi_device_lookup_by_target will always return NULL here, as it will never be able to skip past the first (unuseable) entry. So scsi_report_lun_scan will happily create duplicate sdevs for LUN 0 and we'll be getting the infamous
sysfs: duplicate filename 'xxxx' can not be created
errors.
What we should be doing here is to restart__scsi_device_lookup_by_target() if we find an
sdev but cannot use it (ie is in SDEV_DEL).
James, please apply.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Markus Rex, HRB 16746 (AG Nürnberg)
Restart scsi_device_lookup_by_target()
When scsi_device_lookup_by_target() will always return the first sdev with a matching LUN, regardless of the state. However, when this sdev is in SDEV_DEL it's quite possible to have additional sdevs in the list with the same LUN which are active.
So we should restart __scsi_device_lookup_by_target() whenever we find an sdev with state SDEV_DEL.
drivers/scsi/esp_scsi.c +1 -1
@@ -1158,7 +1158,7 @@ static int esp_reconnect(struct esp *esp) ESP_BUSID); tp = &esp->target[target]; - dev =__scsi_device_lookup_by_target (tp->starget, lun); + dev =__scsi_device_lookup_by_target (tp->starget,NULL , lun);if (!dev) { printk(KERN_ERR PFX "esp%d: Reconnect, no lp " "tgt[%u] lun[%u]\n",
drivers/scsi/scsi.c +13 -6
@@ -1094,23 +1094,29 @@ EXPORT_SYMBOL(__starget_for_each_device);/** * __scsi_device_lookup_by_target - find a device given the target (UNLOCKED) * @starget: SCSI target pointer +* @sdev: SCSI device pointer * @lun: SCSI Logical Unit Number * * Description: Looks up the scsi_device with the specified @lun for a given * @starget. The returned scsi_device does not have an additional * reference. You must hold the host's host_lock over this call and -* any access to the returned scsi_device. +* any access to the returned scsi_device. If @sdev is not NULL use the +* specified @sdev as a starting point after which to start the lookup. * * Note: The only reason why drivers should use this is because * they need to access the device list in irq context. Otherwise you * really want to use scsi_device_lookup_by_target instead. **/ struct scsi_device *__scsi_device_lookup_by_target (struct scsi_target *starget, +struct scsi_device *sdev,uint lun) { -struct scsi_device *sdev; +if (!sdev) + sdev =list_prepare_entry (sdev, &starget->devices, + same_target_siblings); -list_for_each_entry (sdev, &starget->devices, same_target_siblings) { +list_for_each_entry_continue (sdev, &starget->devices, + same_target_siblings) {if (sdev->lun ==lun)return sdev; }
@@ -1131,14 +1137,15 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);struct scsi_device *scsi_device_lookup_by_target (struct scsi_target *starget,uint lun) { -struct scsi_device *sdev; +struct scsi_device *sdev =NULL ;struct Scsi_Host *shost =dev_to_shost (starget->dev.parent);unsigned long flags;spin_lock_irqsave (shost->host_lock, flags); - sdev =__scsi_device_lookup_by_target (starget, lun); + restart: + sdev =__scsi_device_lookup_by_target (starget, sdev, lun);if (sdev &&scsi_device_get (sdev)) - sdev =NULL ; + goto restart;spin_unlock_irqrestore (shost->host_lock, flags);return sdev;
include/scsi/scsi_device.h +1 -0
@@ -285,6 +285,7 @@ extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *,extern struct scsi_device *scsi_device_lookup_by_target (struct scsi_target *,uint );extern struct scsi_device *__scsi_device_lookup_by_target (struct scsi_target *, +struct scsi_device *,uint );extern void starget_for_each_device (struct scsi_target *,void *,void (*fn)(struct scsi_device *,void *));
Quoted 9 lines · Hannes Reinecke
drivers/scsi/scsi.c +1
-1@@ -1131,14 +1137,15 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);struct scsi_device *scsi_device_lookup_by_target (struct scsi_target *starget,uint lun) { -struct scsi_device *sdev; +struct scsi_device *sdev =NULL ;struct Scsi_Host *shost =dev_to_shost (starget->dev.parent);unsigned long flags;
drivers/scsi/scsi.c EXPORT_SYMBOL(__scsi_device_lookup_by_target); · line 1151
Quoted 10 lines
drivers/scsi/scsi.c +3
-2spin_lock_irqsave (shost->host_lock, flags); - sdev =__scsi_device_lookup_by_target (starget, lun); + restart: + sdev =__scsi_device_lookup_by_target (starget, sdev, lun);if (sdev &&scsi_device_get (sdev)) - sdev =NULL ; + goto restart;spin_unlock_irqrestore (shost->host_lock, flags);return sdev;
Backwards jumps are generally disapproved of. How about:
spin_lock_irqsave(shost->host_lock, flags);
for (;;) {
sdev = __scsi_device_lookup_by_target(starget, sdev, lun);
if (!sdev || !scsi_device_get(sdev))
break;
}
spin_unlock_irqrestore(shost->host_lock, flags);Signature · 4 lines
Matthew Wilcox Intel Open Source Technology Centre "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Hi Matthew,
Quoted 23 lines · Matthew Wilcox
Quoted 9 lines · Hannes Reinecke
drivers/scsi/scsi.c +1
-1@@ -1131,14 +1137,15 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);struct scsi_device *scsi_device_lookup_by_target (struct scsi_target *starget,uint lun) { -struct scsi_device *sdev; +struct scsi_device *sdev =NULL ;struct Scsi_Host *shost =dev_to_shost (starget->dev.parent);unsigned long flags;drivers/scsi/scsi.c EXPORT_SYMBOL(__scsi_device_lookup_by_target); · line 1151
Quoted 10 lines
drivers/scsi/scsi.c +3
-2spin_lock_irqsave (shost->host_lock, flags); - sdev =__scsi_device_lookup_by_target (starget, lun); + restart: + sdev =__scsi_device_lookup_by_target (starget, sdev, lun);if (sdev &&scsi_device_get (sdev)) - sdev =NULL ; + goto restart;spin_unlock_irqrestore (shost->host_lock, flags);return sdev;Backwards jumps are generally disapproved of. How about:
Quoted 9 lines
spin_lock_irqsave(shost->host_lock, flags);
for (;;) {
sdev = __scsi_device_lookup_by_target(starget, sdev, lun);
if (!sdev || !scsi_device_get(sdev))
break;
}
spin_unlock_irqrestore(shost->host_lock, flags);I must say I don't really like the for(;;) construct. And it's really confusing as we want to find an sdev, so breaking if it's _not_ found is ... weird.
Cheers,
Hannes
Signature · 8 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Matthew,
Quoted 34 lines · Hannes Reinecke
Quoted 24 lines · Matthew Wilcox
Quoted 19 lines · Hannes Reinecke
drivers/scsi/scsi.c +4
-3@@ -1131,14 +1137,15 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);struct scsi_device *scsi_device_lookup_by_target (struct scsi_target *starget,uint lun) { -struct scsi_device *sdev; +struct scsi_device *sdev =NULL ;struct Scsi_Host *shost =dev_to_shost (starget->dev.parent);unsigned long flags;spin_lock_irqsave (shost->host_lock, flags); - sdev =__scsi_device_lookup_by_target (starget, lun); + restart: + sdev =__scsi_device_lookup_by_target (starget, sdev, lun);if (sdev &&scsi_device_get (sdev)) - sdev =NULL ; + goto restart;spin_unlock_irqrestore (shost->host_lock, flags);Quoted 1 line
return sdev;Backwards jumps are generally disapproved of. How about:
Quoted 9 lines
spin_lock_irqsave(shost->host_lock, flags); for (;;) { sdev = __scsi_device_lookup_by_target(starget, sdev, lun); if (!sdev || !scsi_device_get(sdev)) break; } spin_unlock_irqrestore(shost->host_lock, flags);
Quoted 4 lines
I must say I don't really like the for(;;) construct. And it's really confusing as we want to find an sdev, so breaking if it's _not_ found is ... weird.
What about
do {
sdev = __scsi_device_lookup_by_target(starget, sdev, lun);
} while (sdev && scsi_device_get(sdev));?
Should be identical and easier to read ...
Cheers,
Hannes
Signature · 8 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 11 lines · Hannes Reinecke
Quoted 9 lines · Matthew Wilcox
Backwards jumps are generally disapproved of. How about:
spin_lock_irqsave(shost->host_lock, flags); for (;;) { sdev = __scsi_device_lookup_by_target(starget, sdev, lun); if (!sdev || !scsi_device_get(sdev)) break; } spin_unlock_irqrestore(shost->host_lock, flags);
Quoted 1 line
I must say I don't really like the for(;;) construct.
I'd be fine with:
do {
sdev = __scsi_device_lookup_by_target(starget, sdev, lun);
} while (sdev && scsi_device_get(sdev));though I find that slightly less clear than the for (;;) construct.
Quoted 2 lines
And it's really confusing as we want to find an sdev, so breaking if it's _not_ found is ... weird.
Those are the two conditions when we want to stop trying -- if there's no device or if the device we've found is bad. I can definitely see an argument for splitting the two conditions to make that more obvious. I can also see an argument for not returning an sdev in the _DEL state from __scsi_device_lookup_by_target in the first place.
Signature · 4 lines
Matthew Wilcox Intel Open Source Technology Centre "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Hi Matthew,
Quoted 14 lines · Matthew Wilcox
Quoted 11 lines · Hannes Reinecke
Quoted 9 lines · Matthew Wilcox
Backwards jumps are generally disapproved of. How about:
spin_lock_irqsave(shost->host_lock, flags); for (;;) { sdev = __scsi_device_lookup_by_target(starget, sdev, lun); if (!sdev || !scsi_device_get(sdev)) break; } spin_unlock_irqrestore(shost->host_lock, flags);Quoted 1 line
I must say I don't really like the for(;;) construct.
Quoted 8 lines
I'd be fine with:
do { sdev = __scsi_device_lookup_by_target(starget, sdev, lun); } while (sdev && scsi_device_get(sdev));though I find that slightly less clear than the for (;;) construct.
Opinons differ.
Something for the maintainer to decide. James?
Quoted 9 lines
Quoted 2 lines
And it's really confusing as we want to find an sdev, so breaking if it's _not_ found is ... weird.
Those are the two conditions when we want to stop trying -- if there's no device or if the device we've found is bad. I can definitely see an argument for splitting the two conditions to make that more obvious. I can also see an argument for not returning an sdev in the
_DELstate from__scsi_device_lookup_by_targetin the first place.
Not sure if that would work with esp_scsi.c (the other user of __scsi_device_lookup_by_target()).
And don't have a device to try it with.
But modifying __scsi_device_lookup_by_target() would alter the behaviour, whereas this patch does not.
Cheers,
Hannes
Signature · 8 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
James Bottomley
maintainerQuoted 52 lines · Hannes Reinecke
Hi all,
currently we have this:
struct
scsi_device*scsi_device_lookup_by_target(structscsi_target*starget,
uint lun){ struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); sdev = __scsi_device_lookup_by_target(starget, lun); if (sdev && scsi_device_get(sdev)) sdev = NULL; spin_unlock_irqrestore(shost->host_lock, flags); return sdev; }now consider an sdev list with two entries for LUN 0, the first being in
SDEV_DELand the second being inSDEV_RUNNING.
scsi_device_lookup_by_targetwill always return NULL here, as it will never be able to skip past the first (unuseable) entry. Soscsi_report_lun_scanwill happily create duplicate sdevs for LUN 0 and we'll be getting the infamoussysfs: duplicate filename 'xxxx' can not be created
errors.
What we should be doing here is to restart
__scsi_device_lookup_by_target()if we find an
sdev but cannot use it (ie is inSDEV_DEL).James, please apply.
Cheers,
Hannes
plain text document attachment (scsi-restart-lookup-by-target) Restartscsi_device_lookup_by_target()When
scsi_device_lookup_by_target()will always return the first sdev with a matching LUN, regardless of the state. However, when this sdev is inSDEV_DELit's quite possible to have additional sdevs in the list with the same LUN which are active.
So we should restart__scsi_device_lookup_by_target()whenever we find an sdev with stateSDEV_DEL.
Quoted 1 line
Signed-off-by Hannes Reinecke <[email protected]>
That's brilliant, thanks for finding this bug.
rather than go to the trouble of restarting the scan looking for duplicates, since none of the users of __scsi_device_lookup_by_target actually want invisible dying devices in SDEV_DEL, why not just make it skip over them. If we ignore devices in SDEV_DEL, the target id of the visible devices should all be unique, so alter the
if (sdev->lun == lun)
to
if (sdev->lun == lun && sdev->sdev_state != SDEV_DEL)
And everything should work.
I think this is the correct fix because scsi_device_get() is also programmed to ignore devices in SDEV_DEL (for the same reason).
James
Hi James,
Quoted 65 lines · James Bottomley
Quoted 52 lines · Hannes Reinecke
Hi all,
currently we have this:
struct
scsi_device*scsi_device_lookup_by_target(structscsi_target*starget,
uint lun){ struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); sdev = __scsi_device_lookup_by_target(starget, lun); if (sdev && scsi_device_get(sdev)) sdev = NULL; spin_unlock_irqrestore(shost->host_lock, flags); return sdev; }now consider an sdev list with two entries for LUN 0, the first being in
SDEV_DELand the second being inSDEV_RUNNING.
scsi_device_lookup_by_targetwill always return NULL here, as it will never be able to skip past the first (unuseable) entry. Soscsi_report_lun_scanwill happily create duplicate sdevs for LUN 0 and we'll be getting the infamoussysfs: duplicate filename 'xxxx' can not be created
errors.
What we should be doing here is to restart
__scsi_device_lookup_by_target()if we find an
sdev but cannot use it (ie is inSDEV_DEL).James, please apply.
Cheers,
Hannes
plain text document attachment (scsi-restart-lookup-by-target) Restartscsi_device_lookup_by_target()When
scsi_device_lookup_by_target()will always return the first sdev with a matching LUN, regardless of the state. However, when this sdev is inSDEV_DELit's quite possible to have additional sdevs in the list with the same LUN which are active.
So we should restart__scsi_device_lookup_by_target()whenever we find an sdev with stateSDEV_DEL.Quoted 1 line
Signed-off-by Hannes Reinecke <[email protected]>That's brilliant, thanks for finding this bug.
rather than go to the trouble of restarting the scan looking for duplicates, since none of the users of
__scsi_device_lookup_by_targetactually want invisible dying devices inSDEV_DEL, why not just make it skip over them. If we ignore devices inSDEV_DEL, the target id of the visible devices should all be unique, so alter theif (
sdev->lun== lun)
Quoted 9 lines
to
if (
sdev->lun== lun &&sdev->sdev_state!=SDEV_DEL)And everything should work.
I think this is the correct fix because
scsi_device_get()is also programmed to ignore devices inSDEV_DEL(for the same reason).
As already mentioned, I'm not sure if esp_scsi.c expects it to be that way. If someone confirms that we won't break it with this change, then of course it's the easier way.
Cheers,
Hannes
Signature · 8 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
James Bottomley
maintainerQuoted 72 lines · Hannes Reinecke
Hi James,
Quoted 69 lines · James Bottomley
Quoted 52 lines · Hannes Reinecke
Hi all,
currently we have this:
struct
scsi_device*scsi_device_lookup_by_target(structscsi_target*starget,
uint lun){ struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); sdev = __scsi_device_lookup_by_target(starget, lun); if (sdev && scsi_device_get(sdev)) sdev = NULL; spin_unlock_irqrestore(shost->host_lock, flags); return sdev; }now consider an sdev list with two entries for LUN 0, the first being in
SDEV_DELand the second being inSDEV_RUNNING.
scsi_device_lookup_by_targetwill always return NULL here, as it will never be able to skip past the first (unuseable) entry. Soscsi_report_lun_scanwill happily create duplicate sdevs for LUN 0 and we'll be getting the infamoussysfs: duplicate filename 'xxxx' can not be created
errors.
What we should be doing here is to restart
__scsi_device_lookup_by_target()if we find an
sdev but cannot use it (ie is inSDEV_DEL).James, please apply.
Cheers,
Hannes
plain text document attachment (scsi-restart-lookup-by-target) Restartscsi_device_lookup_by_target()When
scsi_device_lookup_by_target()will always return the first sdev with a matching LUN, regardless of the state. However, when this sdev is inSDEV_DELit's quite possible to have additional sdevs in the list with the same LUN which are active.
So we should restart__scsi_device_lookup_by_target()whenever we find an sdev with stateSDEV_DEL.Quoted 1 line
Signed-off-by Hannes Reinecke <[email protected]>That's brilliant, thanks for finding this bug.
rather than go to the trouble of restarting the scan looking for duplicates, since none of the users of
__scsi_device_lookup_by_targetactually want invisible dying devices inSDEV_DEL, why not just make it skip over them. If we ignore devices inSDEV_DEL, the target id of the visible devices should all be unique, so alter theif (
sdev->lun== lun)to
if (
sdev->lun== lun &&sdev->sdev_state!=SDEV_DEL)
Quoted 8 lines
Quoted 5 lines
And everything should work.
I think this is the correct fix because
scsi_device_get()is also programmed to ignore devices inSDEV_DEL(for the same reason).As already mentioned, I'm not sure if
esp_scsi.cexpects it to be that way. If someone confirms that we won't break it with this change, then of course it's the easier way.
I did check esp ... it's looking for the hostdata in the device, so returning a SDEV_DEL device to it while another is in the list will really confuse it, because it will have a stale (and possibly freed) hostdata structure, so checking for SDEV_DEL will correct a potential bug in it as well ... that's why I was suggesting this way. All of the users are expecting live and visible devices.
James