aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorGal Rosen <galr@storwize.com>2010-01-21 03:15:32 -0500
committerJames Bottomley <James.Bottomley@suse.de>2010-03-08 12:06:53 -0500
commit0d9dc7c8b9b7fa0f53647423b41056ee1beed735 (patch)
tree19cab2c6559f6cc5a31abfcf4fc1e33b0528ebc7 /drivers/scsi
parentd55f88f0275e4b21435957d3d354a79bb9edeec7 (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.c24
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