aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Maier <maier@linux.ibm.com>2019-05-23 09:23:46 -0400
committerMartin K. Petersen <martin.petersen@oracle.com>2019-05-29 21:52:31 -0400
commitef4021fe5fd77ced0323cede27979d80a56211ca (patch)
tree61292a20c28a9c06eb79ee7272163277cfb6b7f9
parentd27e5e07f9c49bf2a6a4ef254ce531c1b4fb5a38 (diff)
scsi: zfcp: fix to prevent port_remove with pure auto scan LUNs (only sdevs)
When the user tries to remove a zfcp port via sysfs, we only rejected it if there are zfcp unit children under the port. With purely automatically scanned LUNs there are no zfcp units but only SCSI devices. In such cases, the port_remove erroneously continued. We close the port and this implicitly closes all LUNs under the port. The SCSI devices survive with their private zfcp_scsi_dev still holding a reference to the "removed" zfcp_port (still allocated but invisible in sysfs) [zfcp_get_port_by_wwpn in zfcp_scsi_slave_alloc]. This is not a problem as long as the fc_rport stays blocked. Once (auto) port scan brings back the removed port, we unblock its fc_rport again by design. However, there is no mechanism that would recover (open) the LUNs under the port (no "ersfs_3" without zfcp_unit [zfcp_erp_strategy_followup_success]). Any pending or new I/O to such LUN leads to repeated: Done: NEEDS_RETRY Result: hostbyte=DID_IMM_RETRY driverbyte=DRIVER_OK See also v4.10 commit 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery"). Even a manual LUN recovery (echo 0 > /sys/bus/scsi/devices/H:C:T:L/zfcp_failed) does not help, as the LUN links to the old "removed" port which remains to lack ZFCP_STATUS_COMMON_RUNNING [zfcp_erp_required_act]. The only workaround is to first ensure that the fc_rport is blocked (e.g. port_remove again in case it was re-discovered by (auto) port scan), then delete the SCSI devices, and finally re-discover by (auto) port scan. The port scan includes an fc_rport unblock, which in turn triggers a new scan on the scsi target to freshly get new pure auto scan LUNs. Fix this by rejecting port_remove also if there are SCSI devices (even without any zfcp_unit) under this port. Re-use mechanics from v3.7 commit d99b601b6338 ("[SCSI] zfcp: restore refcount check on port_remove"). However, we have to give up zfcp_sysfs_port_units_mutex earlier in unit_add to prevent a deadlock with scsi_host scan taking shost->scan_mutex first and then zfcp_sysfs_port_units_mutex now in our zfcp_scsi_slave_alloc(). Signed-off-by: Steffen Maier <maier@linux.ibm.com> Fixes: b62a8d9b45b9 ("[SCSI] zfcp: Use SCSI device data zfcp scsi dev instead of zfcp unit") Fixes: f8210e34887e ("[SCSI] zfcp: Allow midlayer to scan for LUNs when running in NPIV mode") Cc: <stable@vger.kernel.org> #2.6.37+ Reviewed-by: Benjamin Block <bblock@linux.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c9
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c54
-rw-r--r--drivers/s390/scsi/zfcp_unit.c8
4 files changed, 65 insertions, 7 deletions
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index c6acca521ffe..31e8a7240fd7 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -167,6 +167,7 @@ extern const struct attribute_group *zfcp_port_attr_groups[];
167extern struct mutex zfcp_sysfs_port_units_mutex; 167extern struct mutex zfcp_sysfs_port_units_mutex;
168extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; 168extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
169extern struct device_attribute *zfcp_sysfs_shost_attrs[]; 169extern struct device_attribute *zfcp_sysfs_shost_attrs[];
170bool zfcp_sysfs_port_is_removing(const struct zfcp_port *const port);
170 171
171/* zfcp_unit.c */ 172/* zfcp_unit.c */
172extern int zfcp_unit_add(struct zfcp_port *, u64); 173extern int zfcp_unit_add(struct zfcp_port *, u64);
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 221d0dfb8493..e9ded2befa0d 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -129,6 +129,15 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev)
129 129
130 zfcp_sdev->erp_action.port = port; 130 zfcp_sdev->erp_action.port = port;
131 131
132 mutex_lock(&zfcp_sysfs_port_units_mutex);
133 if (zfcp_sysfs_port_is_removing(port)) {
134 /* port is already gone */
135 mutex_unlock(&zfcp_sysfs_port_units_mutex);
136 put_device(&port->dev); /* undo zfcp_get_port_by_wwpn() */
137 return -ENXIO;
138 }
139 mutex_unlock(&zfcp_sysfs_port_units_mutex);
140
132 unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev)); 141 unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev));
133 if (unit) 142 if (unit)
134 put_device(&unit->dev); 143 put_device(&unit->dev);
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 2d78732b270b..af197e2b3e69 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -235,6 +235,53 @@ static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
235 235
236DEFINE_MUTEX(zfcp_sysfs_port_units_mutex); 236DEFINE_MUTEX(zfcp_sysfs_port_units_mutex);
237 237
238static void zfcp_sysfs_port_set_removing(struct zfcp_port *const port)
239{
240 lockdep_assert_held(&zfcp_sysfs_port_units_mutex);
241 atomic_set(&port->units, -1);
242}
243
244bool zfcp_sysfs_port_is_removing(const struct zfcp_port *const port)
245{
246 lockdep_assert_held(&zfcp_sysfs_port_units_mutex);
247 return atomic_read(&port->units) == -1;
248}
249
250static bool zfcp_sysfs_port_in_use(struct zfcp_port *const port)
251{
252 struct zfcp_adapter *const adapter = port->adapter;
253 unsigned long flags;
254 struct scsi_device *sdev;
255 bool in_use = true;
256
257 mutex_lock(&zfcp_sysfs_port_units_mutex);
258 if (atomic_read(&port->units) > 0)
259 goto unlock_port_units_mutex; /* zfcp_unit(s) under port */
260
261 spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
262 __shost_for_each_device(sdev, adapter->scsi_host) {
263 const struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
264
265 if (sdev->sdev_state == SDEV_DEL ||
266 sdev->sdev_state == SDEV_CANCEL)
267 continue;
268 if (zsdev->port != port)
269 continue;
270 /* alive scsi_device under port of interest */
271 goto unlock_host_lock;
272 }
273
274 /* port is about to be removed, so no more unit_add or slave_alloc */
275 zfcp_sysfs_port_set_removing(port);
276 in_use = false;
277
278unlock_host_lock:
279 spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
280unlock_port_units_mutex:
281 mutex_unlock(&zfcp_sysfs_port_units_mutex);
282 return in_use;
283}
284
238static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, 285static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
239 struct device_attribute *attr, 286 struct device_attribute *attr,
240 const char *buf, size_t count) 287 const char *buf, size_t count)
@@ -257,16 +304,11 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
257 else 304 else
258 retval = 0; 305 retval = 0;
259 306
260 mutex_lock(&zfcp_sysfs_port_units_mutex); 307 if (zfcp_sysfs_port_in_use(port)) {
261 if (atomic_read(&port->units) > 0) {
262 retval = -EBUSY; 308 retval = -EBUSY;
263 mutex_unlock(&zfcp_sysfs_port_units_mutex);
264 put_device(&port->dev); /* undo zfcp_get_port_by_wwpn() */ 309 put_device(&port->dev); /* undo zfcp_get_port_by_wwpn() */
265 goto out; 310 goto out;
266 } 311 }
267 /* port is about to be removed, so no more unit_add */
268 atomic_set(&port->units, -1);
269 mutex_unlock(&zfcp_sysfs_port_units_mutex);
270 312
271 write_lock_irq(&adapter->port_list_lock); 313 write_lock_irq(&adapter->port_list_lock);
272 list_del(&port->list); 314 list_del(&port->list);
diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c
index 1bf0a0984a09..e67bf7388cae 100644
--- a/drivers/s390/scsi/zfcp_unit.c
+++ b/drivers/s390/scsi/zfcp_unit.c
@@ -124,7 +124,7 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
124 int retval = 0; 124 int retval = 0;
125 125
126 mutex_lock(&zfcp_sysfs_port_units_mutex); 126 mutex_lock(&zfcp_sysfs_port_units_mutex);
127 if (atomic_read(&port->units) == -1) { 127 if (zfcp_sysfs_port_is_removing(port)) {
128 /* port is already gone */ 128 /* port is already gone */
129 retval = -ENODEV; 129 retval = -ENODEV;
130 goto out; 130 goto out;
@@ -168,8 +168,14 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
168 write_lock_irq(&port->unit_list_lock); 168 write_lock_irq(&port->unit_list_lock);
169 list_add_tail(&unit->list, &port->unit_list); 169 list_add_tail(&unit->list, &port->unit_list);
170 write_unlock_irq(&port->unit_list_lock); 170 write_unlock_irq(&port->unit_list_lock);
171 /*
172 * lock order: shost->scan_mutex before zfcp_sysfs_port_units_mutex
173 * due to zfcp_unit_scsi_scan() => zfcp_scsi_slave_alloc()
174 */
175 mutex_unlock(&zfcp_sysfs_port_units_mutex);
171 176
172 zfcp_unit_scsi_scan(unit); 177 zfcp_unit_scsi_scan(unit);
178 return retval;
173 179
174out: 180out:
175 mutex_unlock(&zfcp_sysfs_port_units_mutex); 181 mutex_unlock(&zfcp_sysfs_port_units_mutex);