From 75cb71f3184f3dd5b78d991d5e0e047774865f5d Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Tue, 14 Apr 2009 15:36:22 +0200 Subject: [S390] qdio: remove dead timeout handler The QDIO ccw devices are started by ccw_device_start so no timeout can occur for the interrupt handler. Remove the dead code. In case of an I/O error set the device state to error and wake up a possibly running qdio_shutdown waiter. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 9e8a2914259b..accd957454e7 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -881,42 +881,6 @@ no_handler: qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); } -static void qdio_call_shutdown(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); - put_device(&cdev->dev); -} - -static void qdio_int_error(struct ccw_device *cdev) -{ - struct qdio_irq *irq_ptr = cdev->private->qdio_data; - - switch (irq_ptr->state) { - case QDIO_IRQ_STATE_INACTIVE: - case QDIO_IRQ_STATE_CLEANUP: - qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); - break; - case QDIO_IRQ_STATE_ESTABLISHED: - case QDIO_IRQ_STATE_ACTIVE: - qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); - if (get_device(&cdev->dev)) { - /* Can't call shutdown from interrupt context. */ - PREPARE_WORK(&cdev->private->kick_work, - qdio_call_shutdown); - queue_work(ccw_device_work, &cdev->private->kick_work); - } - break; - default: - WARN_ON(1); - } - wake_up(&cdev->private->wait_q); -} - static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat, int dstat) { @@ -973,10 +937,8 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, switch (PTR_ERR(irb)) { case -EIO: DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no); - return; - case -ETIMEDOUT: - DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no); - qdio_int_error(cdev); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); + wake_up(&cdev->private->wait_q); return; default: WARN_ON(1); @@ -1001,7 +963,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, case QDIO_IRQ_STATE_ACTIVE: if (cstat & SCHN_STAT_PCI) { qdio_int_handler_pci(irq_ptr); - /* no state change so no need to wake up wait_q */ return; } if ((cstat & ~SCHN_STAT_PCI) || dstat) { -- cgit v1.2.3 From f3445a1a656bc26b07946cc6d20de1ef07c8d116 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 14 Apr 2009 15:36:23 +0200 Subject: [S390] dasd: Use the new async framework for autoonlining. The dasd driver can automatically online detected dasds, which especially important for finding the root device. Currently, it will wait for each online operation to finish individually, which may take long if many dasds need to be onlined. When using the new async framework, these onlining operations can run in parallel and presence of the root device is ensured by the fact that prepare_namespace() waits for all async threads to finish. Signed-off-by: Cornelia Huck Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0570794ccf1c..d1815272c435 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -480,8 +481,10 @@ static void dasd_change_state(struct dasd_device *device) if (rc && rc != -EAGAIN) device->target = device->state; - if (device->state == device->target) + if (device->state == device->target) { wake_up(&dasd_init_waitq); + dasd_put_device(device); + } /* let user-space know that the device status changed */ kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE); @@ -513,12 +516,15 @@ void dasd_kick_device(struct dasd_device *device) */ void dasd_set_target_state(struct dasd_device *device, int target) { + dasd_get_device(device); /* If we are in probeonly mode stop at DASD_STATE_READY. */ if (dasd_probeonly && target > DASD_STATE_READY) target = DASD_STATE_READY; if (device->target != target) { - if (device->state == target) + if (device->state == target) { wake_up(&dasd_init_waitq); + dasd_put_device(device); + } device->target = target; } if (device->state != device->target) @@ -2148,6 +2154,22 @@ dasd_exit(void) * SECTION: common functions for ccw_driver use */ +static void dasd_generic_auto_online(void *data, async_cookie_t cookie) +{ + struct ccw_device *cdev = data; + int ret; + + ret = ccw_device_set_online(cdev); + if (ret) + pr_warning("%s: Setting the DASD online failed with rc=%d\n", + dev_name(&cdev->dev), ret); + else { + struct dasd_device *device = dasd_device_from_cdev(cdev); + wait_event(dasd_init_waitq, _wait_for_device(device)); + dasd_put_device(device); + } +} + /* * Initial attempt at a probe function. this can be simplified once * the other detection code is gone. @@ -2180,10 +2202,7 @@ int dasd_generic_probe(struct ccw_device *cdev, */ if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) || (dasd_autodetect && dasd_busid_known(dev_name(&cdev->dev)) != 0)) - ret = ccw_device_set_online(cdev); - if (ret) - pr_warning("%s: Setting the DASD online failed with rc=%d\n", - dev_name(&cdev->dev), ret); + async_schedule(dasd_generic_auto_online, cdev); return 0; } @@ -2290,13 +2309,7 @@ int dasd_generic_set_online(struct ccw_device *cdev, } else pr_debug("dasd_generic device %s found\n", dev_name(&cdev->dev)); - - /* FIXME: we have to wait for the root device but we don't want - * to wait for each single device but for all at once. */ - wait_event(dasd_init_waitq, _wait_for_device(device)); - dasd_put_device(device); - return rc; } -- cgit v1.2.3 From 52db45c3c55a0fca53077dfd7b123e30cd210aad Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Tue, 14 Apr 2009 15:36:24 +0200 Subject: [S390] dasd: fix idaw boundary checking for track based ccw A ccw command that reads or writes several records at once will usually transfer more data then fits into one page and needs to address memory areas using a list of indirect data address words (idaw). All but the first of these areas must start on a 4KB or 2KB block boundary (depending on the idaw format). A check for this restriction was missing and has been added with this patch. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 21254793c604..cb52da033f06 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2019,15 +2019,23 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( ccw++; recid += count; new_track = 0; + /* first idaw for a ccw may start anywhere */ + if (!idaw_dst) + idaw_dst = dst; } - /* If we start a new idaw, everything is fine and the - * start of the new idaw is the start of this segment. + /* If we start a new idaw, we must make sure that it + * starts on an IDA_BLOCK_SIZE boundary. * If we continue an idaw, we must make sure that the * current segment begins where the so far accumulated * idaw ends */ - if (!idaw_dst) - idaw_dst = dst; + if (!idaw_dst) { + if (__pa(dst) & (IDA_BLOCK_SIZE-1)) { + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-ERANGE); + } else + idaw_dst = dst; + } if ((idaw_dst + idaw_len) != dst) { dasd_sfree_request(cqr, startdev); return ERR_PTR(-ERANGE); -- cgit v1.2.3 From 4d7a3cdfb4c9aa1e2ce61f3b16b24eb6acf6726d Mon Sep 17 00:00:00 2001 From: Frank Munzert Date: Thu, 23 Apr 2009 13:58:09 +0200 Subject: [S390] Use tape_generic_offline directly. tape_3590_offline and tape_34xx_offline are removed and tape_generic_offline is called directly instead. Signed-off-by: Frank Munzert Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tape.h | 2 +- drivers/s390/char/tape_34xx.c | 8 +------- drivers/s390/char/tape_3590.c | 8 +------- drivers/s390/char/tape_core.c | 5 ++++- 4 files changed, 7 insertions(+), 16 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index c07809c8016a..5469e099597e 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -285,7 +285,7 @@ extern int tape_mtop(struct tape_device *, int, int); extern void tape_state_set(struct tape_device *, enum tape_state); extern int tape_generic_online(struct tape_device *, struct tape_discipline *); -extern int tape_generic_offline(struct tape_device *device); +extern int tape_generic_offline(struct ccw_device *); /* Externals from tape_devmap.c */ extern int tape_generic_probe(struct ccw_device *); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 807ded5eb049..5f8e8ef43dd3 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -1294,12 +1294,6 @@ tape_34xx_online(struct ccw_device *cdev) ); } -static int -tape_34xx_offline(struct ccw_device *cdev) -{ - return tape_generic_offline(cdev->dev.driver_data); -} - static struct ccw_driver tape_34xx_driver = { .name = "tape_34xx", .owner = THIS_MODULE, @@ -1307,7 +1301,7 @@ static struct ccw_driver tape_34xx_driver = { .probe = tape_generic_probe, .remove = tape_generic_remove, .set_online = tape_34xx_online, - .set_offline = tape_34xx_offline, + .set_offline = tape_generic_offline, }; static int diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index fc1d91294143..823b05bd0dd7 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -1707,19 +1707,13 @@ tape_3590_online(struct ccw_device *cdev) &tape_discipline_3590); } -static int -tape_3590_offline(struct ccw_device *cdev) -{ - return tape_generic_offline(cdev->dev.driver_data); -} - static struct ccw_driver tape_3590_driver = { .name = "tape_3590", .owner = THIS_MODULE, .ids = tape_3590_ids, .probe = tape_generic_probe, .remove = tape_generic_remove, - .set_offline = tape_3590_offline, + .set_offline = tape_generic_offline, .set_online = tape_3590_online, }; diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 08c09d3503cf..8a109f3b69c6 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -387,8 +387,11 @@ tape_cleanup_device(struct tape_device *device) * Manual offline is only allowed while the drive is not in use. */ int -tape_generic_offline(struct tape_device *device) +tape_generic_offline(struct ccw_device *cdev) { + struct tape_device *device; + + device = cdev->dev.driver_data; if (!device) { return -ENODEV; } -- cgit v1.2.3 From 135ea137e3c2872448c12d3a8890389fa64990c6 Mon Sep 17 00:00:00 2001 From: Martin Petermann Date: Fri, 17 Apr 2009 15:08:01 +0200 Subject: [SCSI] zfcp: Avoid referencing freed memory in req send Avoid referencing a fsf request after sending it in fcp_fsf_req_send, it might have already completed and deallocated. Signed-off-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index b29f3121b666..fa896dc600bf 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -766,8 +766,9 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter, static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; - unsigned long flags; - int idx; + unsigned long flags; + int idx; + int with_qtcb = (req->qtcb != NULL); /* put allocated FSF request into hash table */ spin_lock_irqsave(&adapter->req_list_lock, flags); @@ -789,7 +790,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) } /* Don't increase for unsolicited status */ - if (req->qtcb) + if (with_qtcb) adapter->fsf_req_seq_no++; adapter->req_no++; -- cgit v1.2.3 From 306b6edcdd7b6cc5fee50d48fc398201fa3df841 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:02 +0200 Subject: [SCSI] zfcp: Enable auto-port discovery for NPIV. Since we're setting the host port type now to FC_PORTTYPE_NPIV for adapters running in NPIV mode we should allow this port type for auto-port scanning as well. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index e8d032b9dfbd..e56141d10407 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -637,7 +637,8 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter) max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES; max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE; - if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT) + if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT && + fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) return 0; ret = zfcp_wka_port_get(&adapter->nsp); -- cgit v1.2.3 From ada81b748b768eb5b75567fd1db5e87ba5c98bf0 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 17 Apr 2009 15:08:03 +0200 Subject: [SCSI] zfcp: Dont call zfcp_fsf_req_free on NULL pointer Fix problem that zfcp_fsf_exchange_config_data_sync and zfcp_fsf_exchange_config_data_sync could try to call zfcp_fsf_req_free with a NULL pointer. Reviewed-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index fa896dc600bf..a8e84065d006 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1254,13 +1254,13 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, spin_lock_bh(&adapter->req_q_lock); if (zfcp_fsf_req_sbal_get(adapter)) - goto out; + goto out_unlock; req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA, 0, NULL); if (IS_ERR(req)) { retval = PTR_ERR(req); - goto out; + goto out_unlock; } sbale = zfcp_qdio_sbale_req(req); @@ -1279,14 +1279,16 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter, zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); retval = zfcp_fsf_req_send(req); -out: spin_unlock_bh(&adapter->req_q_lock); if (!retval) wait_event(req->completion_wq, req->status & ZFCP_STATUS_FSFREQ_COMPLETED); zfcp_fsf_req_free(req); + return retval; +out_unlock: + spin_unlock_bh(&adapter->req_q_lock); return retval; } @@ -1353,13 +1355,13 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, spin_lock_bh(&adapter->req_q_lock); if (zfcp_fsf_req_sbal_get(adapter)) - goto out; + goto out_unlock; req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0, NULL); if (IS_ERR(req)) { retval = PTR_ERR(req); - goto out; + goto out_unlock; } if (data) @@ -1372,14 +1374,18 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter, req->handler = zfcp_fsf_exchange_port_data_handler; zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); retval = zfcp_fsf_req_send(req); -out: spin_unlock_bh(&adapter->req_q_lock); + if (!retval) wait_event(req->completion_wq, req->status & ZFCP_STATUS_FSFREQ_COMPLETED); zfcp_fsf_req_free(req); return retval; + +out_unlock: + spin_unlock_bh(&adapter->req_q_lock); + return retval; } static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) -- cgit v1.2.3 From 92d5193b467c68e8432d6878980621c787e735af Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:04 +0200 Subject: [SCSI] zfcp: Dont block zfcp_wq with scan When running the scsi_scan from the zfcp workqueue and the target device does not respond, the zfcp workqueue can block until the scsi_scan hits a timeout. Move the work to the scsi host workqueue, since this one is also used for the scan from the SCSI midlayer. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 5 ++--- drivers/s390/scsi/zfcp_def.h | 2 +- drivers/s390/scsi/zfcp_erp.c | 50 ++++--------------------------------------- drivers/s390/scsi/zfcp_ext.h | 1 + drivers/s390/scsi/zfcp_scsi.c | 17 +++++++++++++++ 5 files changed, 25 insertions(+), 50 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 616c60ffcf2c..5a01ef9aeb66 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -97,9 +97,7 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) ccw_device_set_online(adapter->ccw_device); zfcp_erp_wait(adapter); - wait_event(adapter->erp_done_wqh, - !(atomic_read(&unit->status) & - ZFCP_STATUS_UNIT_SCSI_WORK_PENDING)); + flush_work(&unit->scsi_work); down(&zfcp_data.config_sema); zfcp_unit_put(unit); @@ -279,6 +277,7 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) atomic_set(&unit->refcount, 0); init_waitqueue_head(&unit->remove_wq); + INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); unit->port = port; unit->fcp_lun = fcp_lun; diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index a0318630f047..4c362a9069f0 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -255,7 +255,6 @@ enum zfcp_wka_status { /* logical unit status */ #define ZFCP_STATUS_UNIT_SHARED 0x00000004 #define ZFCP_STATUS_UNIT_READONLY 0x00000008 -#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020 /* FSF request status (this does not have a common part) */ #define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002 @@ -530,6 +529,7 @@ struct zfcp_unit { struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; struct zfcp_latencies latencies; + struct work_struct scsi_work; }; /* FSF request */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 631bdb1dfd6c..4e160523c31b 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1176,48 +1176,6 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) } } -struct zfcp_erp_add_work { - struct zfcp_unit *unit; - struct work_struct work; -}; - -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; - - if (rport && rport->port_state == FC_PORTSTATE_ONLINE) - scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, - scsilun_to_int((struct scsi_lun *)&unit->fcp_lun), 0); - atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); - zfcp_unit_put(unit); - wake_up(&unit->port->adapter->erp_done_wqh); - kfree(p); -} - -static void zfcp_erp_schedule_work(struct zfcp_unit *unit) -{ - struct zfcp_erp_add_work *p; - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) { - dev_err(&unit->port->adapter->ccw_device->dev, - "Registering unit 0x%016Lx on port 0x%016Lx failed\n", - (unsigned long long)unit->fcp_lun, - (unsigned long long)unit->port->wwpn); - return; - } - - zfcp_unit_get(unit); - atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); - INIT_WORK(&p->work, zfcp_erp_scsi_scan); - p->unit = unit; - if (!queue_work(zfcp_data.work_queue, &p->work)) - zfcp_unit_put(unit); -} - static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) { struct zfcp_adapter *adapter = act->adapter; @@ -1226,11 +1184,11 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) switch (act->action) { case ZFCP_ERP_ACTION_REOPEN_UNIT: - flush_work(&port->rport_work); if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) { - if (!(atomic_read(&unit->status) & - ZFCP_STATUS_UNIT_SCSI_WORK_PENDING)) - zfcp_erp_schedule_work(unit); + zfcp_unit_get(unit); + if (scsi_queue_work(unit->port->adapter->scsi_host, + &unit->scsi_work) <= 0) + zfcp_unit_put(unit); } zfcp_unit_put(unit); break; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index f6399ca97bcb..df740f10d26a 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -158,6 +158,7 @@ extern void zfcp_scsi_rport_work(struct work_struct *); extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *); extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); +extern void zfcp_scsi_scan(struct work_struct *); /* zfcp_sysfs.c */ extern struct attribute_group zfcp_sysfs_unit_attrs; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 58201e1ae478..5b11386d70bb 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -583,6 +583,23 @@ void zfcp_scsi_rport_work(struct work_struct *work) } +void zfcp_scsi_scan(struct work_struct *work) +{ + struct zfcp_unit *unit = container_of(work, struct zfcp_unit, + scsi_work); + struct fc_rport *rport; + + flush_work(&unit->port->rport_work); + rport = unit->port->rport; + + if (rport && rport->port_state == FC_PORTSTATE_ONLINE) + scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, + scsilun_to_int((struct scsi_lun *) + &unit->fcp_lun), 0); + + zfcp_unit_put(unit); +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, -- cgit v1.2.3 From 828bc1212a685918bbdb5866504b63eea2c241f5 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:05 +0200 Subject: [SCSI] zfcp: Set WKA-port to offline on adapter deactivation The nameserver port might be in state online when the adapter is offlined. On adapter reactivation the nameserver port is not re-opened due to the PORT_ONLINE status. This results in an unsuccessful recovery. In forcing the nameserver port status to offline on all adapter offline events this issue is prevented. Waiting for the reference count to drop to zero in zfcp_wka_port_offline is not required, so remove it. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 3 ++- drivers/s390/scsi/zfcp_ccw.c | 1 - drivers/s390/scsi/zfcp_erp.c | 1 + drivers/s390/scsi/zfcp_ext.h | 1 + drivers/s390/scsi/zfcp_fc.c | 15 ++++++++------- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 5a01ef9aeb66..a00d2bb9559c 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -524,6 +524,8 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); + zfcp_fc_nameserver_init(adapter); + if (!zfcp_adapter_scsi_register(adapter)) return 0; @@ -552,7 +554,6 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) cancel_work_sync(&adapter->scan_work); cancel_work_sync(&adapter->stat_work); - cancel_delayed_work_sync(&adapter->nsp.work); zfcp_adapter_scsi_unregister(adapter); sysfs_remove_group(&adapter->ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index cfb0dcb6e3ff..733fe3bf6285 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -108,7 +108,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device) /* initialize request counter */ BUG_ON(!zfcp_reqlist_isempty(adapter)); adapter->req_no = 0; - zfcp_fc_nameserver_init(adapter); zfcp_erp_modify_adapter_status(adapter, "ccsonl1", NULL, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 4e160523c31b..b73e37027eb5 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -719,6 +719,7 @@ static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act) zfcp_qdio_close(adapter); zfcp_fsf_req_dismiss_all(adapter); adapter->fsf_req_seq_no = 0; + zfcp_fc_wka_port_force_offline(&adapter->nsp); /* all ports and units are closed */ zfcp_erp_modify_adapter_status(adapter, "erascl1", NULL, ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index df740f10d26a..2e31b536548c 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -106,6 +106,7 @@ extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *); extern void zfcp_test_link(struct zfcp_port *); extern void zfcp_fc_link_test_work(struct work_struct *); extern void zfcp_fc_nameserver_init(struct zfcp_adapter *); +extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *); /* zfcp_fsf.c */ extern int zfcp_fsf_open_port(struct zfcp_erp_action *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index e56141d10407..5d42929fe2f2 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -98,13 +98,6 @@ static void zfcp_wka_port_offline(struct work_struct *work) struct zfcp_wka_port *wka_port = container_of(dw, struct zfcp_wka_port, work); - /* Don't wait forvever. If the wka_port is too busy take it offline - through a new call later */ - if (!wait_event_timeout(wka_port->completion_wq, - atomic_read(&wka_port->refcount) == 0, - HZ >> 1)) - return; - mutex_lock(&wka_port->mutex); if ((atomic_read(&wka_port->refcount) != 0) || (wka_port->status != ZFCP_WKA_PORT_ONLINE)) @@ -142,6 +135,14 @@ void zfcp_fc_nameserver_init(struct zfcp_adapter *adapter) INIT_DELAYED_WORK(&wka_port->work, zfcp_wka_port_offline); } +void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka) +{ + cancel_delayed_work_sync(&wka->work); + mutex_lock(&wka->mutex); + wka->status = ZFCP_WKA_PORT_OFFLINE; + mutex_unlock(&wka->mutex); +} + static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, struct fcp_rscn_element *elem) { -- cgit v1.2.3 From 94ab4b38b2ab35a8ca955f4821c970a0ed7ec9ec Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:06 +0200 Subject: [SCSI] zfcp: avoid false ERP complete due to sema race The ERP thread is performing a task before it is executing the corresponding down on the semaphore. The response handler of the just started exchange config should wait for the completion by performing a down on this semaphore. Since this semaphore is still positive from the ERP enqueue the handler won't wait and therefore the exchange config will always fail leaving the adapter in error. The problem can be solved by performing the down on the semaphore before starting an ERP task. This is the logically correct order. Only walk the ERP loop if there is a task to perform. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_erp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index b73e37027eb5..fdc9b4352a64 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1311,6 +1311,11 @@ static int zfcp_erp_thread(void *data) while (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL)) { + + zfcp_rec_dbf_event_thread_lock("erthrd1", adapter); + ignore = down_interruptible(&adapter->erp_ready_sem); + zfcp_rec_dbf_event_thread_lock("erthrd2", adapter); + write_lock_irqsave(&adapter->erp_lock, flags); next = adapter->erp_ready_head.next; write_unlock_irqrestore(&adapter->erp_lock, flags); @@ -1322,10 +1327,6 @@ static int zfcp_erp_thread(void *data) if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED) zfcp_erp_wakeup(adapter); } - - zfcp_rec_dbf_event_thread_lock("erthrd1", adapter); - ignore = down_interruptible(&adapter->erp_ready_sem); - zfcp_rec_dbf_event_thread_lock("erthrd2", adapter); } atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status); -- cgit v1.2.3 From d81ad31c6cf4e318e6b94c959bd9a56ca440b279 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:07 +0200 Subject: [SCSI] zfcp: no port recovery after storage side error inject The remote port remains in error state even if the connection is re-established. A wrong precondition check was performed on the port status leading to a cancellation of the port reopen. Remove the pre-req check because it's not required and better handled within the ERP. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 5d42929fe2f2..a0f925013acf 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -432,11 +432,6 @@ void zfcp_fc_link_test_work(struct work_struct *work) container_of(work, struct zfcp_port, test_link_work); int retval; - if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) { - zfcp_port_put(port); - return; /* port erp is running and will update rport status */ - } - zfcp_port_get(port); port->rport_task = RPORT_DEL; zfcp_scsi_rport_work(&port->rport_work); -- cgit v1.2.3 From 048225e3f44f07c0f67e9665be0b4f18788de0eb Mon Sep 17 00:00:00 2001 From: Martin Petermann Date: Fri, 17 Apr 2009 15:08:08 +0200 Subject: [SCSI] zfcp: remove unit will fail if add unit is not finished On some hardware it can take some time to add a unit. If some remove this unit during this process the remove will fail. Signed-off-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_sysfs.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 9a3b8e261c0a..3e51e64d1108 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -254,12 +254,21 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, write_lock_irq(&zfcp_data.config_lock); unit = zfcp_get_unit_by_lun(port, fcp_lun); - if (unit && (atomic_read(&unit->refcount) == 0)) { - zfcp_unit_get(unit); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); - list_move(&unit->list, &unit_remove_lh); - } else - unit = NULL; + if (unit) { + write_unlock_irq(&zfcp_data.config_lock); + /* wait for possible timeout during SCSI probe */ + flush_work(&unit->scsi_work); + write_lock_irq(&zfcp_data.config_lock); + + if (atomic_read(&unit->refcount) == 0) { + zfcp_unit_get(unit); + atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, + &unit->status); + list_move(&unit->list, &unit_remove_lh); + } else { + unit = NULL; + } + } write_unlock_irq(&zfcp_data.config_lock); -- cgit v1.2.3 From f7306bf6150161689aaa445c433699895f446208 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 17 Apr 2009 15:08:09 +0200 Subject: [SCSI] zfcp: Let actcli handle control file errors Error codes specific to the control file requests are evaluated by the actcli tool, so don't report -ENXIO for those. Generic problems are still checked for outside the command specific handler. Reviewed-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index a8e84065d006..bd2b23766894 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2479,8 +2479,6 @@ out: static void zfcp_fsf_control_file_handler(struct zfcp_fsf_req *req) { - if (req->qtcb->header.fsf_status != FSF_GOOD) - req->status |= ZFCP_STATUS_FSFREQ_ERROR; } /** -- cgit v1.2.3 From 5b43e719308143ac02e6f4ee24132f8acad99ea7 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Fri, 17 Apr 2009 15:08:10 +0200 Subject: [SCSI] zfcp: no port recovery after ADISC request timeout A remote port remains in error state even if we receive a RSCN stating that the connection is re-established. The port recovery is not started due to a flag which is not reset. The solution is to clear the flag in question before we trigger a ERP. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index a0f925013acf..db409684e06f 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -373,7 +373,8 @@ static void zfcp_fc_adisc_handler(unsigned long data) if (adisc->els.status) { /* request rejected or timed out */ - zfcp_erp_port_forced_reopen(port, 0, "fcadh_1", NULL); + zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, + "fcadh_1", NULL); goto out; } -- cgit v1.2.3 From c6936e7f34383db2f5038dfa2f0c168ebc2920e5 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 17 Apr 2009 15:08:11 +0200 Subject: [SCSI] zfcp: Fix abort handler for completions in progress When the abort handler cannot find a pending FSF request, the request completion could just be running. This means we cannot return SUCCESS, since this would lead to call to scsi_done after exiting the SCSI error handler which is not allowed. Reviewed-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 5b11386d70bb..e7d065209065 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -171,7 +171,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) write_unlock_irqrestore(&adapter->abort_lock, flags); zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, old_req_id); - return SUCCESS; + return FAILED; /* completion could be in progress */ } old_req->data = NULL; -- cgit v1.2.3 From 7001f0c4863230dd1560425fa0f5aad9e21716b8 Mon Sep 17 00:00:00 2001 From: Martin Petermann Date: Fri, 17 Apr 2009 15:08:12 +0200 Subject: [SCSI] zfcp: revert previous patch for sbal counting The current sbal counting can be wrong if a fsf request is waiting for free sbals and at the same time qdio request queue is shutdown and re-opened. Revering a previous patch fixes this issue. Signed-off-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index bd2b23766894..3f365ace7711 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -645,30 +645,30 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) } } -static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter) - __releases(&adapter->req_q_lock) - __acquires(&adapter->req_q_lock) +static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter) { struct zfcp_qdio_queue *req_q = &adapter->req_q; - long ret; - if (atomic_read(&req_q->count) <= -REQUEST_LIST_SIZE) - return -EIO; - if (atomic_read(&req_q->count) > 0) - return 0; + spin_lock_bh(&adapter->req_q_lock); + if (atomic_read(&req_q->count)) + return 1; + spin_unlock_bh(&adapter->req_q_lock); + return 0; +} + +static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter) +{ + long ret; - atomic_dec(&req_q->count); spin_unlock_bh(&adapter->req_q_lock); ret = wait_event_interruptible_timeout(adapter->request_wq, - atomic_read(&req_q->count) >= 0, - 5 * HZ); - spin_lock_bh(&adapter->req_q_lock); - atomic_inc(&req_q->count); - + zfcp_fsf_sbal_check(adapter), 5 * HZ); if (ret > 0) return 0; if (!ret) atomic_inc(&adapter->qdio_outb_full); + + spin_lock_bh(&adapter->req_q_lock); return -EIO; } -- cgit v1.2.3 From 6ab35c0713daf8cd68f272a4be8dfc02c86bbd3b Mon Sep 17 00:00:00 2001 From: Martin Petermann Date: Fri, 17 Apr 2009 15:08:13 +0200 Subject: [SCSI] zfcp: Fix port reference counting If this problem appears zfcp ports cannot be de-queued since it is checked for a zero refcount. The port reference counting is wrong for existing zfcp ports when e.g. an adapter gets on-line again. During port scanning the reference counting for existing ports should not be changed. Signed-off-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index db409684e06f..19ae0842047c 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -539,6 +539,9 @@ static void zfcp_validate_port(struct zfcp_port *port) { struct zfcp_adapter *adapter = port->adapter; + if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC)) + return; + atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status); if ((port->supported_classes != 0) || @@ -599,10 +602,8 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries) if (acc->wwpn == fc_host_port_name(adapter->scsi_host)) continue; port = zfcp_get_port_by_wwpn(adapter, acc->wwpn); - if (port) { - zfcp_port_get(port); + if (port) continue; - } port = zfcp_port_enqueue(adapter, acc->wwpn, ZFCP_STATUS_COMMON_NOESC, d_id); -- cgit v1.2.3 From 3869bb6e73f5f55ce16b13025726b0bfdf1f4aae Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 17 Apr 2009 15:08:14 +0200 Subject: [SCSI] zfcp: Reference counting for cfdc requests Before dropping the reference count with zfcp_adapter_put, increase it with zfcp_adapter_get when issuing cfdc requests. Reviewed-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_cfdc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index 10cbfd172a28..8305c874e86f 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -4,7 +4,7 @@ * Userspace interface for accessing the * Access Control Lists / Control File Data Channel * - * Copyright IBM Corporation 2008 + * Copyright IBM Corporation 2008, 2009 */ #define KMSG_COMPONENT "zfcp" @@ -197,6 +197,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, retval = -ENXIO; goto free_buffer; } + zfcp_adapter_get(adapter); retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg, data_user->control_file); -- cgit v1.2.3 From 70932935b61ee3dcc5a419ec4c367feb2ff808e4 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 17 Apr 2009 15:08:15 +0200 Subject: [SCSI] zfcp: Fix oops when port disappears The zfcp_port might have been removed, while the FC fast_io_fail timer is still running and could trigger the terminate_rport_io callback. Set the pointer to the zfcp_port to NULL and check accordingly before using it. Reviewed-by: Martin Petermann Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 3 +-- drivers/s390/scsi/zfcp_fsf.c | 4 ++++ drivers/s390/scsi/zfcp_scsi.c | 25 +++++++++++++++++++------ 3 files changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index a00d2bb9559c..3ac27ee47396 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -671,8 +671,7 @@ void zfcp_port_dequeue(struct zfcp_port *port) list_del(&port->list); write_unlock_irq(&zfcp_data.config_lock); if (port->rport) - fc_remote_port_delete(port->rport); - port->rport = NULL; + port->rport->dd_data = NULL; zfcp_adapter_put(port->adapter); sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs); device_unregister(&port->sysfs_device); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 3f365ace7711..74dee32afba8 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -172,12 +172,16 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id, struct fsf_link_down_info *link_down) { struct zfcp_adapter *adapter = req->adapter; + unsigned long flags; if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED) return; atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); + + read_lock_irqsave(&zfcp_data.config_lock, flags); zfcp_scsi_schedule_rports_block(adapter); + read_unlock_irqrestore(&zfcp_data.config_lock, flags); if (!link_down) goto out; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e7d065209065..e8fbeaeb5fbf 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -486,10 +486,12 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) */ static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport) { - struct zfcp_port *port = rport->dd_data; + struct zfcp_port *port; write_lock_irq(&zfcp_data.config_lock); - port->rport = NULL; + port = rport->dd_data; + if (port) + port->rport = NULL; write_unlock_irq(&zfcp_data.config_lock); } @@ -503,9 +505,18 @@ 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 = rport->dd_data; + struct zfcp_port *port; + + write_lock_irq(&zfcp_data.config_lock); + port = rport->dd_data; + if (port) + zfcp_port_get(port); + write_unlock_irq(&zfcp_data.config_lock); - zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL); + if (port) { + zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL); + zfcp_port_put(port); + } } static void zfcp_scsi_rport_register(struct zfcp_port *port) @@ -534,8 +545,10 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) static void zfcp_scsi_rport_block(struct zfcp_port *port) { - if (port->rport) - fc_remote_port_delete(port->rport); + struct fc_rport *rport = port->rport; + + if (rport) + fc_remote_port_delete(rport); } void zfcp_scsi_schedule_rport_register(struct zfcp_port *port) -- cgit v1.2.3