162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * zfcp device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Module interface and handling of zfcp data structures. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright IBM Corp. 2002, 2020 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Driver authors: 1262306a36Sopenharmony_ci * Martin Peschke (originator of the driver) 1362306a36Sopenharmony_ci * Raimund Schroeder 1462306a36Sopenharmony_ci * Aron Zeh 1562306a36Sopenharmony_ci * Wolfgang Taphorn 1662306a36Sopenharmony_ci * Stefan Bader 1762306a36Sopenharmony_ci * Heiko Carstens (kernel 2.6 port of the driver) 1862306a36Sopenharmony_ci * Andreas Herrmann 1962306a36Sopenharmony_ci * Maxim Shchetynin 2062306a36Sopenharmony_ci * Volker Sameske 2162306a36Sopenharmony_ci * Ralph Wuerthner 2262306a36Sopenharmony_ci * Michael Loehr 2362306a36Sopenharmony_ci * Swen Schillig 2462306a36Sopenharmony_ci * Christof Schmitt 2562306a36Sopenharmony_ci * Martin Petermann 2662306a36Sopenharmony_ci * Sven Schuetz 2762306a36Sopenharmony_ci * Steffen Maier 2862306a36Sopenharmony_ci * Benjamin Block 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define KMSG_COMPONENT "zfcp" 3262306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/seq_file.h> 3562306a36Sopenharmony_ci#include <linux/slab.h> 3662306a36Sopenharmony_ci#include <linux/module.h> 3762306a36Sopenharmony_ci#include "zfcp_ext.h" 3862306a36Sopenharmony_ci#include "zfcp_fc.h" 3962306a36Sopenharmony_ci#include "zfcp_reqlist.h" 4062306a36Sopenharmony_ci#include "zfcp_diag.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define ZFCP_BUS_ID_SIZE 20 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciMODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); 4562306a36Sopenharmony_ciMODULE_DESCRIPTION("FCP HBA driver"); 4662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic char *init_device; 4962306a36Sopenharmony_cimodule_param_named(device, init_device, charp, 0400); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(device, "specify initial device"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct kmem_cache * __init zfcp_cache_hw_align(const char *name, 5362306a36Sopenharmony_ci unsigned long size) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct ccw_device *cdev; 6162306a36Sopenharmony_ci struct zfcp_adapter *adapter; 6262306a36Sopenharmony_ci struct zfcp_port *port; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); 6562306a36Sopenharmony_ci if (!cdev) 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (ccw_device_set_online(cdev)) 6962306a36Sopenharmony_ci goto out_ccw_device; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci adapter = zfcp_ccw_adapter_by_cdev(cdev); 7262306a36Sopenharmony_ci if (!adapter) 7362306a36Sopenharmony_ci goto out_ccw_device; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci port = zfcp_get_port_by_wwpn(adapter, wwpn); 7662306a36Sopenharmony_ci if (!port) 7762306a36Sopenharmony_ci goto out_port; 7862306a36Sopenharmony_ci flush_work(&port->rport_work); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci zfcp_unit_add(port, lun); 8162306a36Sopenharmony_ci put_device(&port->dev); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciout_port: 8462306a36Sopenharmony_ci zfcp_ccw_adapter_put(adapter); 8562306a36Sopenharmony_ciout_ccw_device: 8662306a36Sopenharmony_ci put_device(&cdev->dev); 8762306a36Sopenharmony_ci return; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void __init zfcp_init_device_setup(char *devstr) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci char *token; 9362306a36Sopenharmony_ci char *str, *str_saved; 9462306a36Sopenharmony_ci char busid[ZFCP_BUS_ID_SIZE]; 9562306a36Sopenharmony_ci u64 wwpn, lun; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* duplicate devstr and keep the original for sysfs presentation*/ 9862306a36Sopenharmony_ci str_saved = kstrdup(devstr, GFP_KERNEL); 9962306a36Sopenharmony_ci str = str_saved; 10062306a36Sopenharmony_ci if (!str) 10162306a36Sopenharmony_ci return; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci token = strsep(&str, ","); 10462306a36Sopenharmony_ci if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE) 10562306a36Sopenharmony_ci goto err_out; 10662306a36Sopenharmony_ci strscpy(busid, token, ZFCP_BUS_ID_SIZE); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci token = strsep(&str, ","); 10962306a36Sopenharmony_ci if (!token || kstrtoull(token, 0, (unsigned long long *) &wwpn)) 11062306a36Sopenharmony_ci goto err_out; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci token = strsep(&str, ","); 11362306a36Sopenharmony_ci if (!token || kstrtoull(token, 0, (unsigned long long *) &lun)) 11462306a36Sopenharmony_ci goto err_out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci kfree(str_saved); 11762306a36Sopenharmony_ci zfcp_init_device_configure(busid, wwpn, lun); 11862306a36Sopenharmony_ci return; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cierr_out: 12162306a36Sopenharmony_ci kfree(str_saved); 12262306a36Sopenharmony_ci pr_err("%s is not a valid SCSI device\n", devstr); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int __init zfcp_module_init(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int retval = -ENOMEM; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (zfcp_experimental_dix) 13062306a36Sopenharmony_ci pr_warn("DIX is enabled. It is experimental and might cause problems\n"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci zfcp_fsf_qtcb_cache = zfcp_cache_hw_align("zfcp_fsf_qtcb", 13362306a36Sopenharmony_ci sizeof(struct fsf_qtcb)); 13462306a36Sopenharmony_ci if (!zfcp_fsf_qtcb_cache) 13562306a36Sopenharmony_ci goto out_qtcb_cache; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci zfcp_fc_req_cache = zfcp_cache_hw_align("zfcp_fc_req", 13862306a36Sopenharmony_ci sizeof(struct zfcp_fc_req)); 13962306a36Sopenharmony_ci if (!zfcp_fc_req_cache) 14062306a36Sopenharmony_ci goto out_fc_cache; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci zfcp_scsi_transport_template = 14362306a36Sopenharmony_ci fc_attach_transport(&zfcp_transport_functions); 14462306a36Sopenharmony_ci if (!zfcp_scsi_transport_template) 14562306a36Sopenharmony_ci goto out_transport; 14662306a36Sopenharmony_ci scsi_transport_reserve_device(zfcp_scsi_transport_template, 14762306a36Sopenharmony_ci sizeof(struct zfcp_scsi_dev)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci retval = ccw_driver_register(&zfcp_ccw_driver); 15062306a36Sopenharmony_ci if (retval) { 15162306a36Sopenharmony_ci pr_err("The zfcp device driver could not register with " 15262306a36Sopenharmony_ci "the common I/O layer\n"); 15362306a36Sopenharmony_ci goto out_ccw_register; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (init_device) 15762306a36Sopenharmony_ci zfcp_init_device_setup(init_device); 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciout_ccw_register: 16162306a36Sopenharmony_ci fc_release_transport(zfcp_scsi_transport_template); 16262306a36Sopenharmony_ciout_transport: 16362306a36Sopenharmony_ci kmem_cache_destroy(zfcp_fc_req_cache); 16462306a36Sopenharmony_ciout_fc_cache: 16562306a36Sopenharmony_ci kmem_cache_destroy(zfcp_fsf_qtcb_cache); 16662306a36Sopenharmony_ciout_qtcb_cache: 16762306a36Sopenharmony_ci return retval; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cimodule_init(zfcp_module_init); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void __exit zfcp_module_exit(void) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci ccw_driver_unregister(&zfcp_ccw_driver); 17562306a36Sopenharmony_ci fc_release_transport(zfcp_scsi_transport_template); 17662306a36Sopenharmony_ci kmem_cache_destroy(zfcp_fc_req_cache); 17762306a36Sopenharmony_ci kmem_cache_destroy(zfcp_fsf_qtcb_cache); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cimodule_exit(zfcp_module_exit); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn 18462306a36Sopenharmony_ci * @adapter: pointer to adapter to search for port 18562306a36Sopenharmony_ci * @wwpn: wwpn to search for 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Returns: pointer to zfcp_port or NULL 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistruct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, 19062306a36Sopenharmony_ci u64 wwpn) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned long flags; 19362306a36Sopenharmony_ci struct zfcp_port *port; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci read_lock_irqsave(&adapter->port_list_lock, flags); 19662306a36Sopenharmony_ci list_for_each_entry(port, &adapter->port_list, list) 19762306a36Sopenharmony_ci if (port->wwpn == wwpn) { 19862306a36Sopenharmony_ci if (!get_device(&port->dev)) 19962306a36Sopenharmony_ci port = NULL; 20062306a36Sopenharmony_ci read_unlock_irqrestore(&adapter->port_list_lock, flags); 20162306a36Sopenharmony_ci return port; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci read_unlock_irqrestore(&adapter->port_list_lock, flags); 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci adapter->pool.erp_req = 21062306a36Sopenharmony_ci mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); 21162306a36Sopenharmony_ci if (!adapter->pool.erp_req) 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci adapter->pool.gid_pn_req = 21562306a36Sopenharmony_ci mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); 21662306a36Sopenharmony_ci if (!adapter->pool.gid_pn_req) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci adapter->pool.scsi_req = 22062306a36Sopenharmony_ci mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); 22162306a36Sopenharmony_ci if (!adapter->pool.scsi_req) 22262306a36Sopenharmony_ci return -ENOMEM; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci adapter->pool.scsi_abort = 22562306a36Sopenharmony_ci mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); 22662306a36Sopenharmony_ci if (!adapter->pool.scsi_abort) 22762306a36Sopenharmony_ci return -ENOMEM; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci adapter->pool.status_read_req = 23062306a36Sopenharmony_ci mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM, 23162306a36Sopenharmony_ci sizeof(struct zfcp_fsf_req)); 23262306a36Sopenharmony_ci if (!adapter->pool.status_read_req) 23362306a36Sopenharmony_ci return -ENOMEM; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci adapter->pool.qtcb_pool = 23662306a36Sopenharmony_ci mempool_create_slab_pool(4, zfcp_fsf_qtcb_cache); 23762306a36Sopenharmony_ci if (!adapter->pool.qtcb_pool) 23862306a36Sopenharmony_ci return -ENOMEM; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct fsf_status_read_buffer) > PAGE_SIZE); 24162306a36Sopenharmony_ci adapter->pool.sr_data = 24262306a36Sopenharmony_ci mempool_create_page_pool(FSF_STATUS_READS_RECOM, 0); 24362306a36Sopenharmony_ci if (!adapter->pool.sr_data) 24462306a36Sopenharmony_ci return -ENOMEM; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci adapter->pool.gid_pn = 24762306a36Sopenharmony_ci mempool_create_slab_pool(1, zfcp_fc_req_cache); 24862306a36Sopenharmony_ci if (!adapter->pool.gid_pn) 24962306a36Sopenharmony_ci return -ENOMEM; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci mempool_destroy(adapter->pool.erp_req); 25762306a36Sopenharmony_ci mempool_destroy(adapter->pool.scsi_req); 25862306a36Sopenharmony_ci mempool_destroy(adapter->pool.scsi_abort); 25962306a36Sopenharmony_ci mempool_destroy(adapter->pool.qtcb_pool); 26062306a36Sopenharmony_ci mempool_destroy(adapter->pool.status_read_req); 26162306a36Sopenharmony_ci mempool_destroy(adapter->pool.sr_data); 26262306a36Sopenharmony_ci mempool_destroy(adapter->pool.gid_pn); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * zfcp_status_read_refill - refill the long running status_read_requests 26762306a36Sopenharmony_ci * @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * Return: 27062306a36Sopenharmony_ci * * 0 on success meaning at least one status read is pending 27162306a36Sopenharmony_ci * * 1 if posting failed and not a single status read buffer is pending, 27262306a36Sopenharmony_ci * also triggers adapter reopen recovery 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ciint zfcp_status_read_refill(struct zfcp_adapter *adapter) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci while (atomic_add_unless(&adapter->stat_miss, -1, 0)) 27762306a36Sopenharmony_ci if (zfcp_fsf_status_read(adapter->qdio)) { 27862306a36Sopenharmony_ci atomic_inc(&adapter->stat_miss); /* undo add -1 */ 27962306a36Sopenharmony_ci if (atomic_read(&adapter->stat_miss) >= 28062306a36Sopenharmony_ci adapter->stat_read_buf_num) { 28162306a36Sopenharmony_ci zfcp_erp_adapter_reopen(adapter, 0, "axsref1"); 28262306a36Sopenharmony_ci return 1; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void _zfcp_status_read_scheduler(struct work_struct *work) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci zfcp_status_read_refill(container_of(work, struct zfcp_adapter, 29262306a36Sopenharmony_ci stat_work)); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void zfcp_version_change_lost_work(struct work_struct *work) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, 29862306a36Sopenharmony_ci version_change_lost_work); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci zfcp_fsf_exchange_config_data_sync(adapter->qdio, NULL); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void zfcp_print_sl(struct seq_file *m, struct service_level *sl) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct zfcp_adapter *adapter = 30662306a36Sopenharmony_ci container_of(sl, struct zfcp_adapter, service_level); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci seq_printf(m, "zfcp: %s microcode level %x\n", 30962306a36Sopenharmony_ci dev_name(&adapter->ccw_device->dev), 31062306a36Sopenharmony_ci adapter->fsf_lic_version); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci char name[TASK_COMM_LEN]; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci snprintf(name, sizeof(name), "zfcp_q_%s", 31862306a36Sopenharmony_ci dev_name(&adapter->ccw_device->dev)); 31962306a36Sopenharmony_ci adapter->work_queue = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (adapter->work_queue) 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci return -ENOMEM; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci if (adapter->work_queue) 32962306a36Sopenharmony_ci destroy_workqueue(adapter->work_queue); 33062306a36Sopenharmony_ci adapter->work_queue = NULL; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * zfcp_adapter_enqueue - enqueue a new adapter to the list 33662306a36Sopenharmony_ci * @ccw_device: pointer to the struct cc_device 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Returns: struct zfcp_adapter* 33962306a36Sopenharmony_ci * Enqueues an adapter at the end of the adapter list in the driver data. 34062306a36Sopenharmony_ci * All adapter internal structures are set up. 34162306a36Sopenharmony_ci * Proc-fs entries are also created. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistruct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct zfcp_adapter *adapter; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!get_device(&ccw_device->dev)) 34862306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); 35162306a36Sopenharmony_ci if (!adapter) { 35262306a36Sopenharmony_ci put_device(&ccw_device->dev); 35362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci kref_init(&adapter->ref); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ccw_device->handler = NULL; 35962306a36Sopenharmony_ci adapter->ccw_device = ccw_device; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); 36262306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports); 36362306a36Sopenharmony_ci INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); 36462306a36Sopenharmony_ci INIT_WORK(&adapter->version_change_lost_work, 36562306a36Sopenharmony_ci zfcp_version_change_lost_work); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci adapter->next_port_scan = jiffies; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci adapter->erp_action.adapter = adapter; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (zfcp_diag_adapter_setup(adapter)) 37262306a36Sopenharmony_ci goto failed; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (zfcp_qdio_setup(adapter)) 37562306a36Sopenharmony_ci goto failed; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (zfcp_allocate_low_mem_buffers(adapter)) 37862306a36Sopenharmony_ci goto failed; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci adapter->req_list = zfcp_reqlist_alloc(); 38162306a36Sopenharmony_ci if (!adapter->req_list) 38262306a36Sopenharmony_ci goto failed; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (zfcp_dbf_adapter_register(adapter)) 38562306a36Sopenharmony_ci goto failed; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (zfcp_setup_adapter_work_queue(adapter)) 38862306a36Sopenharmony_ci goto failed; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (zfcp_fc_gs_setup(adapter)) 39162306a36Sopenharmony_ci goto failed; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci rwlock_init(&adapter->port_list_lock); 39462306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->port_list); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->events.list); 39762306a36Sopenharmony_ci INIT_WORK(&adapter->events.work, zfcp_fc_post_event); 39862306a36Sopenharmony_ci spin_lock_init(&adapter->events.list_lock); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci init_waitqueue_head(&adapter->erp_ready_wq); 40162306a36Sopenharmony_ci init_waitqueue_head(&adapter->erp_done_wqh); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->erp_ready_head); 40462306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->erp_running_head); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci rwlock_init(&adapter->erp_lock); 40762306a36Sopenharmony_ci rwlock_init(&adapter->abort_lock); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (zfcp_erp_thread_setup(adapter)) 41062306a36Sopenharmony_ci goto failed; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci adapter->service_level.seq_print = zfcp_print_sl; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci dev_set_drvdata(&ccw_device->dev, adapter); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (device_add_groups(&ccw_device->dev, zfcp_sysfs_adapter_attr_groups)) 41762306a36Sopenharmony_ci goto err_sysfs; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* report size limit per scatter-gather segment */ 42062306a36Sopenharmony_ci adapter->ccw_device->dev.dma_parms = &adapter->dma_parms; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci adapter->stat_read_buf_num = FSF_STATUS_READS_RECOM; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return adapter; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cierr_sysfs: 42762306a36Sopenharmony_cifailed: 42862306a36Sopenharmony_ci /* TODO: make this more fine-granular */ 42962306a36Sopenharmony_ci cancel_delayed_work_sync(&adapter->scan_work); 43062306a36Sopenharmony_ci cancel_work_sync(&adapter->stat_work); 43162306a36Sopenharmony_ci cancel_work_sync(&adapter->ns_up_work); 43262306a36Sopenharmony_ci cancel_work_sync(&adapter->version_change_lost_work); 43362306a36Sopenharmony_ci zfcp_destroy_adapter_work_queue(adapter); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci zfcp_fc_wka_ports_force_offline(adapter->gs); 43662306a36Sopenharmony_ci zfcp_scsi_adapter_unregister(adapter); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci zfcp_erp_thread_kill(adapter); 43962306a36Sopenharmony_ci zfcp_dbf_adapter_unregister(adapter); 44062306a36Sopenharmony_ci zfcp_qdio_destroy(adapter->qdio); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci zfcp_ccw_adapter_put(adapter); /* final put to release */ 44362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_civoid zfcp_adapter_unregister(struct zfcp_adapter *adapter) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct ccw_device *cdev = adapter->ccw_device; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci cancel_delayed_work_sync(&adapter->scan_work); 45162306a36Sopenharmony_ci cancel_work_sync(&adapter->stat_work); 45262306a36Sopenharmony_ci cancel_work_sync(&adapter->ns_up_work); 45362306a36Sopenharmony_ci cancel_work_sync(&adapter->version_change_lost_work); 45462306a36Sopenharmony_ci zfcp_destroy_adapter_work_queue(adapter); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci zfcp_fc_wka_ports_force_offline(adapter->gs); 45762306a36Sopenharmony_ci zfcp_scsi_adapter_unregister(adapter); 45862306a36Sopenharmony_ci device_remove_groups(&cdev->dev, zfcp_sysfs_adapter_attr_groups); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci zfcp_erp_thread_kill(adapter); 46162306a36Sopenharmony_ci zfcp_dbf_adapter_unregister(adapter); 46262306a36Sopenharmony_ci zfcp_qdio_destroy(adapter->qdio); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci zfcp_ccw_adapter_put(adapter); /* final put to release */ 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/** 46862306a36Sopenharmony_ci * zfcp_adapter_release - remove the adapter from the resource list 46962306a36Sopenharmony_ci * @ref: pointer to struct kref 47062306a36Sopenharmony_ci * locks: adapter list write lock is assumed to be held by caller 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_civoid zfcp_adapter_release(struct kref *ref) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, 47562306a36Sopenharmony_ci ref); 47662306a36Sopenharmony_ci struct ccw_device *cdev = adapter->ccw_device; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dev_set_drvdata(&adapter->ccw_device->dev, NULL); 47962306a36Sopenharmony_ci zfcp_fc_gs_destroy(adapter); 48062306a36Sopenharmony_ci zfcp_free_low_mem_buffers(adapter); 48162306a36Sopenharmony_ci zfcp_diag_adapter_free(adapter); 48262306a36Sopenharmony_ci kfree(adapter->req_list); 48362306a36Sopenharmony_ci kfree(adapter->fc_stats); 48462306a36Sopenharmony_ci kfree(adapter->stats_reset_data); 48562306a36Sopenharmony_ci kfree(adapter); 48662306a36Sopenharmony_ci put_device(&cdev->dev); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void zfcp_port_release(struct device *dev) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci zfcp_ccw_adapter_put(port->adapter); 49462306a36Sopenharmony_ci kfree(port); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/** 49862306a36Sopenharmony_ci * zfcp_port_enqueue - enqueue port to port list of adapter 49962306a36Sopenharmony_ci * @adapter: adapter where remote port is added 50062306a36Sopenharmony_ci * @wwpn: WWPN of the remote port to be enqueued 50162306a36Sopenharmony_ci * @status: initial status for the port 50262306a36Sopenharmony_ci * @d_id: destination id of the remote port to be enqueued 50362306a36Sopenharmony_ci * Returns: pointer to enqueued port on success, ERR_PTR on error 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * All port internal structures are set up and the sysfs entry is generated. 50662306a36Sopenharmony_ci * d_id is used to enqueue ports with a well known address like the Directory 50762306a36Sopenharmony_ci * Service for nameserver lookup. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistruct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, 51062306a36Sopenharmony_ci u32 status, u32 d_id) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct zfcp_port *port; 51362306a36Sopenharmony_ci int retval = -ENOMEM; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci kref_get(&adapter->ref); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci port = zfcp_get_port_by_wwpn(adapter, wwpn); 51862306a36Sopenharmony_ci if (port) { 51962306a36Sopenharmony_ci put_device(&port->dev); 52062306a36Sopenharmony_ci retval = -EEXIST; 52162306a36Sopenharmony_ci goto err_put; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); 52562306a36Sopenharmony_ci if (!port) 52662306a36Sopenharmony_ci goto err_put; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rwlock_init(&port->unit_list_lock); 52962306a36Sopenharmony_ci INIT_LIST_HEAD(&port->unit_list); 53062306a36Sopenharmony_ci atomic_set(&port->units, 0); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); 53362306a36Sopenharmony_ci INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); 53462306a36Sopenharmony_ci INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci port->adapter = adapter; 53762306a36Sopenharmony_ci port->d_id = d_id; 53862306a36Sopenharmony_ci port->wwpn = wwpn; 53962306a36Sopenharmony_ci port->rport_task = RPORT_NONE; 54062306a36Sopenharmony_ci port->dev.parent = &adapter->ccw_device->dev; 54162306a36Sopenharmony_ci port->dev.groups = zfcp_port_attr_groups; 54262306a36Sopenharmony_ci port->dev.release = zfcp_port_release; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci port->erp_action.adapter = adapter; 54562306a36Sopenharmony_ci port->erp_action.port = port; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) { 54862306a36Sopenharmony_ci kfree(port); 54962306a36Sopenharmony_ci goto err_put; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci retval = -EINVAL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (device_register(&port->dev)) { 55462306a36Sopenharmony_ci put_device(&port->dev); 55562306a36Sopenharmony_ci goto err_out; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci write_lock_irq(&adapter->port_list_lock); 55962306a36Sopenharmony_ci list_add_tail(&port->list, &adapter->port_list); 56062306a36Sopenharmony_ci write_unlock_irq(&adapter->port_list_lock); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci atomic_or(status | ZFCP_STATUS_COMMON_RUNNING, &port->status); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return port; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cierr_put: 56762306a36Sopenharmony_ci zfcp_ccw_adapter_put(adapter); 56862306a36Sopenharmony_cierr_out: 56962306a36Sopenharmony_ci return ERR_PTR(retval); 57062306a36Sopenharmony_ci} 571