Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d9dc7c8 authored by Gal Rosen's avatar Gal Rosen Committed by James Bottomley
Browse files

[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: default avatarGal Rosen <galr@storwize.com>
Acked-by: default avatarJames Smart <james.smart@emulex.com>
Cc: Stable Tree <stable@kernel.org>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent d55f88f0
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -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;
@@ -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;
		}
@@ -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