Skip to content

Commit

Permalink
[SCSI] scsi_transport_fc: Fix synchronization issue while deleting vport
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Gal Rosen authored and James Bottomley committed Mar 8, 2010
1 parent d55f88f commit 0d9dc7c
Showing 1 changed file with 12 additions and 12 deletions.
24 changes: 12 additions & 12 deletions drivers/scsi/scsi_transport_fc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,15 @@ store_fc_vport_delete(struct device *dev, struct device_attribute *attr,
{
struct fc_vport *vport = transport_class_to_vport(dev);
struct Scsi_Host *shost = vport_to_shost(vport);
unsigned long flags;

spin_lock_irqsave(shost->host_lock, flags);
if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
spin_unlock_irqrestore(shost->host_lock, flags);
return -EBUSY;
}
vport->flags |= FC_VPORT_DELETING;
spin_unlock_irqrestore(shost->host_lock, flags);

fc_queue_work(shost, &vport->vport_delete_work);
return count;
Expand Down Expand Up @@ -1821,6 +1830,9 @@ store_fc_host_vport_delete(struct device *dev, struct device_attribute *attr,
list_for_each_entry(vport, &fc_host->vports, peers) {
if ((vport->channel == 0) &&
(vport->port_name == wwpn) && (vport->node_name == wwnn)) {
if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
break;
vport->flags |= FC_VPORT_DELETING;
match = 1;
break;
}
Expand Down Expand Up @@ -3370,18 +3382,6 @@ fc_vport_terminate(struct fc_vport *vport)
unsigned long flags;
int stat;

spin_lock_irqsave(shost->host_lock, flags);
if (vport->flags & FC_VPORT_CREATING) {
spin_unlock_irqrestore(shost->host_lock, flags);
return -EBUSY;
}
if (vport->flags & (FC_VPORT_DEL)) {
spin_unlock_irqrestore(shost->host_lock, flags);
return -EALREADY;
}
vport->flags |= FC_VPORT_DELETING;
spin_unlock_irqrestore(shost->host_lock, flags);

if (i->f->vport_delete)
stat = i->f->vport_delete(vport);
else
Expand Down

0 comments on commit 0d9dc7c

Please sign in to comment.