[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>
This commit is contained in:
parent
d55f88f027
commit
0d9dc7c8b9
@ -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 fc_vport *vport = transport_class_to_vport(dev);
|
||||||
struct Scsi_Host *shost = vport_to_shost(vport);
|
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);
|
fc_queue_work(shost, &vport->vport_delete_work);
|
||||||
return count;
|
return count;
|
||||||
@ -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) {
|
list_for_each_entry(vport, &fc_host->vports, peers) {
|
||||||
if ((vport->channel == 0) &&
|
if ((vport->channel == 0) &&
|
||||||
(vport->port_name == wwpn) && (vport->node_name == wwnn)) {
|
(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;
|
match = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3370,18 +3382,6 @@ fc_vport_terminate(struct fc_vport *vport)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int stat;
|
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)
|
if (i->f->vport_delete)
|
||||||
stat = i->f->vport_delete(vport);
|
stat = i->f->vport_delete(vport);
|
||||||
else
|
else
|
||||||
|
Loading…
x
Reference in New Issue
Block a user