aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorChristof Schmitt <christof.schmitt@de.ibm.com>2007-05-08 05:16:52 -0400
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2007-05-08 12:55:33 -0400
commit5f852be9e11d62223ea063f6ceed4f9677f54051 (patch)
tree55552b19035695b23329db2b5f03fc7592412b56 /drivers/s390
parent801e0ced1891a2b8cad1a435c45234a719b3b6bf (diff)
[SCSI] zfcp: Fix deadlock between zfcp ERP and SCSI
The SCSI stack requires low level drivers to register and unregister devices. For zfcp this leads to the situation where zfcp calls the SCSI stack, the SCSI tries to scan the new device and the scan SCSI command fails. This would require the zfcp erp, but the erp thread is already blocked in the register call. The fix is to make sure that the calls from the ERP thread to the SCSI stack do not block the ERP thread. In detail: 1) Use a workqueue to avoid blocking of the scsi_scan_target calls. 2) When removing a unit make sure that no scsi_scan_target call is pending. 3) Replace scsi_flush_work with scsi_target_unblock. This avoids blocking and has the same result. Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com> Signed-off-by: Swen Schillig <swen@vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/scsi/zfcp_aux.c2
-rw-r--r--drivers/s390/scsi/zfcp_def.h5
-rw-r--r--drivers/s390/scsi/zfcp_erp.c64
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c5
4 files changed, 72 insertions, 4 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index ec3f664f6c80..49d5fc729bef 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -913,6 +913,8 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
913 unit->sysfs_device.release = zfcp_sysfs_unit_release; 913 unit->sysfs_device.release = zfcp_sysfs_unit_release;
914 dev_set_drvdata(&unit->sysfs_device, unit); 914 dev_set_drvdata(&unit->sysfs_device, unit);
915 915
916 init_waitqueue_head(&unit->scsi_scan_wq);
917
916 /* mark unit unusable as long as sysfs registration is not complete */ 918 /* mark unit unusable as long as sysfs registration is not complete */
917 atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); 919 atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
918 920
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 32933ed54b8a..07b0957b82f3 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -637,6 +637,7 @@ do { \
637#define ZFCP_STATUS_UNIT_SHARED 0x00000004 637#define ZFCP_STATUS_UNIT_SHARED 0x00000004
638#define ZFCP_STATUS_UNIT_READONLY 0x00000008 638#define ZFCP_STATUS_UNIT_READONLY 0x00000008
639#define ZFCP_STATUS_UNIT_REGISTERED 0x00000010 639#define ZFCP_STATUS_UNIT_REGISTERED 0x00000010
640#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020
640 641
641/* FSF request status (this does not have a common part) */ 642/* FSF request status (this does not have a common part) */
642#define ZFCP_STATUS_FSFREQ_NOT_INIT 0x00000000 643#define ZFCP_STATUS_FSFREQ_NOT_INIT 0x00000000
@@ -980,6 +981,10 @@ struct zfcp_unit {
980 struct scsi_device *device; /* scsi device struct pointer */ 981 struct scsi_device *device; /* scsi device struct pointer */
981 struct zfcp_erp_action erp_action; /* pending error recovery */ 982 struct zfcp_erp_action erp_action; /* pending error recovery */
982 atomic_t erp_counter; 983 atomic_t erp_counter;
984 wait_queue_head_t scsi_scan_wq; /* can be used to wait until
985 all scsi_scan_target
986 requests have been
987 completed. */
983}; 988};
984 989
985/* FSF request */ 990/* FSF request */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index f326bbe49fa7..885572300589 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1591,6 +1591,62 @@ zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result)
1591 return result; 1591 return result;
1592} 1592}
1593 1593
1594struct zfcp_erp_add_work {
1595 struct zfcp_unit *unit;
1596 struct work_struct work;
1597};
1598
1599/**
1600 * zfcp_erp_scsi_scan
1601 * @data: pointer to a struct zfcp_erp_add_work
1602 *
1603 * Registers a logical unit with the SCSI stack.
1604 */
1605static void zfcp_erp_scsi_scan(struct work_struct *work)
1606{
1607 struct zfcp_erp_add_work *p =
1608 container_of(work, struct zfcp_erp_add_work, work);
1609 struct zfcp_unit *unit = p->unit;
1610 struct fc_rport *rport = unit->port->rport;
1611 scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
1612 unit->scsi_lun, 0);
1613 atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
1614 wake_up(&unit->scsi_scan_wq);
1615 zfcp_unit_put(unit);
1616 kfree(p);
1617}
1618
1619/**
1620 * zfcp_erp_schedule_work
1621 * @unit: pointer to unit which should be registered with SCSI stack
1622 *
1623 * Schedules work which registers a unit with the SCSI stack
1624 */
1625static void
1626zfcp_erp_schedule_work(struct zfcp_unit *unit)
1627{
1628 struct zfcp_erp_add_work *p;
1629
1630 p = kmalloc(sizeof(*p), GFP_KERNEL);
1631 if (!p) {
1632 ZFCP_LOG_NORMAL("error: Out of resources. Could not register "
1633 "the FCP-LUN 0x%Lx connected to "
1634 "the port with WWPN 0x%Lx connected to "
1635 "the adapter %s with the SCSI stack.\n",
1636 unit->fcp_lun,
1637 unit->port->wwpn,
1638 zfcp_get_busid_by_unit(unit));
1639 return;
1640 }
1641
1642 zfcp_unit_get(unit);
1643 memset(p, 0, sizeof(*p));
1644 atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
1645 INIT_WORK(&p->work, zfcp_erp_scsi_scan);
1646 p->unit = unit;
1647 schedule_work(&p->work);
1648}
1649
1594/* 1650/*
1595 * function: 1651 * function:
1596 * 1652 *
@@ -3092,9 +3148,9 @@ zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
3092 && port->rport) { 3148 && port->rport) {
3093 atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED, 3149 atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED,
3094 &unit->status); 3150 &unit->status);
3095 scsi_scan_target(&port->rport->dev, 0, 3151 if (atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING,
3096 port->rport->scsi_target_id, 3152 &unit->status) == 0)
3097 unit->scsi_lun, 0); 3153 zfcp_erp_schedule_work(unit);
3098 } 3154 }
3099 zfcp_unit_put(unit); 3155 zfcp_unit_put(unit);
3100 break; 3156 break;
@@ -3121,7 +3177,7 @@ zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
3121 zfcp_get_busid_by_port(port), 3177 zfcp_get_busid_by_port(port),
3122 port->wwpn); 3178 port->wwpn);
3123 else { 3179 else {
3124 scsi_flush_work(adapter->scsi_host); 3180 scsi_target_unblock(&port->rport->dev);
3125 port->rport->maxframe_size = port->maxframe_size; 3181 port->rport->maxframe_size = port->maxframe_size;
3126 port->rport->supported_classes = 3182 port->rport->supported_classes =
3127 port->supported_classes; 3183 port->supported_classes;
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 99db02062c3b..e742b3de16ac 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -22,6 +22,7 @@
22#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI 22#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI
23 23
24#include "zfcp_ext.h" 24#include "zfcp_ext.h"
25#include <asm/atomic.h>
25 26
26static void zfcp_scsi_slave_destroy(struct scsi_device *sdp); 27static void zfcp_scsi_slave_destroy(struct scsi_device *sdp);
27static int zfcp_scsi_slave_alloc(struct scsi_device *sdp); 28static int zfcp_scsi_slave_alloc(struct scsi_device *sdp);
@@ -179,6 +180,10 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
179 struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; 180 struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
180 181
181 if (unit) { 182 if (unit) {
183 zfcp_erp_wait(unit->port->adapter);
184 wait_event(unit->scsi_scan_wq,
185 atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING,
186 &unit->status) == 0);
182 atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); 187 atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
183 sdpnt->hostdata = NULL; 188 sdpnt->hostdata = NULL;
184 unit->device = NULL; 189 unit->device = NULL;