From b03670e5277224d1166cb5e4f610fc388186b065 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Mon, 7 May 2007 16:35:04 +0200 Subject: [SCSI] zfcp: Stop system after memory corruption For each request that is sent to the FCP adapter, zfcp allocates memory. Status information and data that is being read from the device is written to this memory by the hardware. After that, the hardware signals this via the response queue and zfcp continues processing. Now, if zfcp detects that there is a signal for an incoming response from the hardware, but there is no outstanding request for that request id, then some memory that can be in use anywhere in the system has just been overwritten. This should never happen, but if it does, stop the system with a panic. Signed-off-by: Christof Schmitt Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_qdio.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 1e12a78e8edd..e50e6ad4e6cb 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -285,8 +285,8 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device, /** * zfcp_qdio_reqid_check - checks for valid reqids or unsolicited status */ -static int zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, - unsigned long req_id) +static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, + unsigned long req_id) { struct zfcp_fsf_req *fsf_req; unsigned long flags; @@ -298,9 +298,7 @@ static int zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, if (!fsf_req) { spin_unlock_irqrestore(&adapter->req_list_lock, flags); - ZFCP_LOG_NORMAL("error: unknown request id (%ld).\n", req_id); - zfcp_erp_adapter_reopen(adapter, 0); - return -EINVAL; + panic("error: unknown request id (%ld).\n", req_id); } zfcp_reqlist_remove(adapter, req_id); @@ -309,8 +307,6 @@ static int zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, /* finish the FSF request */ zfcp_fsf_req_complete(fsf_req); - - return 0; } /* @@ -374,27 +370,9 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device, /* look for QDIO request identifiers in SB */ buffere = &buffer->element[buffere_index]; - retval = zfcp_qdio_reqid_check(adapter, - (unsigned long) buffere->addr); - - if (retval) { - ZFCP_LOG_NORMAL("bug: unexpected inbound " - "packet on adapter %s " - "(reqid=0x%lx, " - "first_element=%d, " - "elements_processed=%d)\n", - zfcp_get_busid_by_adapter(adapter), - (unsigned long) buffere->addr, - first_element, - elements_processed); - ZFCP_LOG_NORMAL("hex dump of inbound buffer " - "at address %p " - "(buffer_index=%d, " - "buffere_index=%d)\n", buffer, - buffer_index, buffere_index); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) buffer, SBAL_SIZE); - } + zfcp_qdio_reqid_check(adapter, + (unsigned long) buffere->addr); + /* * A single used SBALE per inbound SBALE has been * implemented by QDIO so far. Hope they will -- cgit v1.2.3 From 1d589edf9eeb60c9c8e62753d05cf4c8e094e5a7 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 8 May 2007 11:14:41 +0200 Subject: [SCSI] zfcp: print S_ID and D_ID with 3 bytes S_ID and D_ID are defined in the FCP spec as 3 byte fields. Change the output in zfcp print statements accordingly to print them with only 3 bytes. Signed-off-by: Christof Schmitt Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 6 +++--- drivers/s390/scsi/zfcp_erp.c | 20 ++++++++++---------- drivers/s390/scsi/zfcp_fsf.c | 30 +++++++++++++++--------------- 3 files changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 1f9554e08013..ec3f664f6c80 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -1497,7 +1497,7 @@ zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter, if (!port || (port->wwpn != (*(wwn_t *) &els_plogi->serv_param.wwpn))) { ZFCP_LOG_DEBUG("ignored incoming PLOGI for nonexisting port " - "with d_id 0x%08x on adapter %s\n", + "with d_id 0x%06x on adapter %s\n", status_buffer->d_id, zfcp_get_busid_by_adapter(adapter)); } else { @@ -1522,7 +1522,7 @@ zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, if (!port || (port->wwpn != els_logo->nport_wwpn)) { ZFCP_LOG_DEBUG("ignored incoming LOGO for nonexisting port " - "with d_id 0x%08x on adapter %s\n", + "with d_id 0x%06x on adapter %s\n", status_buffer->d_id, zfcp_get_busid_by_adapter(adapter)); } else { @@ -1704,7 +1704,7 @@ static void zfcp_ns_gid_pn_handler(unsigned long data) /* looks like a valid d_id */ port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK; atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status); - ZFCP_LOG_DEBUG("adapter %s: wwpn=0x%016Lx ---> d_id=0x%08x\n", + ZFCP_LOG_DEBUG("adapter %s: wwpn=0x%016Lx ---> d_id=0x%06x\n", zfcp_get_busid_by_port(port), port->wwpn, port->d_id); goto out; diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index c1f2d4b14c2b..f326bbe49fa7 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -342,9 +342,9 @@ zfcp_erp_adisc(struct zfcp_port *port) adisc->wwpn = fc_host_port_name(adapter->scsi_host); adisc->wwnn = fc_host_node_name(adapter->scsi_host); adisc->nport_id = fc_host_port_id(adapter->scsi_host); - ZFCP_LOG_INFO("ADISC request from s_id 0x%08x to d_id 0x%08x " + ZFCP_LOG_INFO("ADISC request from s_id 0x%06x to d_id 0x%06x " "(wwpn=0x%016Lx, wwnn=0x%016Lx, " - "hard_nport_id=0x%08x, nport_id=0x%08x)\n", + "hard_nport_id=0x%06x, nport_id=0x%06x)\n", adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn, adisc->hard_nport_id, adisc->nport_id); @@ -352,7 +352,7 @@ zfcp_erp_adisc(struct zfcp_port *port) retval = zfcp_fsf_send_els(send_els); if (retval != 0) { ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port " - "0x%08x on adapter %s\n", send_els->d_id, + "0x%06x on adapter %s\n", send_els->d_id, zfcp_get_busid_by_adapter(adapter)); goto freemem; } @@ -398,7 +398,7 @@ zfcp_erp_adisc_handler(unsigned long data) if (send_els->status != 0) { ZFCP_LOG_NORMAL("ELS request rejected/timed out, " "force physical port reopen " - "(adapter %s, port d_id=0x%08x)\n", + "(adapter %s, port d_id=0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); debug_text_event(adapter->erp_dbf, 3, "forcreop"); if (zfcp_erp_port_forced_reopen(port, 0)) @@ -411,9 +411,9 @@ zfcp_erp_adisc_handler(unsigned long data) adisc = zfcp_sg_to_address(send_els->resp); - ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id " - "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " - "hard_nport_id=0x%08x, nport_id=0x%08x)\n", + ZFCP_LOG_INFO("ADISC response from d_id 0x%06x to s_id " + "0x%06x (wwpn=0x%016Lx, wwnn=0x%016Lx, " + "hard_nport_id=0x%06x, nport_id=0x%06x)\n", d_id, fc_host_port_id(adapter->scsi_host), (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn, adisc->hard_nport_id, adisc->nport_id); @@ -1377,7 +1377,7 @@ zfcp_erp_port_failed(struct zfcp_port *port) if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status)) ZFCP_LOG_NORMAL("port erp failed (adapter %s, " - "port d_id=0x%08x)\n", + "port d_id=0x%06x)\n", zfcp_get_busid_by_port(port), port->d_id); else ZFCP_LOG_NORMAL("port erp failed (adapter %s, wwpn=0x%016Lx)\n", @@ -2401,7 +2401,7 @@ zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action) retval = ZFCP_ERP_FAILED; } } else { - ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%08x -> " + ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> " "trying open\n", port->wwpn, port->d_id); retval = zfcp_erp_port_strategy_open_port(erp_action); } @@ -2441,7 +2441,7 @@ zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action) case ZFCP_ERP_STEP_UNINITIALIZED: case ZFCP_ERP_STEP_PHYS_PORT_CLOSING: case ZFCP_ERP_STEP_PORT_CLOSING: - ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%08x -> trying open\n", + ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> trying open\n", port->wwpn, port->d_id); retval = zfcp_erp_port_strategy_open_port(erp_action); break; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 4c0a59afd5c8..f120b16c77d5 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -828,7 +828,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) { ZFCP_LOG_NORMAL("bug: Reopen port indication received for" - "nonexisting port with d_id 0x%08x on " + "nonexisting port with d_id 0x%06x on " "adapter %s. Ignored.\n", status_buffer->d_id & ZFCP_DID_MASK, zfcp_get_busid_by_adapter(adapter)); @@ -853,7 +853,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) &status_buffer->status_subtype, sizeof (u32)); ZFCP_LOG_NORMAL("bug: Undefined status subtype received " "for a reopen indication on port with " - "d_id 0x%08x on the adapter %s. " + "d_id 0x%06x on the adapter %s. " "Ignored. (debug info 0x%x)\n", status_buffer->d_id, zfcp_get_busid_by_adapter(adapter), @@ -1156,7 +1156,7 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, } ZFCP_LOG_DEBUG("Abort FCP Command request initiated " - "(adapter%s, port d_id=0x%08x, " + "(adapter%s, port d_id=0x%06x, " "unit x%016Lx, old_req_id=0x%lx)\n", zfcp_get_busid_by_adapter(adapter), unit->port->d_id, @@ -1554,7 +1554,7 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) case FSF_ACCESS_DENIED: ZFCP_LOG_NORMAL("access denied, cannot send generic service " - "command (adapter %s, port d_id=0x%08x)\n", + "command (adapter %s, port d_id=0x%06x)\n", zfcp_get_busid_by_port(port), port->d_id); for (counter = 0; counter < 2; counter++) { subtable = header->fsf_status_qual.halfword[counter * 2]; @@ -1576,7 +1576,7 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) case FSF_GENERIC_COMMAND_REJECTED: ZFCP_LOG_INFO("generic service command rejected " - "(adapter %s, port d_id=0x%08x)\n", + "(adapter %s, port d_id=0x%06x)\n", zfcp_get_busid_by_port(port), port->d_id); ZFCP_LOG_INFO("status qualifier:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO, @@ -1602,7 +1602,7 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) case FSF_PORT_BOXED: ZFCP_LOG_INFO("port needs to be reopened " - "(adapter %s, port d_id=0x%08x)\n", + "(adapter %s, port d_id=0x%06x)\n", zfcp_get_busid_by_port(port), port->d_id); debug_text_event(adapter->erp_dbf, 2, "fsf_s_pboxed"); zfcp_erp_port_boxed(port); @@ -1683,7 +1683,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) NULL, &lock_flags, &fsf_req); if (ret < 0) { ZFCP_LOG_INFO("error: creation of ELS request failed " - "(adapter %s, port d_id: 0x%08x)\n", + "(adapter %s, port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); goto failed_req; } @@ -1708,7 +1708,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) ZFCP_MAX_SBALS_PER_ELS_REQ); if (bytes <= 0) { ZFCP_LOG_INFO("error: creation of ELS request failed " - "(adapter %s, port d_id: 0x%08x)\n", + "(adapter %s, port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); if (bytes == 0) { ret = -ENOMEM; @@ -1725,7 +1725,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) ZFCP_MAX_SBALS_PER_ELS_REQ); if (bytes <= 0) { ZFCP_LOG_INFO("error: creation of ELS request failed " - "(adapter %s, port d_id: 0x%08x)\n", + "(adapter %s, port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); if (bytes == 0) { ret = -ENOMEM; @@ -1739,7 +1739,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) /* reject request */ ZFCP_LOG_INFO("error: microcode does not support chained SBALs" ", ELS request too big (adapter %s, " - "port d_id: 0x%08x)\n", + "port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); ret = -EOPNOTSUPP; goto failed_send; @@ -1760,13 +1760,13 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) ret = zfcp_fsf_req_send(fsf_req); if (ret) { ZFCP_LOG_DEBUG("error: initiation of ELS request failed " - "(adapter %s, port d_id: 0x%08x)\n", + "(adapter %s, port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); goto failed_send; } ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: " - "0x%08x)\n", zfcp_get_busid_by_adapter(adapter), d_id); + "0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); goto out; failed_send: @@ -1859,7 +1859,7 @@ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) case FSF_ELS_COMMAND_REJECTED: ZFCP_LOG_INFO("ELS has been rejected because command filter " "prohibited sending " - "(adapter: %s, port d_id: 0x%08x)\n", + "(adapter: %s, port d_id: 0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); break; @@ -1907,7 +1907,7 @@ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) case FSF_ACCESS_DENIED: ZFCP_LOG_NORMAL("access denied, cannot send ELS command " - "(adapter %s, port d_id=0x%08x)\n", + "(adapter %s, port d_id=0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id); for (counter = 0; counter < 2; counter++) { subtable = header->fsf_status_qual.halfword[counter * 2]; @@ -2070,7 +2070,7 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) ZFCP_LOG_NORMAL("The adapter %s reported the following characteristics:\n" "WWNN 0x%016Lx, " "WWPN 0x%016Lx, " - "S_ID 0x%08x,\n" + "S_ID 0x%06x,\n" "adapter version 0x%x, " "LIC version 0x%x, " "FC link speed %d Gb/s\n", -- cgit v1.2.3 From 801e0ced1891a2b8cad1a435c45234a719b3b6bf Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 8 May 2007 11:15:48 +0200 Subject: [SCSI] zfcp: Locking for req_no and req_seq_no There is a possible race condition while generating the unique request ids and sequence numbers. Both might be read at the same time and have the same value. Fix this by serializing the access through the queue lock of the adapter: First call zfcp_fsf_req_sbal_get that acquires the lock, then read and increment the unique ids. Signed-off-by: Christof Schmitt Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index f120b16c77d5..07094c3dc341 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -4645,23 +4645,22 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, fsf_req->adapter = adapter; fsf_req->fsf_command = fsf_cmd; INIT_LIST_HEAD(&fsf_req->list); - - /* this is serialized (we are holding req_queue-lock of adapter */ - if (adapter->req_no == 0) - adapter->req_no++; - fsf_req->req_id = adapter->req_no++; - init_timer(&fsf_req->timer); - zfcp_fsf_req_qtcb_init(fsf_req); /* initialize waitqueue which may be used to wait on this request completion */ init_waitqueue_head(&fsf_req->completion_wq); ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags); - if(ret < 0) { + if (ret < 0) goto failed_sbals; - } + + /* this is serialized (we are holding req_queue-lock of adapter) */ + if (adapter->req_no == 0) + adapter->req_no++; + fsf_req->req_id = adapter->req_no++; + + zfcp_fsf_req_qtcb_init(fsf_req); /* * We hold queue_lock here. Check if QDIOUP is set and let request fail -- cgit v1.2.3 From 5f852be9e11d62223ea063f6ceed4f9677f54051 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 8 May 2007 11:16:52 +0200 Subject: [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 Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 2 ++ drivers/s390/scsi/zfcp_def.h | 5 ++++ drivers/s390/scsi/zfcp_erp.c | 64 ++++++++++++++++++++++++++++++++++++++++--- drivers/s390/scsi/zfcp_scsi.c | 5 ++++ 4 files changed, 72 insertions(+), 4 deletions(-) (limited to 'drivers/s390') 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) unit->sysfs_device.release = zfcp_sysfs_unit_release; dev_set_drvdata(&unit->sysfs_device, unit); + init_waitqueue_head(&unit->scsi_scan_wq); + /* mark unit unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); 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 { \ #define ZFCP_STATUS_UNIT_SHARED 0x00000004 #define ZFCP_STATUS_UNIT_READONLY 0x00000008 #define ZFCP_STATUS_UNIT_REGISTERED 0x00000010 +#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020 /* FSF request status (this does not have a common part) */ #define ZFCP_STATUS_FSFREQ_NOT_INIT 0x00000000 @@ -980,6 +981,10 @@ struct zfcp_unit { struct scsi_device *device; /* scsi device struct pointer */ struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; + wait_queue_head_t scsi_scan_wq; /* can be used to wait until + all scsi_scan_target + requests have been + completed. */ }; /* 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) return result; } +struct zfcp_erp_add_work { + struct zfcp_unit *unit; + struct work_struct work; +}; + +/** + * zfcp_erp_scsi_scan + * @data: pointer to a struct zfcp_erp_add_work + * + * Registers a logical unit with the SCSI stack. + */ +static void zfcp_erp_scsi_scan(struct work_struct *work) +{ + struct zfcp_erp_add_work *p = + container_of(work, struct zfcp_erp_add_work, work); + struct zfcp_unit *unit = p->unit; + struct fc_rport *rport = unit->port->rport; + scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, + unit->scsi_lun, 0); + atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); + wake_up(&unit->scsi_scan_wq); + zfcp_unit_put(unit); + kfree(p); +} + +/** + * zfcp_erp_schedule_work + * @unit: pointer to unit which should be registered with SCSI stack + * + * Schedules work which registers a unit with the SCSI stack + */ +static void +zfcp_erp_schedule_work(struct zfcp_unit *unit) +{ + struct zfcp_erp_add_work *p; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + ZFCP_LOG_NORMAL("error: Out of resources. Could not register " + "the FCP-LUN 0x%Lx connected to " + "the port with WWPN 0x%Lx connected to " + "the adapter %s with the SCSI stack.\n", + unit->fcp_lun, + unit->port->wwpn, + zfcp_get_busid_by_unit(unit)); + return; + } + + zfcp_unit_get(unit); + memset(p, 0, sizeof(*p)); + atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); + INIT_WORK(&p->work, zfcp_erp_scsi_scan); + p->unit = unit; + schedule_work(&p->work); +} + /* * function: * @@ -3092,9 +3148,9 @@ zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter, && port->rport) { atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); - scsi_scan_target(&port->rport->dev, 0, - port->rport->scsi_target_id, - unit->scsi_lun, 0); + if (atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, + &unit->status) == 0) + zfcp_erp_schedule_work(unit); } zfcp_unit_put(unit); break; @@ -3121,7 +3177,7 @@ zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter, zfcp_get_busid_by_port(port), port->wwpn); else { - scsi_flush_work(adapter->scsi_host); + scsi_target_unblock(&port->rport->dev); port->rport->maxframe_size = port->maxframe_size; port->rport->supported_classes = 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 @@ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI #include "zfcp_ext.h" +#include static void zfcp_scsi_slave_destroy(struct scsi_device *sdp); static int zfcp_scsi_slave_alloc(struct scsi_device *sdp); @@ -179,6 +180,10 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; if (unit) { + zfcp_erp_wait(unit->port->adapter); + wait_event(unit->scsi_scan_wq, + atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, + &unit->status) == 0); atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); sdpnt->hostdata = NULL; unit->device = NULL; -- cgit v1.2.3 From ca2d02c2f9ea476062ae181eec60b8bcd97857d6 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 8 May 2007 11:17:54 +0200 Subject: [SCSI] zfcp: rework request ID management. Simplify request ID management and make sure that frequently used functions are inlined. Also fix a memory leak in zfcp_adapter_enqueue() which only gets hit in error handling. Signed-off-by: Heiko Carstens Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 82 +++++-------------------------------------- drivers/s390/scsi/zfcp_def.h | 36 +++++++++++++++++++ drivers/s390/scsi/zfcp_erp.c | 3 +- drivers/s390/scsi/zfcp_ext.h | 4 --- drivers/s390/scsi/zfcp_fsf.c | 2 +- drivers/s390/scsi/zfcp_qdio.c | 17 +++++---- drivers/s390/scsi/zfcp_scsi.c | 4 +-- 7 files changed, 59 insertions(+), 89 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 49d5fc729bef..324899c96efe 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -118,97 +118,32 @@ _zfcp_hex_dump(char *addr, int count) #define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF -static int zfcp_reqlist_init(struct zfcp_adapter *adapter) +static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter) { - int i; + int idx; adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head), GFP_KERNEL); - if (!adapter->req_list) return -ENOMEM; - for (i=0; ireq_list[i]); - + for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) + INIT_LIST_HEAD(&adapter->req_list[idx]); return 0; } static void zfcp_reqlist_free(struct zfcp_adapter *adapter) { - struct zfcp_fsf_req *request, *tmp; - unsigned int i; - - for (i=0; ireq_list[i])) - continue; - - list_for_each_entry_safe(request, tmp, - &adapter->req_list[i], list) - list_del(&request->list); - } - kfree(adapter->req_list); } -void zfcp_reqlist_add(struct zfcp_adapter *adapter, - struct zfcp_fsf_req *fsf_req) -{ - unsigned int i; - - i = fsf_req->req_id % REQUEST_LIST_SIZE; - list_add_tail(&fsf_req->list, &adapter->req_list[i]); -} - -void zfcp_reqlist_remove(struct zfcp_adapter *adapter, unsigned long req_id) -{ - struct zfcp_fsf_req *request, *tmp; - unsigned int i, counter; - u64 dbg_tmp[2]; - - i = req_id % REQUEST_LIST_SIZE; - BUG_ON(list_empty(&adapter->req_list[i])); - - counter = 0; - list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list) { - if (request->req_id == req_id) { - dbg_tmp[0] = (u64) atomic_read(&adapter->reqs_active); - dbg_tmp[1] = (u64) counter; - debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16); - list_del(&request->list); - break; - } - counter++; - } -} - -struct zfcp_fsf_req *zfcp_reqlist_ismember(struct zfcp_adapter *adapter, - unsigned long req_id) -{ - struct zfcp_fsf_req *request, *tmp; - unsigned int i; - - /* 0 is reserved as an invalid req_id */ - if (req_id == 0) - return NULL; - - i = req_id % REQUEST_LIST_SIZE; - - list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list) - if (request->req_id == req_id) - return request; - - return NULL; -} - int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) { - unsigned int i; + unsigned int idx; - for (i=0; ireq_list[i])) + for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) + if (!list_empty(&adapter->req_list[idx])) return 0; - return 1; } @@ -1106,7 +1041,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) /* initialize list of fsf requests */ spin_lock_init(&adapter->req_list_lock); - retval = zfcp_reqlist_init(adapter); + retval = zfcp_reqlist_alloc(adapter); if (retval) { ZFCP_LOG_INFO("request list initialization failed\n"); goto failed_low_mem_buffers; @@ -1167,6 +1102,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device) zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); sysfs_failed: dev_set_drvdata(&ccw_device->dev, NULL); + zfcp_reqlist_free(adapter); failed_low_mem_buffers: zfcp_free_low_mem_buffers(adapter); if (qdio_free(ccw_device) != 0) diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 07b0957b82f3..22649639230b 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -1089,6 +1089,42 @@ extern void _zfcp_hex_dump(char *, int); #define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter)) #define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port)) +/* + * Helper functions for request ID management. + */ +static inline int zfcp_reqlist_hash(unsigned long req_id) +{ + return req_id % REQUEST_LIST_SIZE; +} + +static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter, + struct zfcp_fsf_req *fsf_req) +{ + unsigned int idx; + + idx = zfcp_reqlist_hash(fsf_req->req_id); + list_add_tail(&fsf_req->list, &adapter->req_list[idx]); +} + +static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter, + struct zfcp_fsf_req *fsf_req) +{ + list_del(&fsf_req->list); +} + +static inline struct zfcp_fsf_req * +zfcp_reqlist_find(struct zfcp_adapter *adapter, unsigned long req_id) +{ + struct zfcp_fsf_req *request; + unsigned int idx; + + idx = zfcp_reqlist_hash(req_id); + list_for_each_entry(request, &adapter->req_list[idx], list) + if (request->req_id == req_id) + return request; + return NULL; +} + /* * functions needed for reference/usage counting */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 885572300589..e2a3d6fe1651 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -847,8 +847,7 @@ zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action) if (erp_action->fsf_req) { /* take lock to ensure that request is not deleted meanwhile */ spin_lock(&adapter->req_list_lock); - if (zfcp_reqlist_ismember(adapter, - erp_action->fsf_req->req_id)) { + if (zfcp_reqlist_find(adapter, erp_action->fsf_req->req_id)) { /* fsf_req still exists */ debug_text_event(adapter->erp_dbf, 3, "a_ca_req"); debug_event(adapter->erp_dbf, 3, &erp_action->fsf_req, diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 01386ac688a2..991d45667a44 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -184,10 +184,6 @@ extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *, unsigned long); extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *, struct scsi_cmnd *); -extern void zfcp_reqlist_add(struct zfcp_adapter *, struct zfcp_fsf_req *); -extern void zfcp_reqlist_remove(struct zfcp_adapter *, unsigned long); -extern struct zfcp_fsf_req *zfcp_reqlist_ismember(struct zfcp_adapter *, - unsigned long); extern int zfcp_reqlist_isempty(struct zfcp_adapter *); #endif /* ZFCP_EXT_H */ diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 07094c3dc341..083308b1d3e2 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -4787,7 +4787,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req) retval = -EIO; del_timer(&fsf_req->timer); spin_lock(&adapter->req_list_lock); - zfcp_reqlist_remove(adapter, fsf_req->req_id); + zfcp_reqlist_remove(adapter, fsf_req); spin_unlock(&adapter->req_list_lock); /* undo changes in request queue made for this request */ zfcp_qdio_zero_sbals(req_queue->buffer, diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index e50e6ad4e6cb..cb08ca3cc0f9 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -283,7 +283,7 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device, } /** - * zfcp_qdio_reqid_check - checks for valid reqids or unsolicited status + * zfcp_qdio_reqid_check - checks for valid reqids. */ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, unsigned long req_id) @@ -294,14 +294,17 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, debug_long_event(adapter->erp_dbf, 4, req_id); spin_lock_irqsave(&adapter->req_list_lock, flags); - fsf_req = zfcp_reqlist_ismember(adapter, req_id); + fsf_req = zfcp_reqlist_find(adapter, req_id); - if (!fsf_req) { - spin_unlock_irqrestore(&adapter->req_list_lock, flags); - panic("error: unknown request id (%ld).\n", req_id); - } + if (!fsf_req) + /* + * Unknown request means that we have potentially memory + * corruption and must stop the machine immediatly. + */ + panic("error: unknown request id (%ld) on adapter %s.\n", + req_id, zfcp_get_busid_by_adapter(adapter)); - zfcp_reqlist_remove(adapter, req_id); + zfcp_reqlist_remove(adapter, fsf_req); atomic_dec(&adapter->reqs_active); spin_unlock_irqrestore(&adapter->req_list_lock, flags); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e742b3de16ac..16e2d64658af 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -407,8 +407,8 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) /* Check whether corresponding fsf_req is still pending */ spin_lock(&adapter->req_list_lock); - fsf_req = zfcp_reqlist_ismember(adapter, (unsigned long) - scpnt->host_scribble); + fsf_req = zfcp_reqlist_find(adapter, + (unsigned long) scpnt->host_scribble); spin_unlock(&adapter->req_list_lock); if (!fsf_req) { write_unlock_irqrestore(&adapter->abort_lock, flags); -- cgit v1.2.3 From 3b02191aaeedfffe8cba258ebc212c577c278dbe Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 8 May 2007 11:18:50 +0200 Subject: [SCSI] zfcp: clear adapter failed flag if an fsf request times out. Must clear adapter failed flag if an fsf request times out. This is necessary because on link down situations the failed flags gets set but the QDIO queues are still up. Since an adapter reopen will be skipped if the failed flag is set an adapter_reopen that is issued on fsf request timeout has no effect if the local link is down. Might lead to locked up system if the SCSI stack is waiting for abort completion. Signed-off-by: Heiko Carstens Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_erp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index e2a3d6fe1651..aef66bc2b6ca 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -179,7 +179,7 @@ static void zfcp_close_fsf(struct zfcp_adapter *adapter) static void zfcp_fsf_request_timeout_handler(unsigned long data) { struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; - zfcp_erp_adapter_reopen(adapter, 0); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); } void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout) -- cgit v1.2.3 From b64ddf96456cde17be22bf74cafed381a29d58ba Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 8 May 2007 11:19:57 +0200 Subject: [SCSI] zfcp: clear boxed flag on unit reopen. The boxed flag for units was never cleared. This doesn't hurt, but on ACL updates the error recovery could reopen more units than needed. Signed-off-by: Heiko Carstens Signed-off-by: Swen Schillig Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 083308b1d3e2..a8b02542ac2d 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -3043,6 +3043,7 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) queue_designator = &header->fsf_status_qual.fsf_queue_designator; atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | + ZFCP_STATUS_COMMON_ACCESS_BOXED | ZFCP_STATUS_UNIT_SHARED | ZFCP_STATUS_UNIT_READONLY, &unit->status); -- cgit v1.2.3