[SCSI] zfcp: restore refcount check on port_remove
authorSteffen Maier <maier@linux.vnet.ibm.com>
Tue, 4 Sep 2012 13:23:34 +0000 (15:23 +0200)
committerJames Bottomley <JBottomley@Parallels.com>
Mon, 24 Sep 2012 08:11:02 +0000 (12:11 +0400)
Upstream commit f3450c7b917201bb49d67032e9f60d5125675d6a
"[SCSI] zfcp: Replace local reference counting with common kref"
accidentally dropped a reference count check before tearing down
zfcp_ports that are potentially in use by zfcp_units.
Even remote ports in use can be removed causing
unreachable garbage objects zfcp_ports with zfcp_units.
Thus units won't come back even after a manual port_rescan.
The kref of zfcp_port->dev.kobj is already used by the driver core.
We cannot re-use it to track the number of zfcp_units.
Re-introduce our own counter for units per port
and check on port_remove.

Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com>
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: <stable@vger.kernel.org> #2.6.33+
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/s390/scsi/zfcp_aux.c
drivers/s390/scsi/zfcp_def.h
drivers/s390/scsi/zfcp_ext.h
drivers/s390/scsi/zfcp_sysfs.c
drivers/s390/scsi/zfcp_unit.c

index aff8621de8069b813014a550c468a8bf8d78937b..f6adde44f226e1352c327a3b27696f09f7a7b501 100644 (file)
@@ -519,6 +519,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
 
        rwlock_init(&port->unit_list_lock);
        INIT_LIST_HEAD(&port->unit_list);
+       atomic_set(&port->units, 0);
 
        INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
        INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
index 5bc4afba8b09760d69e9269d0c3a044a59b6fdf5..1305955cbf5978399eb3bf4bd694c8cc3ae4465c 100644 (file)
@@ -205,6 +205,7 @@ struct zfcp_port {
        struct zfcp_adapter    *adapter;       /* adapter used to access port */
        struct list_head        unit_list;      /* head of logical unit list */
        rwlock_t                unit_list_lock; /* unit list lock */
+       atomic_t                units;         /* zfcp_unit count */
        atomic_t               status;         /* status of this remote port */
        u64                    wwnn;           /* WWNN if known */
        u64                    wwpn;           /* WWPN */
index 4eeef78447f25f7430ca948693278a44d6059c90..03441a7fb463c6fc062da5a20ba0abc4d174d893 100644 (file)
@@ -159,6 +159,7 @@ extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int);
 extern struct attribute_group zfcp_sysfs_unit_attrs;
 extern struct attribute_group zfcp_sysfs_adapter_attrs;
 extern struct attribute_group zfcp_sysfs_port_attrs;
+extern struct mutex zfcp_sysfs_port_units_mutex;
 extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
 extern struct device_attribute *zfcp_sysfs_shost_attrs[];
 
index c66af27b230bb99fdde7cd3f28bc45f6eac82ba3..1e0eb089dfbafd620e430c2357497284acf5349a 100644 (file)
@@ -227,6 +227,8 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
 static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
                     zfcp_sysfs_port_rescan_store);
 
+DEFINE_MUTEX(zfcp_sysfs_port_units_mutex);
+
 static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
                                            struct device_attribute *attr,
                                            const char *buf, size_t count)
@@ -249,6 +251,16 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
        else
                retval = 0;
 
+       mutex_lock(&zfcp_sysfs_port_units_mutex);
+       if (atomic_read(&port->units) > 0) {
+               retval = -EBUSY;
+               mutex_unlock(&zfcp_sysfs_port_units_mutex);
+               goto out;
+       }
+       /* port is about to be removed, so no more unit_add */
+       atomic_set(&port->units, -1);
+       mutex_unlock(&zfcp_sysfs_port_units_mutex);
+
        write_lock_irq(&adapter->port_list_lock);
        list_del(&port->list);
        write_unlock_irq(&adapter->port_list_lock);
@@ -289,12 +301,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
 {
        struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
        u64 fcp_lun;
+       int retval;
 
        if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
                return -EINVAL;
 
-       if (zfcp_unit_add(port, fcp_lun))
-               return -EINVAL;
+       retval = zfcp_unit_add(port, fcp_lun);
+       if (retval)
+               return retval;
 
        return count;
 }
index 3f2bff0d3aa21fd9996cedc187c441bff167718e..1cd2b99ab256c06132dd85c0a7a3d4293e98cb87 100644 (file)
@@ -104,7 +104,7 @@ static void zfcp_unit_release(struct device *dev)
 {
        struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
 
-       put_device(&unit->port->dev);
+       atomic_dec(&unit->port->units);
        kfree(unit);
 }
 
@@ -119,16 +119,27 @@ static void zfcp_unit_release(struct device *dev)
 int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
 {
        struct zfcp_unit *unit;
+       int retval = 0;
+
+       mutex_lock(&zfcp_sysfs_port_units_mutex);
+       if (atomic_read(&port->units) == -1) {
+               /* port is already gone */
+               retval = -ENODEV;
+               goto out;
+       }
 
        unit = zfcp_unit_find(port, fcp_lun);
        if (unit) {
                put_device(&unit->dev);
-               return -EEXIST;
+               retval = -EEXIST;
+               goto out;
        }
 
        unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
-       if (!unit)
-               return -ENOMEM;
+       if (!unit) {
+               retval = -ENOMEM;
+               goto out;
+       }
 
        unit->port = port;
        unit->fcp_lun = fcp_lun;
@@ -139,28 +150,33 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
        if (dev_set_name(&unit->dev, "0x%016llx",
                         (unsigned long long) fcp_lun)) {
                kfree(unit);
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto out;
        }
 
-       get_device(&port->dev);
-
        if (device_register(&unit->dev)) {
                put_device(&unit->dev);
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto out;
        }
 
        if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) {
                device_unregister(&unit->dev);
-               return -EINVAL;
+               retval = -EINVAL;
+               goto out;
        }
 
+       atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */
+
        write_lock_irq(&port->unit_list_lock);
        list_add_tail(&unit->list, &port->unit_list);
        write_unlock_irq(&port->unit_list_lock);
 
        zfcp_unit_scsi_scan(unit);
 
-       return 0;
+out:
+       mutex_unlock(&zfcp_sysfs_port_units_mutex);
+       return retval;
 }
 
 /**