diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_ccw.c')
| -rw-r--r-- | drivers/s390/scsi/zfcp_ccw.c | 448 |
1 files changed, 227 insertions, 221 deletions
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 0fc46381fc2..f9879d400d0 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -1,312 +1,318 @@ /* - * linux/drivers/s390/scsi/zfcp_ccw.c + * zfcp device driver * - * FCP adapter driver for IBM eServer zSeries + * Registration and callback for the s390 common I/O layer. * - * CCW driver related routines - * - * (C) Copyright IBM Corp. 2003, 2004 - * - * Authors: - * Martin Peschke <mpeschke@de.ibm.com> - * Heiko Carstens <heiko.carstens@de.ibm.com> - * Andreas Herrmann <aherrman@de.ibm.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Copyright IBM Corp. 2002, 2010 */ -#define ZFCP_CCW_C_REVISION "$Revision: 1.58 $" +#define KMSG_COMPONENT "zfcp" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/module.h> #include "zfcp_ext.h" +#include "zfcp_reqlist.h" + +#define ZFCP_MODEL_PRIV 0x4 + +static DEFINE_SPINLOCK(zfcp_ccw_adapter_ref_lock); -#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG +struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *cdev) +{ + struct zfcp_adapter *adapter; + unsigned long flags; + + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + adapter = dev_get_drvdata(&cdev->dev); + if (adapter) + kref_get(&adapter->ref); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); + return adapter; +} -static int zfcp_ccw_probe(struct ccw_device *); -static void zfcp_ccw_remove(struct ccw_device *); -static int zfcp_ccw_set_online(struct ccw_device *); -static int zfcp_ccw_set_offline(struct ccw_device *); -static int zfcp_ccw_notify(struct ccw_device *, int); -static void zfcp_ccw_shutdown(struct device *); +void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags); + kref_put(&adapter->ref, zfcp_adapter_release); + spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); +} + +/** + * zfcp_ccw_activate - activate adapter and wait for it to finish + * @cdev: pointer to belonging ccw device + * @clear: Status flags to clear. + * @tag: s390dbf trace record tag + */ +static int zfcp_ccw_activate(struct ccw_device *cdev, int clear, char *tag) +{ + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 0; + + zfcp_erp_clear_adapter_status(adapter, clear); + zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + tag); + zfcp_erp_wait(adapter); + flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + + zfcp_ccw_adapter_put(adapter); + + return 0; +} static struct ccw_device_id zfcp_ccw_device_id[] = { - {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, - ZFCP_CONTROL_UNIT_MODEL, - ZFCP_DEVICE_TYPE, - ZFCP_DEVICE_MODEL)}, - {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, - ZFCP_CONTROL_UNIT_MODEL, - ZFCP_DEVICE_TYPE, - ZFCP_DEVICE_MODEL_PRIV)}, + { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) }, + { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_MODEL_PRIV) }, {}, }; - -static struct ccw_driver zfcp_ccw_driver = { - .owner = THIS_MODULE, - .name = ZFCP_NAME, - .ids = zfcp_ccw_device_id, - .probe = zfcp_ccw_probe, - .remove = zfcp_ccw_remove, - .set_online = zfcp_ccw_set_online, - .set_offline = zfcp_ccw_set_offline, - .notify = zfcp_ccw_notify, - .driver = { - .shutdown = zfcp_ccw_shutdown, - }, -}; - MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); /** * zfcp_ccw_probe - probe function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * - * This function gets called by the common i/o layer and sets up the initial - * data structures for each fcp adapter, which was detected by the system. - * Also the sysfs files for this adapter will be created by this function. - * In addition the nameserver port will be added to the ports of the adapter - * and its sysfs representation will be created too. + * This function gets called by the common i/o layer for each FCP + * device found on the current system. This is only a stub to make cio + * work: To only allocate adapter resources for devices actually used, + * the allocation is deferred to the first call to ccw_set_online. */ -static int -zfcp_ccw_probe(struct ccw_device *ccw_device) +static int zfcp_ccw_probe(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; - int retval = 0; - - down(&zfcp_data.config_sema); - adapter = zfcp_adapter_enqueue(ccw_device); - if (!adapter) - retval = -EINVAL; - else - ZFCP_LOG_DEBUG("Probed adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - up(&zfcp_data.config_sema); - return retval; + return 0; } /** * zfcp_ccw_remove - remove function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * * This function gets called by the common i/o layer and removes an adapter * from the system. Task of this function is to get rid of all units and * ports that belong to this adapter. And in addition all resources of this * adapter will be freed too. */ -static void -zfcp_ccw_remove(struct ccw_device *ccw_device) +static void zfcp_ccw_remove(struct ccw_device *cdev) { struct zfcp_adapter *adapter; struct zfcp_port *port, *p; struct zfcp_unit *unit, *u; + LIST_HEAD(unit_remove_lh); + LIST_HEAD(port_remove_lh); - ccw_device_set_offline(ccw_device); - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); - - ZFCP_LOG_DEBUG("Removing adapter %s\n", - zfcp_get_busid_by_adapter(adapter)); - write_lock_irq(&zfcp_data.config_lock); - list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { - list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { - list_move(&unit->list, &port->unit_remove_lh); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, - &unit->status); - } - list_move(&port->list, &adapter->port_remove_lh); - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); - } - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - write_unlock_irq(&zfcp_data.config_lock); + ccw_device_set_offline(cdev); - list_for_each_entry_safe(port, p, &adapter->port_remove_lh, list) { - list_for_each_entry_safe(unit, u, &port->unit_remove_lh, list) { - zfcp_unit_dequeue(unit); - } - zfcp_port_dequeue(port); + adapter = zfcp_ccw_adapter_by_cdev(cdev); + if (!adapter) + return; + + write_lock_irq(&adapter->port_list_lock); + list_for_each_entry_safe(port, p, &adapter->port_list, list) { + write_lock(&port->unit_list_lock); + list_for_each_entry_safe(unit, u, &port->unit_list, list) + list_move(&unit->list, &unit_remove_lh); + write_unlock(&port->unit_list_lock); + list_move(&port->list, &port_remove_lh); } - zfcp_adapter_wait(adapter); - zfcp_adapter_dequeue(adapter); + write_unlock_irq(&adapter->port_list_lock); + zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */ + + list_for_each_entry_safe(unit, u, &unit_remove_lh, list) + device_unregister(&unit->dev); + + list_for_each_entry_safe(port, p, &port_remove_lh, list) + device_unregister(&port->dev); - up(&zfcp_data.config_sema); + zfcp_adapter_unregister(adapter); } /** * zfcp_ccw_set_online - set_online function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * @cdev: pointer to belonging ccw device * - * This function gets called by the common i/o layer and sets an adapter - * into state online. Setting an fcp device online means that it will be - * registered with the SCSI stack, that the QDIO queues will be set up - * and that the adapter will be opened (asynchronously). + * This function gets called by the common i/o layer and sets an + * adapter into state online. The first call will allocate all + * adapter resources that will be retained until the device is removed + * via zfcp_ccw_remove. + * + * Setting an fcp device online means that it will be registered with + * the SCSI stack, that the QDIO queues will be set up and that the + * adapter will be opened. */ -static int -zfcp_ccw_set_online(struct ccw_device *ccw_device) +static int zfcp_ccw_set_online(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; - int retval; - - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); - - retval = zfcp_adapter_debug_register(adapter); - if (retval) - goto out; - retval = zfcp_erp_thread_setup(adapter); - if (retval) { - ZFCP_LOG_INFO("error: start of error recovery thread for " - "adapter %s failed\n", - zfcp_get_busid_by_adapter(adapter)); - goto out_erp_thread; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) { + adapter = zfcp_adapter_enqueue(cdev); + + if (IS_ERR(adapter)) { + dev_err(&cdev->dev, + "Setting up data structures for the " + "FCP adapter failed\n"); + return PTR_ERR(adapter); + } + kref_get(&adapter->ref); } - retval = zfcp_adapter_scsi_register(adapter); - if (retval) - goto out_scsi_register; - zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING, - ZFCP_SET); - zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); - zfcp_erp_wait(adapter); - goto out; + /* initialize request counter */ + BUG_ON(!zfcp_reqlist_isempty(adapter->req_list)); + adapter->req_no = 0; - out_scsi_register: - zfcp_erp_thread_kill(adapter); - out_erp_thread: - zfcp_adapter_debug_unregister(adapter); - out: - up(&zfcp_data.config_sema); - return retval; + zfcp_ccw_activate(cdev, 0, "ccsonl1"); + /* scan for remote ports + either at the end of any successful adapter recovery + or only after the adapter recovery for setting a device online */ + zfcp_fc_inverse_conditional_port_scan(adapter); + flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + zfcp_ccw_adapter_put(adapter); + return 0; } /** - * zfcp_ccw_set_offline - set_offline function of zfcp driver - * @ccw_device: pointer to belonging ccw device + * zfcp_ccw_offline_sync - shut down adapter and wait for it to finish + * @cdev: pointer to belonging ccw device + * @set: Status flags to set. + * @tag: s390dbf trace record tag * * This function gets called by the common i/o layer and sets an adapter - * into state offline. Setting an fcp device offline means that it will be - * unregistered from the SCSI stack and that the adapter will be shut down - * asynchronously. + * into state offline. */ -static int -zfcp_ccw_set_offline(struct ccw_device *ccw_device) +static int zfcp_ccw_offline_sync(struct ccw_device *cdev, int set, char *tag) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 0; - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); - zfcp_erp_adapter_shutdown(adapter, 0); + zfcp_erp_set_adapter_status(adapter, set); + zfcp_erp_adapter_shutdown(adapter, 0, tag); zfcp_erp_wait(adapter); - zfcp_adapter_scsi_unregister(adapter); - zfcp_erp_thread_kill(adapter); - zfcp_adapter_debug_unregister(adapter); - up(&zfcp_data.config_sema); + + zfcp_ccw_adapter_put(adapter); return 0; } /** - * zfcp_ccw_notify - * @ccw_device: pointer to belonging ccw device + * zfcp_ccw_set_offline - set_offline function of zfcp driver + * @cdev: pointer to belonging ccw device + * + * This function gets called by the common i/o layer and sets an adapter + * into state offline. + */ +static int zfcp_ccw_set_offline(struct ccw_device *cdev) +{ + return zfcp_ccw_offline_sync(cdev, 0, "ccsoff1"); +} + +/** + * zfcp_ccw_notify - ccw notify function + * @cdev: pointer to belonging ccw device * @event: indicates if adapter was detached or attached * * This function gets called by the common i/o layer if an adapter has gone * or reappeared. */ -static int -zfcp_ccw_notify(struct ccw_device *ccw_device, int event) +static int zfcp_ccw_notify(struct ccw_device *cdev, int event) { - struct zfcp_adapter *adapter; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return 1; - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(&ccw_device->dev); switch (event) { case CIO_GONE: - ZFCP_LOG_NORMAL("adapter %s: device gone\n", - zfcp_get_busid_by_adapter(adapter)); - debug_text_event(adapter->erp_dbf,1,"dev_gone"); - zfcp_erp_adapter_shutdown(adapter, 0); + if (atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */ + zfcp_dbf_hba_basic("ccnigo1", adapter); + break; + } + dev_warn(&cdev->dev, "The FCP device has been detached\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1"); break; case CIO_NO_PATH: - ZFCP_LOG_NORMAL("adapter %s: no path\n", - zfcp_get_busid_by_adapter(adapter)); - debug_text_event(adapter->erp_dbf,1,"no_path"); - zfcp_erp_adapter_shutdown(adapter, 0); + dev_warn(&cdev->dev, + "The CHPID for the FCP device is offline\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2"); break; case CIO_OPER: - ZFCP_LOG_NORMAL("adapter %s: operational again\n", - zfcp_get_busid_by_adapter(adapter)); - debug_text_event(adapter->erp_dbf,1,"dev_oper"); - zfcp_erp_modify_adapter_status(adapter, - ZFCP_STATUS_COMMON_RUNNING, - ZFCP_SET); - zfcp_erp_adapter_reopen(adapter, - ZFCP_STATUS_COMMON_ERP_FAILED); + if (atomic_read(&adapter->status) & + ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */ + zfcp_dbf_hba_basic("ccniop1", adapter); + break; + } + dev_info(&cdev->dev, "The FCP device is operational again\n"); + zfcp_erp_set_adapter_status(adapter, + ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, + "ccnoti4"); + break; + case CIO_BOXED: + dev_warn(&cdev->dev, "The FCP device did not respond within " + "the specified time\n"); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5"); break; } - zfcp_erp_wait(adapter); - up(&zfcp_data.config_sema); + + zfcp_ccw_adapter_put(adapter); return 1; } /** - * zfcp_ccw_register - ccw register function - * - * Registers the driver at the common i/o layer. This function will be called - * at module load time/system start. + * zfcp_ccw_shutdown - handle shutdown from cio + * @cdev: device for adapter to shutdown. */ -int __init -zfcp_ccw_register(void) +static void zfcp_ccw_shutdown(struct ccw_device *cdev) { - int retval; - - retval = ccw_driver_register(&zfcp_ccw_driver); - if (retval) - goto out; - retval = zfcp_sysfs_driver_create_files(&zfcp_ccw_driver.driver); - if (retval) - ccw_driver_unregister(&zfcp_ccw_driver); - out: - return retval; + struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + + if (!adapter) + return; + + zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1"); + zfcp_erp_wait(adapter); + zfcp_erp_thread_kill(adapter); + + zfcp_ccw_adapter_put(adapter); } -/** - * zfcp_ccw_unregister - ccw unregister function - * - * Unregisters the driver from common i/o layer. Function will be called at - * module unload/system shutdown. - */ -void __exit -zfcp_ccw_unregister(void) +static int zfcp_ccw_suspend(struct ccw_device *cdev) { - zfcp_sysfs_driver_remove_files(&zfcp_ccw_driver.driver); - ccw_driver_unregister(&zfcp_ccw_driver); + zfcp_ccw_offline_sync(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccsusp1"); + return 0; } -/** - * zfcp_ccw_shutdown - gets called on reboot/shutdown - * - * Makes sure that QDIO queues are down when the system gets stopped. - */ -static void -zfcp_ccw_shutdown(struct device *dev) +static int zfcp_ccw_thaw(struct ccw_device *cdev) { - struct zfcp_adapter *adapter; + /* trace records for thaw and final shutdown during suspend + can only be found in system dump until the end of suspend + but not after resume because it's based on the memory image + right after the very first suspend (freeze) callback */ + zfcp_ccw_activate(cdev, 0, "ccthaw1"); + return 0; +} - down(&zfcp_data.config_sema); - adapter = dev_get_drvdata(dev); - zfcp_erp_adapter_shutdown(adapter, 0); - zfcp_erp_wait(adapter); - up(&zfcp_data.config_sema); +static int zfcp_ccw_resume(struct ccw_device *cdev) +{ + zfcp_ccw_activate(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccresu1"); + return 0; } -#undef ZFCP_LOG_AREA +struct ccw_driver zfcp_ccw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "zfcp", + }, + .ids = zfcp_ccw_device_id, + .probe = zfcp_ccw_probe, + .remove = zfcp_ccw_remove, + .set_online = zfcp_ccw_set_online, + .set_offline = zfcp_ccw_set_offline, + .notify = zfcp_ccw_notify, + .shutdown = zfcp_ccw_shutdown, + .freeze = zfcp_ccw_suspend, + .thaw = zfcp_ccw_thaw, + .restore = zfcp_ccw_resume, +}; |
