Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SCSI] zfcp: restore refcount check on port_remove

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>

authored by

Steffen Maier and committed by
James Bottomley
d99b601b ca579c9f

+45 -12
+1
drivers/s390/scsi/zfcp_aux.c
··· 519 519 520 520 rwlock_init(&port->unit_list_lock); 521 521 INIT_LIST_HEAD(&port->unit_list); 522 + atomic_set(&port->units, 0); 522 523 523 524 INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); 524 525 INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
+1
drivers/s390/scsi/zfcp_def.h
··· 205 205 struct zfcp_adapter *adapter; /* adapter used to access port */ 206 206 struct list_head unit_list; /* head of logical unit list */ 207 207 rwlock_t unit_list_lock; /* unit list lock */ 208 + atomic_t units; /* zfcp_unit count */ 208 209 atomic_t status; /* status of this remote port */ 209 210 u64 wwnn; /* WWNN if known */ 210 211 u64 wwpn; /* WWPN */
+1
drivers/s390/scsi/zfcp_ext.h
··· 159 159 extern struct attribute_group zfcp_sysfs_unit_attrs; 160 160 extern struct attribute_group zfcp_sysfs_adapter_attrs; 161 161 extern struct attribute_group zfcp_sysfs_port_attrs; 162 + extern struct mutex zfcp_sysfs_port_units_mutex; 162 163 extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; 163 164 extern struct device_attribute *zfcp_sysfs_shost_attrs[]; 164 165
+16 -2
drivers/s390/scsi/zfcp_sysfs.c
··· 227 227 static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, 228 228 zfcp_sysfs_port_rescan_store); 229 229 230 + DEFINE_MUTEX(zfcp_sysfs_port_units_mutex); 231 + 230 232 static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, 231 233 struct device_attribute *attr, 232 234 const char *buf, size_t count) ··· 250 248 goto out; 251 249 else 252 250 retval = 0; 251 + 252 + mutex_lock(&zfcp_sysfs_port_units_mutex); 253 + if (atomic_read(&port->units) > 0) { 254 + retval = -EBUSY; 255 + mutex_unlock(&zfcp_sysfs_port_units_mutex); 256 + goto out; 257 + } 258 + /* port is about to be removed, so no more unit_add */ 259 + atomic_set(&port->units, -1); 260 + mutex_unlock(&zfcp_sysfs_port_units_mutex); 253 261 254 262 write_lock_irq(&adapter->port_list_lock); 255 263 list_del(&port->list); ··· 301 289 { 302 290 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); 303 291 u64 fcp_lun; 292 + int retval; 304 293 305 294 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) 306 295 return -EINVAL; 307 296 308 - if (zfcp_unit_add(port, fcp_lun)) 309 - return -EINVAL; 297 + retval = zfcp_unit_add(port, fcp_lun); 298 + if (retval) 299 + return retval; 310 300 311 301 return count; 312 302 }
+26 -10
drivers/s390/scsi/zfcp_unit.c
··· 104 104 { 105 105 struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); 106 106 107 - put_device(&unit->port->dev); 107 + atomic_dec(&unit->port->units); 108 108 kfree(unit); 109 109 } 110 110 ··· 119 119 int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) 120 120 { 121 121 struct zfcp_unit *unit; 122 + int retval = 0; 123 + 124 + mutex_lock(&zfcp_sysfs_port_units_mutex); 125 + if (atomic_read(&port->units) == -1) { 126 + /* port is already gone */ 127 + retval = -ENODEV; 128 + goto out; 129 + } 122 130 123 131 unit = zfcp_unit_find(port, fcp_lun); 124 132 if (unit) { 125 133 put_device(&unit->dev); 126 - return -EEXIST; 134 + retval = -EEXIST; 135 + goto out; 127 136 } 128 137 129 138 unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); 130 - if (!unit) 131 - return -ENOMEM; 139 + if (!unit) { 140 + retval = -ENOMEM; 141 + goto out; 142 + } 132 143 133 144 unit->port = port; 134 145 unit->fcp_lun = fcp_lun; ··· 150 139 if (dev_set_name(&unit->dev, "0x%016llx", 151 140 (unsigned long long) fcp_lun)) { 152 141 kfree(unit); 153 - return -ENOMEM; 142 + retval = -ENOMEM; 143 + goto out; 154 144 } 155 - 156 - get_device(&port->dev); 157 145 158 146 if (device_register(&unit->dev)) { 159 147 put_device(&unit->dev); 160 - return -ENOMEM; 148 + retval = -ENOMEM; 149 + goto out; 161 150 } 162 151 163 152 if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) { 164 153 device_unregister(&unit->dev); 165 - return -EINVAL; 154 + retval = -EINVAL; 155 + goto out; 166 156 } 157 + 158 + atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */ 167 159 168 160 write_lock_irq(&port->unit_list_lock); 169 161 list_add_tail(&unit->list, &port->unit_list); ··· 174 160 175 161 zfcp_unit_scsi_scan(unit); 176 162 177 - return 0; 163 + out: 164 + mutex_unlock(&zfcp_sysfs_port_units_mutex); 165 + return retval; 178 166 } 179 167 180 168 /**