diff options
author | Steffen Maier <maier@linux.vnet.ibm.com> | 2012-09-04 09:23:34 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-09-24 04:11:02 -0400 |
commit | d99b601b63386f3395dc26a699ae703a273d9982 (patch) | |
tree | 208966f18e89775d68e82092c1ff648206fbd688 /drivers/s390 | |
parent | ca579c9f136af4274ccfd1bcaee7f38a29a0e2e9 (diff) |
[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>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/scsi/zfcp_aux.c | 1 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_def.h | 1 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_ext.h | 1 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_sysfs.c | 18 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_unit.c | 36 |
5 files changed, 45 insertions, 12 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index aff8621de806..f6adde44f226 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c | |||
@@ -519,6 +519,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, | |||
519 | 519 | ||
520 | rwlock_init(&port->unit_list_lock); | 520 | rwlock_init(&port->unit_list_lock); |
521 | INIT_LIST_HEAD(&port->unit_list); | 521 | INIT_LIST_HEAD(&port->unit_list); |
522 | atomic_set(&port->units, 0); | ||
522 | 523 | ||
523 | INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); | 524 | INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); |
524 | INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); | 525 | INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); |
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 5bc4afba8b09..1305955cbf59 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h | |||
@@ -205,6 +205,7 @@ struct zfcp_port { | |||
205 | struct zfcp_adapter *adapter; /* adapter used to access port */ | 205 | struct zfcp_adapter *adapter; /* adapter used to access port */ |
206 | struct list_head unit_list; /* head of logical unit list */ | 206 | struct list_head unit_list; /* head of logical unit list */ |
207 | rwlock_t unit_list_lock; /* unit list lock */ | 207 | rwlock_t unit_list_lock; /* unit list lock */ |
208 | atomic_t units; /* zfcp_unit count */ | ||
208 | atomic_t status; /* status of this remote port */ | 209 | atomic_t status; /* status of this remote port */ |
209 | u64 wwnn; /* WWNN if known */ | 210 | u64 wwnn; /* WWNN if known */ |
210 | u64 wwpn; /* WWPN */ | 211 | u64 wwpn; /* WWPN */ |
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 4eeef78447f2..03441a7fb463 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h | |||
@@ -159,6 +159,7 @@ extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int); | |||
159 | extern struct attribute_group zfcp_sysfs_unit_attrs; | 159 | extern struct attribute_group zfcp_sysfs_unit_attrs; |
160 | extern struct attribute_group zfcp_sysfs_adapter_attrs; | 160 | extern struct attribute_group zfcp_sysfs_adapter_attrs; |
161 | extern struct attribute_group zfcp_sysfs_port_attrs; | 161 | extern struct attribute_group zfcp_sysfs_port_attrs; |
162 | extern struct mutex zfcp_sysfs_port_units_mutex; | ||
162 | extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; | 163 | extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; |
163 | extern struct device_attribute *zfcp_sysfs_shost_attrs[]; | 164 | extern struct device_attribute *zfcp_sysfs_shost_attrs[]; |
164 | 165 | ||
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index c66af27b230b..1e0eb089dfba 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c | |||
@@ -227,6 +227,8 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, | |||
227 | static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, | 227 | static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, |
228 | zfcp_sysfs_port_rescan_store); | 228 | zfcp_sysfs_port_rescan_store); |
229 | 229 | ||
230 | DEFINE_MUTEX(zfcp_sysfs_port_units_mutex); | ||
231 | |||
230 | static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, | 232 | static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, |
231 | struct device_attribute *attr, | 233 | struct device_attribute *attr, |
232 | const char *buf, size_t count) | 234 | const char *buf, size_t count) |
@@ -249,6 +251,16 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, | |||
249 | else | 251 | else |
250 | retval = 0; | 252 | retval = 0; |
251 | 253 | ||
254 | mutex_lock(&zfcp_sysfs_port_units_mutex); | ||
255 | if (atomic_read(&port->units) > 0) { | ||
256 | retval = -EBUSY; | ||
257 | mutex_unlock(&zfcp_sysfs_port_units_mutex); | ||
258 | goto out; | ||
259 | } | ||
260 | /* port is about to be removed, so no more unit_add */ | ||
261 | atomic_set(&port->units, -1); | ||
262 | mutex_unlock(&zfcp_sysfs_port_units_mutex); | ||
263 | |||
252 | write_lock_irq(&adapter->port_list_lock); | 264 | write_lock_irq(&adapter->port_list_lock); |
253 | list_del(&port->list); | 265 | list_del(&port->list); |
254 | write_unlock_irq(&adapter->port_list_lock); | 266 | write_unlock_irq(&adapter->port_list_lock); |
@@ -289,12 +301,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, | |||
289 | { | 301 | { |
290 | struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); | 302 | struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); |
291 | u64 fcp_lun; | 303 | u64 fcp_lun; |
304 | int retval; | ||
292 | 305 | ||
293 | if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) | 306 | if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) |
294 | return -EINVAL; | 307 | return -EINVAL; |
295 | 308 | ||
296 | if (zfcp_unit_add(port, fcp_lun)) | 309 | retval = zfcp_unit_add(port, fcp_lun); |
297 | return -EINVAL; | 310 | if (retval) |
311 | return retval; | ||
298 | 312 | ||
299 | return count; | 313 | return count; |
300 | } | 314 | } |
diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c index 3f2bff0d3aa2..1cd2b99ab256 100644 --- a/drivers/s390/scsi/zfcp_unit.c +++ b/drivers/s390/scsi/zfcp_unit.c | |||
@@ -104,7 +104,7 @@ static void zfcp_unit_release(struct device *dev) | |||
104 | { | 104 | { |
105 | struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); | 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 | kfree(unit); | 108 | kfree(unit); |
109 | } | 109 | } |
110 | 110 | ||
@@ -119,16 +119,27 @@ static void zfcp_unit_release(struct device *dev) | |||
119 | int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) | 119 | int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) |
120 | { | 120 | { |
121 | struct zfcp_unit *unit; | 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 | unit = zfcp_unit_find(port, fcp_lun); | 131 | unit = zfcp_unit_find(port, fcp_lun); |
124 | if (unit) { | 132 | if (unit) { |
125 | put_device(&unit->dev); | 133 | put_device(&unit->dev); |
126 | return -EEXIST; | 134 | retval = -EEXIST; |
135 | goto out; | ||
127 | } | 136 | } |
128 | 137 | ||
129 | unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); | 138 | unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); |
130 | if (!unit) | 139 | if (!unit) { |
131 | return -ENOMEM; | 140 | retval = -ENOMEM; |
141 | goto out; | ||
142 | } | ||
132 | 143 | ||
133 | unit->port = port; | 144 | unit->port = port; |
134 | unit->fcp_lun = fcp_lun; | 145 | unit->fcp_lun = fcp_lun; |
@@ -139,28 +150,33 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) | |||
139 | if (dev_set_name(&unit->dev, "0x%016llx", | 150 | if (dev_set_name(&unit->dev, "0x%016llx", |
140 | (unsigned long long) fcp_lun)) { | 151 | (unsigned long long) fcp_lun)) { |
141 | kfree(unit); | 152 | kfree(unit); |
142 | return -ENOMEM; | 153 | retval = -ENOMEM; |
154 | goto out; | ||
143 | } | 155 | } |
144 | 156 | ||
145 | get_device(&port->dev); | ||
146 | |||
147 | if (device_register(&unit->dev)) { | 157 | if (device_register(&unit->dev)) { |
148 | put_device(&unit->dev); | 158 | put_device(&unit->dev); |
149 | return -ENOMEM; | 159 | retval = -ENOMEM; |
160 | goto out; | ||
150 | } | 161 | } |
151 | 162 | ||
152 | if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) { | 163 | if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) { |
153 | device_unregister(&unit->dev); | 164 | device_unregister(&unit->dev); |
154 | return -EINVAL; | 165 | retval = -EINVAL; |
166 | goto out; | ||
155 | } | 167 | } |
156 | 168 | ||
169 | atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */ | ||
170 | |||
157 | write_lock_irq(&port->unit_list_lock); | 171 | write_lock_irq(&port->unit_list_lock); |
158 | list_add_tail(&unit->list, &port->unit_list); | 172 | list_add_tail(&unit->list, &port->unit_list); |
159 | write_unlock_irq(&port->unit_list_lock); | 173 | write_unlock_irq(&port->unit_list_lock); |
160 | 174 | ||
161 | zfcp_unit_scsi_scan(unit); | 175 | zfcp_unit_scsi_scan(unit); |
162 | 176 | ||
163 | return 0; | 177 | out: |
178 | mutex_unlock(&zfcp_sysfs_port_units_mutex); | ||
179 | return retval; | ||
164 | } | 180 | } |
165 | 181 | ||
166 | /** | 182 | /** |