From a67417ab7eeff45bba55666c0e1083260f3624ee Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:06 +0200 Subject: [SCSI] zfcp: invalid usage after free of port resources In certain error scenarios ports, rports are getting attached, validated and removed from the systems environment. Depending on the layer this occurs asynchronously. This patch fixes the few races which existed and ensures all references and cross references are cleared at the time they're invalid. In addition fc transports actions are only scheduled when required. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 6925a178468..54a7a7474aa 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -582,8 +582,10 @@ void zfcp_scsi_schedule_rport_block(struct zfcp_port *port) zfcp_port_get(port); port->rport_task = RPORT_DEL; - if (!queue_work(zfcp_data.work_queue, &port->rport_work)) - zfcp_port_put(port); + if (port->rport && queue_work(zfcp_data.work_queue, &port->rport_work)) + return; + + zfcp_port_put(port); } void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter) -- cgit v1.2.3 From dcd20e2316cdc333dfdee09649dbe3642eb30e75 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 18 Aug 2009 15:43:08 +0200 Subject: [SCSI] zfcp: Only collect SCSI debug data for matching trace levels The default trace level is to only trace failed SCSI commands. Thus it is not necessary to collect trace data for most SCSI commands since it will be thrown away later. Restructure the SCSI trace infrastructure to first check the trace level in a inline function and only do the expensive data collection for matching trace levels. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 54a7a7474aa..0bd80a90426 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -9,8 +9,9 @@ #define KMSG_COMPONENT "zfcp" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt -#include "zfcp_ext.h" #include +#include "zfcp_ext.h" +#include "zfcp_dbf.h" static unsigned int default_depth = 32; module_param_named(queue_depth, default_depth, uint, 0600); -- cgit v1.2.3 From 058b8647892ed49ba6a0d2c0966a72e20e2e69ff Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:14 +0200 Subject: [SCSI] zfcp: Replace fsf_req wait_queue with completion The combination wait_queue/wakeup in conjunction with the flag ZFCP_STATUS_FSFREQ_COMPLETED to signal the completion of an fsfreq was not race-safe and can be better solved by a completion. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0bd80a90426..0de059161b3 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -206,8 +206,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) if (!abrt_req) return FAILED; - wait_event(abrt_req->completion_wq, - abrt_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + wait_for_completion(&abrt_req->completion); if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) dbf_tag = "okay"; @@ -246,8 +245,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (!fsf_req) return FAILED; - wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + wait_for_completion(&fsf_req->completion); if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt); -- cgit v1.2.3 From 4544683a4b1d4e65ccca8c736bac56a195a5206b Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:17 +0200 Subject: [SCSI] zfcp: Move workqueue to adapter struct Remove the global driver work queue and replace it with a workqueue local to the adapter. The usage of this workqueue makes this the correct place for the structure. In addition multiple adapters won't block each other due to the serialization of the queued work. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0de059161b3..2e13d41269a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -572,7 +572,7 @@ void zfcp_scsi_schedule_rport_register(struct zfcp_port *port) zfcp_port_get(port); port->rport_task = RPORT_ADD; - if (!queue_work(zfcp_data.work_queue, &port->rport_work)) + if (!queue_work(port->adapter->work_queue, &port->rport_work)) zfcp_port_put(port); } @@ -581,7 +581,8 @@ void zfcp_scsi_schedule_rport_block(struct zfcp_port *port) zfcp_port_get(port); port->rport_task = RPORT_DEL; - if (port->rport && queue_work(zfcp_data.work_queue, &port->rport_work)) + if (port->rport && queue_work(port->adapter->work_queue, + &port->rport_work)) return; zfcp_port_put(port); -- cgit v1.2.3 From 564e1c86c810f9ccfe4300afa402815e3db4886d Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:19 +0200 Subject: [SCSI] zfcp: Move qdio related data out of zfcp_adapter The zfcp_adapter structure was growing over time to a size of almost one memory page. To reduce the size of the data structure and to seperate different layers, put all qdio related data in the new zfcp_qdio data structure. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 2e13d41269a..4414720c87f 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -225,7 +225,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) { struct zfcp_unit *unit = scpnt->device->hostdata; struct zfcp_adapter *adapter = unit->port->adapter; - struct zfcp_fsf_req *fsf_req; + struct zfcp_fsf_req *fsf_req = NULL; int retval = SUCCESS; int retry = 3; @@ -429,7 +429,7 @@ static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host) if (!data) return NULL; - ret = zfcp_fsf_exchange_port_data_sync(adapter, data); + ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); if (ret) { kfree(data); return NULL; @@ -458,7 +458,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost) if (!data) return; - ret = zfcp_fsf_exchange_port_data_sync(adapter, data); + ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data); if (ret) kfree(data); else { -- cgit v1.2.3 From 5771710bd5edfafcb8656f49b93690a6fae5a4d2 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:21 +0200 Subject: [SCSI] zfcp: Update dbf calls Change the dbf data and functions to use the zfcp_dbf prefix throughout the code. Also change the calls to dbf to use zfcp_dbf instead of zfcp_adapter. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 4414720c87f..b6177ad2d5b 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -53,11 +53,11 @@ static int zfcp_scsi_slave_configure(struct scsi_device *sdp) static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result) { + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; set_host_byte(scpnt, result); if ((scpnt->device != NULL) && (scpnt->device->host != NULL)) - zfcp_scsi_dbf_event_result("fail", 4, - (struct zfcp_adapter*) scpnt->device->host->hostdata[0], - scpnt, NULL); + zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL); /* return directly */ scpnt->scsi_done(scpnt); } @@ -93,7 +93,7 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, scsi_result = fc_remote_port_chkready(rport); if (unlikely(scsi_result)) { scpnt->result = scsi_result; - zfcp_scsi_dbf_event_result("fail", 4, adapter, scpnt, NULL); + zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL); scpnt->scsi_done(scpnt); return 0; } @@ -181,8 +181,8 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) spin_unlock(&adapter->req_list_lock); if (!old_req) { write_unlock_irqrestore(&adapter->abort_lock, flags); - zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, - old_reqid); + zfcp_dbf_scsi_abort("lte1", adapter->dbf, scpnt, NULL, + old_reqid); return FAILED; /* completion could be in progress */ } old_req->data = NULL; @@ -198,8 +198,8 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) zfcp_erp_wait(adapter); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { - zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL, - old_reqid); + zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL, + old_reqid); return SUCCESS; } } @@ -216,7 +216,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) dbf_tag = "fail"; retval = FAILED; } - zfcp_scsi_dbf_event_abort(dbf_tag, adapter, scpnt, abrt_req, old_reqid); + zfcp_dbf_scsi_abort(dbf_tag, adapter->dbf, scpnt, abrt_req, old_reqid); zfcp_fsf_req_free(abrt_req); return retval; } @@ -237,8 +237,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) zfcp_erp_wait(adapter); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { - zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, - scpnt); + zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt); return SUCCESS; } } @@ -248,13 +247,13 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) wait_for_completion(&fsf_req->completion); if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { - zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt); + zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt); retval = FAILED; } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) { - zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt); + zfcp_dbf_scsi_devreset("nsup", tm_flags, unit, scpnt); retval = FAILED; } else - zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt); + zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt); zfcp_fsf_req_free(fsf_req); return retval; -- cgit v1.2.3 From ea945ff84c2ce1089edb7914ffdd998c24c25903 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 18 Aug 2009 15:43:24 +0200 Subject: [SCSI] zfcp: resolve false usage of dd_data in fc_rport The fc_rport structure reserves a reference where a LLD can put information required in a situation where the fc transport class is triggering LLD callbacks. The zfcp driver was using this variable directly which is discouraged. This patch solves this issue by making this reference unnecessary. In addition the dev_loss_tmo callback is removed, it is not required: zfcp does not access the fc_rport after calling fc_remote_port_delete. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'drivers/s390/scsi/zfcp_scsi.c') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b6177ad2d5b..3ff726afafc 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -490,21 +490,6 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) rport->dev_loss_tmo = timeout; } -/** - * zfcp_scsi_dev_loss_tmo_callbk - Free any reference to rport - * @rport: The rport that is about to be deleted. - */ -static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport) -{ - struct zfcp_port *port; - - write_lock_irq(&zfcp_data.config_lock); - port = rport->dd_data; - if (port) - port->rport = NULL; - write_unlock_irq(&zfcp_data.config_lock); -} - /** * zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport * @rport: The FC rport where to teminate I/O @@ -516,9 +501,12 @@ static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport) static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) { struct zfcp_port *port; + struct Scsi_Host *shost = rport_to_shost(rport); + struct zfcp_adapter *adapter = + (struct zfcp_adapter *)shost->hostdata[0]; write_lock_irq(&zfcp_data.config_lock); - port = rport->dd_data; + port = zfcp_get_port_by_wwpn(adapter, rport->port_name); if (port) zfcp_port_get(port); write_unlock_irq(&zfcp_data.config_lock); @@ -550,7 +538,6 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) return; } - rport->dd_data = port; rport->maxframe_size = port->maxframe_size; rport->supported_classes = port->supported_classes; port->rport = rport; @@ -663,7 +650,6 @@ struct fc_function_template zfcp_transport_functions = { .reset_fc_host_stats = zfcp_reset_fc_host_stats, .set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo, .get_host_port_state = zfcp_get_host_port_state, - .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk, .terminate_rport_io = zfcp_scsi_terminate_rport_io, .show_host_port_state = 1, .bsg_request = zfcp_execute_fc_job, -- cgit v1.2.3