diff options
author | Gal Rosen <galr@storwize.com> | 2010-01-21 03:15:32 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-03-08 12:06:53 -0500 |
commit | 0d9dc7c8b9b7fa0f53647423b41056ee1beed735 (patch) | |
tree | 19cab2c6559f6cc5a31abfcf4fc1e33b0528ebc7 /drivers/scsi | |
parent | d55f88f0275e4b21435957d3d354a79bb9edeec7 (diff) |
[SCSI] scsi_transport_fc: Fix synchronization issue while deleting vport
The issue occur while deleting 60 virtual ports through the sys
interface /sys/class/fc_vports/vport-X/vport_delete. It happen while in
a mistake each request sent twice for the same vport. This interface is
asynchronous, entering the delete request into a work queue, allowing
more than one request to enter to the delete work queue. The result is a
NULL pointer. The first request already delete the vport, while the
second request got a pointer to the vport before the device destroyed.
Re-create vport later cause system freeze.
Solution: Check vport flags before entering the request to the work queue.
[jejb: fixed int<->long problem on spinlock flags variable]
Signed-off-by: Gal Rosen <galr@storwize.com>
Acked-by: James Smart <james.smart@emulex.com>
Cc: Stable Tree <stable@kernel.org>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 24 |
1 files changed, 12 insertions, 12 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 79660ee3e211..1d5b72173dd8 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
@@ -1232,6 +1232,15 @@ store_fc_vport_delete(struct device *dev, struct device_attribute *attr, | |||
1232 | { | 1232 | { |
1233 | struct fc_vport *vport = transport_class_to_vport(dev); | 1233 | struct fc_vport *vport = transport_class_to_vport(dev); |
1234 | struct Scsi_Host *shost = vport_to_shost(vport); | 1234 | struct Scsi_Host *shost = vport_to_shost(vport); |
1235 | unsigned long flags; | ||
1236 | |||
1237 | spin_lock_irqsave(shost->host_lock, flags); | ||
1238 | if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) { | ||
1239 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1240 | return -EBUSY; | ||
1241 | } | ||
1242 | vport->flags |= FC_VPORT_DELETING; | ||
1243 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
1235 | 1244 | ||
1236 | fc_queue_work(shost, &vport->vport_delete_work); | 1245 | fc_queue_work(shost, &vport->vport_delete_work); |
1237 | return count; | 1246 | return count; |
@@ -1821,6 +1830,9 @@ store_fc_host_vport_delete(struct device *dev, struct device_attribute *attr, | |||
1821 | list_for_each_entry(vport, &fc_host->vports, peers) { | 1830 | list_for_each_entry(vport, &fc_host->vports, peers) { |
1822 | if ((vport->channel == 0) && | 1831 | if ((vport->channel == 0) && |
1823 | (vport->port_name == wwpn) && (vport->node_name == wwnn)) { | 1832 | (vport->port_name == wwpn) && (vport->node_name == wwnn)) { |
1833 | if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) | ||
1834 | break; | ||
1835 | vport->flags |= FC_VPORT_DELETING; | ||
1824 | match = 1; | 1836 | match = 1; |
1825 | break; | 1837 | break; |
1826 | } | 1838 | } |
@@ -3370,18 +3382,6 @@ fc_vport_terminate(struct fc_vport *vport) | |||
3370 | unsigned long flags; | 3382 | unsigned long flags; |
3371 | int stat; | 3383 | int stat; |
3372 | 3384 | ||
3373 | spin_lock_irqsave(shost->host_lock, flags); | ||
3374 | if (vport->flags & FC_VPORT_CREATING) { | ||
3375 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
3376 | return -EBUSY; | ||
3377 | } | ||
3378 | if (vport->flags & (FC_VPORT_DEL)) { | ||
3379 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
3380 | return -EALREADY; | ||
3381 | } | ||
3382 | vport->flags |= FC_VPORT_DELETING; | ||
3383 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
3384 | |||
3385 | if (i->f->vport_delete) | 3385 | if (i->f->vport_delete) |
3386 | stat = i->f->vport_delete(vport); | 3386 | stat = i->f->vport_delete(vport); |
3387 | else | 3387 | else |