mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 09:13:55 +08:00
[SCSI] zfcp: restore refcount check on port_remove
Upstream commit f3450c7b91
"[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>
This commit is contained in:
parent
ca579c9f13
commit
d99b601b63
@ -519,6 +519,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
|
|||||||
|
|
||||||
rwlock_init(&port->unit_list_lock);
|
rwlock_init(&port->unit_list_lock);
|
||||||
INIT_LIST_HEAD(&port->unit_list);
|
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->gid_pn_work, zfcp_fc_port_did_lookup);
|
||||||
INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
|
INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
|
||||||
|
@ -205,6 +205,7 @@ struct zfcp_port {
|
|||||||
struct zfcp_adapter *adapter; /* adapter used to access port */
|
struct zfcp_adapter *adapter; /* adapter used to access port */
|
||||||
struct list_head unit_list; /* head of logical unit list */
|
struct list_head unit_list; /* head of logical unit list */
|
||||||
rwlock_t unit_list_lock; /* unit list lock */
|
rwlock_t unit_list_lock; /* unit list lock */
|
||||||
|
atomic_t units; /* zfcp_unit count */
|
||||||
atomic_t status; /* status of this remote port */
|
atomic_t status; /* status of this remote port */
|
||||||
u64 wwnn; /* WWNN if known */
|
u64 wwnn; /* WWNN if known */
|
||||||
u64 wwpn; /* WWPN */
|
u64 wwpn; /* WWPN */
|
||||||
|
@ -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_unit_attrs;
|
||||||
extern struct attribute_group zfcp_sysfs_adapter_attrs;
|
extern struct attribute_group zfcp_sysfs_adapter_attrs;
|
||||||
extern struct attribute_group zfcp_sysfs_port_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_sdev_attrs[];
|
||||||
extern struct device_attribute *zfcp_sysfs_shost_attrs[];
|
extern struct device_attribute *zfcp_sysfs_shost_attrs[];
|
||||||
|
|
||||||
|
@ -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,
|
static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
|
||||||
zfcp_sysfs_port_rescan_store);
|
zfcp_sysfs_port_rescan_store);
|
||||||
|
|
||||||
|
DEFINE_MUTEX(zfcp_sysfs_port_units_mutex);
|
||||||
|
|
||||||
static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
|
static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
@ -249,6 +251,16 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
|
|||||||
else
|
else
|
||||||
retval = 0;
|
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);
|
write_lock_irq(&adapter->port_list_lock);
|
||||||
list_del(&port->list);
|
list_del(&port->list);
|
||||||
write_unlock_irq(&adapter->port_list_lock);
|
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);
|
struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
|
||||||
u64 fcp_lun;
|
u64 fcp_lun;
|
||||||
|
int retval;
|
||||||
|
|
||||||
if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
|
if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (zfcp_unit_add(port, fcp_lun))
|
retval = zfcp_unit_add(port, fcp_lun);
|
||||||
return -EINVAL;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ static void zfcp_unit_release(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
|
struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
|
||||||
|
|
||||||
put_device(&unit->port->dev);
|
atomic_dec(&unit->port->units);
|
||||||
kfree(unit);
|
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)
|
int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
|
||||||
{
|
{
|
||||||
struct zfcp_unit *unit;
|
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);
|
unit = zfcp_unit_find(port, fcp_lun);
|
||||||
if (unit) {
|
if (unit) {
|
||||||
put_device(&unit->dev);
|
put_device(&unit->dev);
|
||||||
return -EEXIST;
|
retval = -EEXIST;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
|
unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
|
||||||
if (!unit)
|
if (!unit) {
|
||||||
return -ENOMEM;
|
retval = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
unit->port = port;
|
unit->port = port;
|
||||||
unit->fcp_lun = fcp_lun;
|
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",
|
if (dev_set_name(&unit->dev, "0x%016llx",
|
||||||
(unsigned long long) fcp_lun)) {
|
(unsigned long long) fcp_lun)) {
|
||||||
kfree(unit);
|
kfree(unit);
|
||||||
return -ENOMEM;
|
retval = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_device(&port->dev);
|
|
||||||
|
|
||||||
if (device_register(&unit->dev)) {
|
if (device_register(&unit->dev)) {
|
||||||
put_device(&unit->dev);
|
put_device(&unit->dev);
|
||||||
return -ENOMEM;
|
retval = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) {
|
if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) {
|
||||||
device_unregister(&unit->dev);
|
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);
|
write_lock_irq(&port->unit_list_lock);
|
||||||
list_add_tail(&unit->list, &port->unit_list);
|
list_add_tail(&unit->list, &port->unit_list);
|
||||||
write_unlock_irq(&port->unit_list_lock);
|
write_unlock_irq(&port->unit_list_lock);
|
||||||
|
|
||||||
zfcp_unit_scsi_scan(unit);
|
zfcp_unit_scsi_scan(unit);
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
mutex_unlock(&zfcp_sysfs_port_units_mutex);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user