From ecf0c7721b104c0ce9c8ca534c911f6310cf92a8 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 24 Nov 2009 16:53:58 +0100 Subject: [SCSI] zfcp: Replace global config_lock with local list locks The global config_lock was used to protect the configuration organized in independent lists. It is not necessary to have a lock on driver level for this purpose. This patch replaces the global config_lock with a set of local list locks. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 80 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 34 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 2889e5f2dfd..883e13948ac 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -187,7 +187,6 @@ static int __init zfcp_module_init(void) goto out_gid_cache; mutex_init(&zfcp_data.config_mutex); - rwlock_init(&zfcp_data.config_lock); zfcp_data.scsi_transport_template = fc_attach_transport(&zfcp_transport_functions); @@ -238,12 +237,18 @@ module_init(zfcp_module_init); */ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) { + unsigned long flags; struct zfcp_unit *unit; - list_for_each_entry(unit, &port->unit_list_head, list) + read_lock_irqsave(&port->unit_list_lock, flags); + list_for_each_entry(unit, &port->unit_list, list) if ((unit->fcp_lun == fcp_lun) && - !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) - return unit; + !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) { + zfcp_unit_get(unit); + read_unlock_irqrestore(&port->unit_list_lock, flags); + return unit; + } + read_unlock_irqrestore(&port->unit_list_lock, flags); return NULL; } @@ -257,12 +262,18 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, u64 wwpn) { + unsigned long flags; struct zfcp_port *port; - list_for_each_entry(port, &adapter->port_list_head, list) + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) if ((port->wwpn == wwpn) && - !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) + !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) { + zfcp_port_get(port); + read_unlock_irqrestore(&adapter->port_list_lock, flags); return port; + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); return NULL; } @@ -284,12 +295,11 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) { struct zfcp_unit *unit; - read_lock_irq(&zfcp_data.config_lock); - if (zfcp_get_unit_by_lun(port, fcp_lun)) { - read_unlock_irq(&zfcp_data.config_lock); + unit = zfcp_get_unit_by_lun(port, fcp_lun); + if (unit) { + zfcp_unit_put(unit); return ERR_PTR(-EINVAL); } - read_unlock_irq(&zfcp_data.config_lock); unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); if (!unit) @@ -335,13 +345,13 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) zfcp_unit_get(unit); - write_lock_irq(&zfcp_data.config_lock); - list_add_tail(&unit->list, &port->unit_list_head); + write_lock_irq(&port->unit_list_lock); + list_add_tail(&unit->list, &port->unit_list); + write_unlock_irq(&port->unit_list_lock); + atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); - write_unlock_irq(&zfcp_data.config_lock); - zfcp_port_get(port); return unit; @@ -356,11 +366,11 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) */ void zfcp_unit_dequeue(struct zfcp_unit *unit) { + struct zfcp_port *port = unit->port; + wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0); - write_lock_irq(&zfcp_data.config_lock); - list_del(&unit->list); - write_unlock_irq(&zfcp_data.config_lock); - zfcp_port_put(unit->port); + list_del(&unit->list); /* no list locking required */ + zfcp_port_put(port); sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs); device_unregister(&unit->sysfs_device); } @@ -539,11 +549,13 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) if (zfcp_fc_gs_setup(adapter)) goto generic_services_failed; + rwlock_init(&adapter->port_list_lock); + INIT_LIST_HEAD(&adapter->port_list); + init_waitqueue_head(&adapter->remove_wq); init_waitqueue_head(&adapter->erp_ready_wq); init_waitqueue_head(&adapter->erp_done_wqh); - INIT_LIST_HEAD(&adapter->port_list_head); INIT_LIST_HEAD(&adapter->erp_ready_head); INIT_LIST_HEAD(&adapter->erp_running_head); @@ -650,19 +662,20 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, { struct zfcp_port *port; - read_lock_irq(&zfcp_data.config_lock); - if (zfcp_get_port_by_wwpn(adapter, wwpn)) { - read_unlock_irq(&zfcp_data.config_lock); - return ERR_PTR(-EINVAL); + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (port) { + zfcp_port_put(port); + return ERR_PTR(-EEXIST); } - read_unlock_irq(&zfcp_data.config_lock); port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) return ERR_PTR(-ENOMEM); + rwlock_init(&port->unit_list_lock); + INIT_LIST_HEAD(&port->unit_list); + init_waitqueue_head(&port->remove_wq); - INIT_LIST_HEAD(&port->unit_list_head); INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); @@ -698,13 +711,13 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, zfcp_port_get(port); - write_lock_irq(&zfcp_data.config_lock); - list_add_tail(&port->list, &adapter->port_list_head); + write_lock_irq(&adapter->port_list_lock); + list_add_tail(&port->list, &adapter->port_list); + write_unlock_irq(&adapter->port_list_lock); + atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); - write_unlock_irq(&zfcp_data.config_lock); - zfcp_adapter_get(adapter); return port; } @@ -715,12 +728,11 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, */ void zfcp_port_dequeue(struct zfcp_port *port) { - write_lock_irq(&zfcp_data.config_lock); - list_del(&port->list); - write_unlock_irq(&zfcp_data.config_lock); + struct zfcp_adapter *adapter = port->adapter; + + list_del(&port->list); /* no list locking required here */ wait_event(port->remove_wq, atomic_read(&port->refcount) == 0); - cancel_work_sync(&port->rport_work); /* usually not necessary */ - zfcp_adapter_put(port->adapter); + zfcp_adapter_put(adapter); sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs); device_unregister(&port->sysfs_device); } -- cgit v1.2.3 From f3450c7b917201bb49d67032e9f60d5125675d6a Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 24 Nov 2009 16:53:59 +0100 Subject: [SCSI] zfcp: Replace local reference counting with common kref Replace the local reference counting by already available mechanisms offered by kref. Where possible existing device structures were used, including the same functionality. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 231 ++++++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 123 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 883e13948ac..8492ceac140 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -96,13 +96,12 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) adapter = dev_get_drvdata(&ccwdev->dev); if (!adapter) goto out_unlock; - zfcp_adapter_get(adapter); + kref_get(&adapter->ref); port = zfcp_get_port_by_wwpn(adapter, wwpn); if (!port) goto out_port; - zfcp_port_get(port); unit = zfcp_unit_enqueue(port, lun); if (IS_ERR(unit)) goto out_unit; @@ -113,11 +112,10 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) flush_work(&unit->scsi_work); mutex_lock(&zfcp_data.config_mutex); - zfcp_unit_put(unit); out_unit: - zfcp_port_put(port); + put_device(&port->sysfs_device); out_port: - zfcp_adapter_put(adapter); + kref_put(&adapter->ref, zfcp_adapter_release); out_unlock: mutex_unlock(&zfcp_data.config_mutex); out_ccwdev: @@ -244,7 +242,7 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) list_for_each_entry(unit, &port->unit_list, list) if ((unit->fcp_lun == fcp_lun) && !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) { - zfcp_unit_get(unit); + get_device(&unit->sysfs_device); read_unlock_irqrestore(&port->unit_list_lock, flags); return unit; } @@ -269,7 +267,7 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, list_for_each_entry(port, &adapter->port_list, list) if ((port->wwpn == wwpn) && !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) { - zfcp_port_get(port); + get_device(&port->sysfs_device); read_unlock_irqrestore(&adapter->port_list_lock, flags); return port; } @@ -277,9 +275,20 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, return NULL; } -static void zfcp_sysfs_unit_release(struct device *dev) +/** + * zfcp_unit_release - dequeue unit + * @dev: pointer to device + * + * waits until all work is done on unit and removes it then from the unit->list + * of the associated port. + */ +static void zfcp_unit_release(struct device *dev) { - kfree(container_of(dev, struct zfcp_unit, sysfs_device)); + struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, + sysfs_device); + + put_device(&unit->port->sysfs_device); + kfree(unit); } /** @@ -294,36 +303,39 @@ static void zfcp_sysfs_unit_release(struct device *dev) struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) { struct zfcp_unit *unit; + int retval = -ENOMEM; + + get_device(&port->sysfs_device); unit = zfcp_get_unit_by_lun(port, fcp_lun); if (unit) { - zfcp_unit_put(unit); - return ERR_PTR(-EINVAL); + put_device(&unit->sysfs_device); + retval = -EEXIST; + goto err_out; } unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); if (!unit) - return ERR_PTR(-ENOMEM); - - atomic_set(&unit->refcount, 0); - init_waitqueue_head(&unit->remove_wq); - INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); + goto err_out; unit->port = port; unit->fcp_lun = fcp_lun; + unit->sysfs_device.parent = &port->sysfs_device; + unit->sysfs_device.release = zfcp_unit_release; if (dev_set_name(&unit->sysfs_device, "0x%016llx", (unsigned long long) fcp_lun)) { kfree(unit); - return ERR_PTR(-ENOMEM); + goto err_out; } - unit->sysfs_device.parent = &port->sysfs_device; - unit->sysfs_device.release = zfcp_sysfs_unit_release; dev_set_drvdata(&unit->sysfs_device, unit); + retval = -EINVAL; /* mark unit unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); + INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); + spin_lock_init(&unit->latencies.lock); unit->latencies.write.channel.min = 0xFFFFFFFF; unit->latencies.write.fabric.min = 0xFFFFFFFF; @@ -334,16 +346,12 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) if (device_register(&unit->sysfs_device)) { put_device(&unit->sysfs_device); - return ERR_PTR(-EINVAL); + goto err_out; } if (sysfs_create_group(&unit->sysfs_device.kobj, - &zfcp_sysfs_unit_attrs)) { - device_unregister(&unit->sysfs_device); - return ERR_PTR(-EINVAL); - } - - zfcp_unit_get(unit); + &zfcp_sysfs_unit_attrs)) + goto err_out_put; write_lock_irq(&port->unit_list_lock); list_add_tail(&unit->list, &port->unit_list); @@ -352,27 +360,13 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); - zfcp_port_get(port); - return unit; -} - -/** - * zfcp_unit_dequeue - dequeue unit - * @unit: pointer to zfcp_unit - * - * waits until all work is done on unit and removes it then from the unit->list - * of the associated port. - */ -void zfcp_unit_dequeue(struct zfcp_unit *unit) -{ - struct zfcp_port *port = unit->port; - wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0); - list_del(&unit->list); /* no list locking required */ - zfcp_port_put(port); - sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs); +err_out_put: device_unregister(&unit->sysfs_device); +err_out: + put_device(&port->sysfs_device); + return ERR_PTR(retval); } static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) @@ -518,41 +512,44 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) { struct zfcp_adapter *adapter; - /* - * Note: It is safe to release the list_lock, as any list changes - * are protected by the config_mutex, which must be held to get here - */ + if (!get_device(&ccw_device->dev)) + return -ENODEV; adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); - if (!adapter) + if (!adapter) { + put_device(&ccw_device->dev); return -ENOMEM; + } + + kref_init(&adapter->ref); ccw_device->handler = NULL; adapter->ccw_device = ccw_device; - atomic_set(&adapter->refcount, 0); + + INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); + INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later); if (zfcp_qdio_setup(adapter)) - goto qdio_failed; + goto failed; if (zfcp_allocate_low_mem_buffers(adapter)) - goto low_mem_buffers_failed; + goto failed; if (zfcp_reqlist_alloc(adapter)) - goto low_mem_buffers_failed; + goto failed; if (zfcp_dbf_adapter_register(adapter)) - goto debug_register_failed; + goto failed; if (zfcp_setup_adapter_work_queue(adapter)) - goto work_queue_failed; + goto failed; if (zfcp_fc_gs_setup(adapter)) - goto generic_services_failed; + goto failed; rwlock_init(&adapter->port_list_lock); INIT_LIST_HEAD(&adapter->port_list); - init_waitqueue_head(&adapter->remove_wq); init_waitqueue_head(&adapter->erp_ready_wq); init_waitqueue_head(&adapter->erp_done_wqh); @@ -565,10 +562,7 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) rwlock_init(&adapter->abort_lock); if (zfcp_erp_thread_setup(adapter)) - goto erp_thread_failed; - - INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); - INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later); + goto failed; adapter->service_level.seq_print = zfcp_print_sl; @@ -579,54 +573,37 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) if (sysfs_create_group(&ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs)) - goto sysfs_failed; + goto failed; atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); if (!zfcp_adapter_scsi_register(adapter)) return 0; -sysfs_failed: - zfcp_erp_thread_kill(adapter); -erp_thread_failed: - zfcp_fc_gs_destroy(adapter); -generic_services_failed: - zfcp_destroy_adapter_work_queue(adapter); -work_queue_failed: - zfcp_dbf_adapter_unregister(adapter->dbf); -debug_register_failed: - dev_set_drvdata(&ccw_device->dev, NULL); - kfree(adapter->req_list); -low_mem_buffers_failed: - zfcp_free_low_mem_buffers(adapter); -qdio_failed: - zfcp_qdio_destroy(adapter->qdio); - kfree(adapter); +failed: + kref_put(&adapter->ref, zfcp_adapter_release); return -ENOMEM; } /** - * zfcp_adapter_dequeue - remove the adapter from the resource list - * @adapter: pointer to struct zfcp_adapter which should be removed + * zfcp_adapter_release - remove the adapter from the resource list + * @ref: pointer to struct kref * locks: adapter list write lock is assumed to be held by caller */ -void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) +void zfcp_adapter_release(struct kref *ref) { - int retval = 0; - unsigned long flags; + struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, + ref); + struct ccw_device *ccw_device = adapter->ccw_device; cancel_work_sync(&adapter->stat_work); + zfcp_fc_wka_ports_force_offline(adapter->gs); - sysfs_remove_group(&adapter->ccw_device->dev.kobj, - &zfcp_sysfs_adapter_attrs); - dev_set_drvdata(&adapter->ccw_device->dev, NULL); - /* sanity check: no pending FSF requests */ - spin_lock_irqsave(&adapter->req_list_lock, flags); - retval = zfcp_reqlist_isempty(adapter); - spin_unlock_irqrestore(&adapter->req_list_lock, flags); - if (!retval) - return; + sysfs_remove_group(&ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs); + + dev_set_drvdata(&ccw_device->dev, NULL); + dev_set_drvdata(&adapter->ccw_device->dev, NULL); zfcp_fc_gs_destroy(adapter); zfcp_erp_thread_kill(adapter); zfcp_destroy_adapter_work_queue(adapter); @@ -637,11 +614,30 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); kfree(adapter); + put_device(&ccw_device->dev); +} + +/** + * zfcp_device_unregister - remove port, unit from system + * @dev: reference to device which is to be removed + * @grp: related reference to attribute group + * + * Helper function to unregister port, unit from system + */ +void zfcp_device_unregister(struct device *dev, + const struct attribute_group *grp) +{ + sysfs_remove_group(&dev->kobj, grp); + device_unregister(dev); } -static void zfcp_sysfs_port_release(struct device *dev) +static void zfcp_port_release(struct device *dev) { - kfree(container_of(dev, struct zfcp_port, sysfs_device)); + struct zfcp_port *port = container_of(dev, struct zfcp_port, + sysfs_device); + + kref_put(&port->adapter->ref, zfcp_adapter_release); + kfree(port); } /** @@ -661,21 +657,24 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, u32 status, u32 d_id) { struct zfcp_port *port; + int retval = -ENOMEM; + + kref_get(&adapter->ref); port = zfcp_get_port_by_wwpn(adapter, wwpn); if (port) { - zfcp_port_put(port); - return ERR_PTR(-EEXIST); + put_device(&port->sysfs_device); + retval = -EEXIST; + goto err_out; } port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) - return ERR_PTR(-ENOMEM); + goto err_out; rwlock_init(&port->unit_list_lock); INIT_LIST_HEAD(&port->unit_list); - init_waitqueue_head(&port->remove_wq); INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); @@ -684,32 +683,28 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, port->d_id = d_id; port->wwpn = wwpn; port->rport_task = RPORT_NONE; + port->sysfs_device.parent = &adapter->ccw_device->dev; + port->sysfs_device.release = zfcp_port_release; /* mark port unusable as long as sysfs registration is not complete */ atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); - atomic_set(&port->refcount, 0); if (dev_set_name(&port->sysfs_device, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); - return ERR_PTR(-ENOMEM); + goto err_out; } - port->sysfs_device.parent = &adapter->ccw_device->dev; - port->sysfs_device.release = zfcp_sysfs_port_release; dev_set_drvdata(&port->sysfs_device, port); + retval = -EINVAL; if (device_register(&port->sysfs_device)) { put_device(&port->sysfs_device); - return ERR_PTR(-EINVAL); + goto err_out; } if (sysfs_create_group(&port->sysfs_device.kobj, - &zfcp_sysfs_port_attrs)) { - device_unregister(&port->sysfs_device); - return ERR_PTR(-EINVAL); - } - - zfcp_port_get(port); + &zfcp_sysfs_port_attrs)) + goto err_out_put; write_lock_irq(&adapter->port_list_lock); list_add_tail(&port->list, &adapter->port_list); @@ -718,23 +713,13 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); - zfcp_adapter_get(adapter); return port; -} -/** - * zfcp_port_dequeue - dequeues a port from the port list of the adapter - * @port: pointer to struct zfcp_port which should be removed - */ -void zfcp_port_dequeue(struct zfcp_port *port) -{ - struct zfcp_adapter *adapter = port->adapter; - - list_del(&port->list); /* no list locking required here */ - wait_event(port->remove_wq, atomic_read(&port->refcount) == 0); - zfcp_adapter_put(adapter); - sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs); +err_out_put: device_unregister(&port->sysfs_device); +err_out: + kref_put(&adapter->ref, zfcp_adapter_release); + return ERR_PTR(retval); } /** -- cgit v1.2.3 From de3dc57214a1466034ecc4d4ffb10331d34c09a3 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 24 Nov 2009 16:54:00 +0100 Subject: [SCSI] zfcp: Remove global config_mutex The global config_mutex was required for the serialization of a configuration change within the zfcp driver. This global locking is now obsolete and can be removed. The requirement of serializing the access to a zfcp_adapter reference via a ccw_device is realized wth a static spinlock. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 86 +++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 46 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8492ceac140..ed31bd0ff3f 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -80,23 +80,21 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { - struct ccw_device *ccwdev; + struct ccw_device *cdev; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; - ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); - if (!ccwdev) + cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!cdev) return; - if (ccw_device_set_online(ccwdev)) - goto out_ccwdev; + if (ccw_device_set_online(cdev)) + goto out_ccw_device; - mutex_lock(&zfcp_data.config_mutex); - adapter = dev_get_drvdata(&ccwdev->dev); + adapter = zfcp_ccw_adapter_by_cdev(cdev); if (!adapter) - goto out_unlock; - kref_get(&adapter->ref); + goto out_ccw_device; port = zfcp_get_port_by_wwpn(adapter, wwpn); if (!port) @@ -105,21 +103,17 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) unit = zfcp_unit_enqueue(port, lun); if (IS_ERR(unit)) goto out_unit; - mutex_unlock(&zfcp_data.config_mutex); zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL); zfcp_erp_wait(adapter); flush_work(&unit->scsi_work); - mutex_lock(&zfcp_data.config_mutex); out_unit: put_device(&port->sysfs_device); out_port: - kref_put(&adapter->ref, zfcp_adapter_release); -out_unlock: - mutex_unlock(&zfcp_data.config_mutex); -out_ccwdev: - put_device(&ccwdev->dev); + zfcp_ccw_adapter_put(adapter); +out_ccw_device: + put_device(&cdev->dev); return; } @@ -184,8 +178,6 @@ static int __init zfcp_module_init(void) if (!zfcp_data.gid_pn_cache) goto out_gid_cache; - mutex_init(&zfcp_data.config_mutex); - zfcp_data.scsi_transport_template = fc_attach_transport(&zfcp_transport_functions); if (!zfcp_data.scsi_transport_template) @@ -296,7 +288,6 @@ static void zfcp_unit_release(struct device *dev) * @port: pointer to port where unit is added * @fcp_lun: FCP LUN of unit to be enqueued * Returns: pointer to enqueued unit on success, ERR_PTR on error - * Locks: config_mutex must be held to serialize changes to the unit list * * Sets up some unit internal structures and creates sysfs entry. */ @@ -371,7 +362,6 @@ err_out: static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) { - /* must only be called with zfcp_data.config_mutex taken */ adapter->pool.erp_req = mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); if (!adapter->pool.erp_req) @@ -419,7 +409,6 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { - /* zfcp_data.config_mutex must be held */ if (adapter->pool.erp_req) mempool_destroy(adapter->pool.erp_req); if (adapter->pool.scsi_req) @@ -501,24 +490,22 @@ static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter) * zfcp_adapter_enqueue - enqueue a new adapter to the list * @ccw_device: pointer to the struct cc_device * - * Returns: 0 if a new adapter was successfully enqueued - * -ENOMEM if alloc failed + * Returns: struct zfcp_adapter* * Enqueues an adapter at the end of the adapter list in the driver data. * All adapter internal structures are set up. * Proc-fs entries are also created. - * locks: config_mutex must be held to serialize changes to the adapter list */ -int zfcp_adapter_enqueue(struct ccw_device *ccw_device) +struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) { struct zfcp_adapter *adapter; if (!get_device(&ccw_device->dev)) - return -ENODEV; + return ERR_PTR(-ENODEV); adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); if (!adapter) { put_device(&ccw_device->dev); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } kref_init(&adapter->ref); @@ -578,11 +565,30 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); if (!zfcp_adapter_scsi_register(adapter)) - return 0; + return adapter; failed: - kref_put(&adapter->ref, zfcp_adapter_release); - return -ENOMEM; + zfcp_adapter_unregister(adapter); + return ERR_PTR(-ENOMEM); +} + +void zfcp_adapter_unregister(struct zfcp_adapter *adapter) +{ + struct ccw_device *cdev = adapter->ccw_device; + + cancel_work_sync(&adapter->scan_work); + cancel_work_sync(&adapter->stat_work); + zfcp_destroy_adapter_work_queue(adapter); + + zfcp_fc_wka_ports_force_offline(adapter->gs); + zfcp_adapter_scsi_unregister(adapter); + sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); + + zfcp_erp_thread_kill(adapter); + zfcp_dbf_adapter_unregister(adapter->dbf); + zfcp_qdio_destroy(adapter->qdio); + + zfcp_ccw_adapter_put(adapter); /* final put to release */ } /** @@ -594,27 +600,16 @@ void zfcp_adapter_release(struct kref *ref) { struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, ref); - struct ccw_device *ccw_device = adapter->ccw_device; - - cancel_work_sync(&adapter->stat_work); - - zfcp_fc_wka_ports_force_offline(adapter->gs); - sysfs_remove_group(&ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs); - - dev_set_drvdata(&ccw_device->dev, NULL); + struct ccw_device *cdev = adapter->ccw_device; dev_set_drvdata(&adapter->ccw_device->dev, NULL); zfcp_fc_gs_destroy(adapter); - zfcp_erp_thread_kill(adapter); - zfcp_destroy_adapter_work_queue(adapter); - zfcp_dbf_adapter_unregister(adapter->dbf); zfcp_free_low_mem_buffers(adapter); - zfcp_qdio_destroy(adapter->qdio); kfree(adapter->req_list); kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); kfree(adapter); - put_device(&ccw_device->dev); + put_device(&cdev->dev); } /** @@ -636,7 +631,7 @@ static void zfcp_port_release(struct device *dev) struct zfcp_port *port = container_of(dev, struct zfcp_port, sysfs_device); - kref_put(&port->adapter->ref, zfcp_adapter_release); + zfcp_ccw_adapter_put(port->adapter); kfree(port); } @@ -647,7 +642,6 @@ static void zfcp_port_release(struct device *dev) * @status: initial status for the port * @d_id: destination id of the remote port to be enqueued * Returns: pointer to enqueued port on success, ERR_PTR on error - * Locks: config_mutex must be held to serialize changes to the port list * * All port internal structures are set up and the sysfs entry is generated. * d_id is used to enqueue ports with a well known address like the Directory @@ -718,7 +712,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, err_out_put: device_unregister(&port->sysfs_device); err_out: - kref_put(&adapter->ref, zfcp_adapter_release); + zfcp_ccw_adapter_put(adapter); return ERR_PTR(retval); } -- cgit v1.2.3 From 25458eb791acf0e5e65183c5adb3918d8d71d756 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 24 Nov 2009 16:54:02 +0100 Subject: [SCSI] zfcp: Access ports and units with container_of in sysfs code When accessing port and unit attributes, use container_of instead of dev_get_drvdata. This eliminates some code checker warnings about aliased access of data structures. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index ed31bd0ff3f..baef2ec7482 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -319,7 +319,6 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) kfree(unit); goto err_out; } - dev_set_drvdata(&unit->sysfs_device, unit); retval = -EINVAL; /* mark unit unusable as long as sysfs registration is not complete */ @@ -688,7 +687,6 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, kfree(port); goto err_out; } - dev_set_drvdata(&port->sysfs_device, port); retval = -EINVAL; if (device_register(&port->sysfs_device)) { -- cgit v1.2.3 From c1fad4176464281e776022dee7d029144afbeb13 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 24 Nov 2009 16:54:04 +0100 Subject: [SCSI] zfcp: Implement module unloading With the reference counting for zfcp data structures, it is now possible to implement module unloading again. Module unloading requires to free all data structures in the module exit function. This is done by unregistering zfcp from s390 cio and the SCSI midlayer first in the module exit function. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index baef2ec7482..12de1ce9a92 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -189,7 +189,7 @@ static int __init zfcp_module_init(void) goto out_misc; } - retval = zfcp_ccw_register(); + retval = ccw_driver_register(&zfcp_ccw_driver); if (retval) { pr_err("The zfcp device driver could not register with " "the common I/O layer\n"); @@ -218,6 +218,19 @@ out: module_init(zfcp_module_init); +static void __exit zfcp_module_exit(void) +{ + ccw_driver_unregister(&zfcp_ccw_driver); + misc_deregister(&zfcp_cfdc_misc); + fc_release_transport(zfcp_data.scsi_transport_template); + kmem_cache_destroy(zfcp_data.gid_pn_cache); + kmem_cache_destroy(zfcp_data.sr_buffer_cache); + kmem_cache_destroy(zfcp_data.qtcb_cache); + kmem_cache_destroy(zfcp_data.gpn_ft_cache); +} + +module_exit(zfcp_module_exit); + /** * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN * @port: pointer to port to search for unit -- cgit v1.2.3 From 6b183334c23969d52d4d9f775da554480d05ca4d Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 24 Nov 2009 16:54:05 +0100 Subject: [SCSI] zfcp: Remove STATUS_COMMON_REMOVE flag as it is not required anymore The flag ZFCP_STATUS_COMMON_REMOVE was used to indicate that a resource is not ready to be used or about to be removed from the system. This is now better done by an improved list handling and therefore the additional indicator is not required anymore. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 12de1ce9a92..6b94f8d0609 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -245,9 +245,9 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) read_lock_irqsave(&port->unit_list_lock, flags); list_for_each_entry(unit, &port->unit_list, list) - if ((unit->fcp_lun == fcp_lun) && - !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) { - get_device(&unit->sysfs_device); + if (unit->fcp_lun == fcp_lun) { + if (!get_device(&unit->sysfs_device)) + unit = NULL; read_unlock_irqrestore(&port->unit_list_lock, flags); return unit; } @@ -270,9 +270,9 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, read_lock_irqsave(&adapter->port_list_lock, flags); list_for_each_entry(port, &adapter->port_list, list) - if ((port->wwpn == wwpn) && - !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) { - get_device(&port->sysfs_device); + if (port->wwpn == wwpn) { + if (!get_device(&port->sysfs_device)) + port = NULL; read_unlock_irqrestore(&adapter->port_list_lock, flags); return port; } @@ -334,9 +334,6 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) } retval = -EINVAL; - /* mark unit unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); - INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); spin_lock_init(&unit->latencies.lock); @@ -360,7 +357,6 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) list_add_tail(&unit->list, &port->unit_list); write_unlock_irq(&port->unit_list_lock); - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); return unit; @@ -565,17 +561,12 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->service_level.seq_print = zfcp_print_sl; - /* mark adapter unusable as long as sysfs registration is not complete */ - atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - dev_set_drvdata(&ccw_device->dev, adapter); if (sysfs_create_group(&ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs)) goto failed; - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - if (!zfcp_adapter_scsi_register(adapter)) return adapter; @@ -692,9 +683,6 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, port->sysfs_device.parent = &adapter->ccw_device->dev; port->sysfs_device.release = zfcp_port_release; - /* mark port unusable as long as sysfs registration is not complete */ - atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); - if (dev_set_name(&port->sysfs_device, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); @@ -715,8 +703,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, list_add_tail(&port->list, &adapter->port_list); write_unlock_irq(&adapter->port_list_lock); - atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); - atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); + atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status); return port; -- cgit v1.2.3 From 9eae07ef6bb5988163d8bb82cd952905db47b721 Mon Sep 17 00:00:00 2001 From: Swen Schillig Date: Tue, 24 Nov 2009 16:54:06 +0100 Subject: [SCSI] zfcp: Assign scheduled work to driver queue The port_scan work was scheduled to the work_queue provided by the kernel. This resulted on SMP systems to a likely situation that more than one scan_work were processed in parallel. This is not required and openes the possibility of race conditions between the removal of invalid ports and the enqueue of just scanned ports. This patch synchronizes the scan_work tasks by scheduling them to adapter local work_queue. Signed-off-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 6b94f8d0609..107d3f2b6e9 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -522,7 +522,7 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->ccw_device = ccw_device; INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); - INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later); + INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); if (zfcp_qdio_setup(adapter)) goto failed; -- cgit v1.2.3 From dbf5dfe9dbcecf159139eec25ad256738cbc3715 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 24 Nov 2009 16:54:10 +0100 Subject: [SCSI] zfcp: Use common code definitions for FC CT structs Use common code definitions for FC GPN_FT and GID_PN instead of inventing private ones. Move the private structs still required inside zfcp to zfcp_fc header file. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 107d3f2b6e9..58bb17732f5 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -31,6 +31,7 @@ #include #include #include "zfcp_ext.h" +#include "zfcp_fc.h" #define ZFCP_BUS_ID_SIZE 20 @@ -159,7 +160,7 @@ static int __init zfcp_module_init(void) int retval = -ENOMEM; zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn", - sizeof(struct ct_iu_gpn_ft_req)); + sizeof(struct zfcp_fc_gpn_ft_req)); if (!zfcp_data.gpn_ft_cache) goto out; @@ -174,7 +175,7 @@ static int __init zfcp_module_init(void) goto out_sr_cache; zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid", - sizeof(struct zfcp_gid_pn_data)); + sizeof(struct zfcp_fc_gid_pn)); if (!zfcp_data.gid_pn_cache) goto out_gid_cache; @@ -407,9 +408,9 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) if (!adapter->pool.status_read_data) return -ENOMEM; - adapter->pool.gid_pn_data = + adapter->pool.gid_pn = mempool_create_slab_pool(1, zfcp_data.gid_pn_cache); - if (!adapter->pool.gid_pn_data) + if (!adapter->pool.gid_pn) return -ENOMEM; return 0; @@ -429,8 +430,8 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) mempool_destroy(adapter->pool.status_read_req); if (adapter->pool.status_read_data) mempool_destroy(adapter->pool.status_read_data); - if (adapter->pool.gid_pn_data) - mempool_destroy(adapter->pool.gid_pn_data); + if (adapter->pool.gid_pn) + mempool_destroy(adapter->pool.gid_pn); } /** -- cgit v1.2.3 From ee744622c65cd66824e8dd1b9509e515c800de14 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 24 Nov 2009 16:54:14 +0100 Subject: [SCSI] zfcp: Improve ELS ADISC handling Introduce kmem_cache for ELS ADISC data to guarantee the required hardware alignment and free the allocated memory in case the send failes. Reviewed-by: Swen Schillig Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/s390/scsi/zfcp_aux.c') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 58bb17732f5..9d0c941b7d3 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -179,6 +179,11 @@ static int __init zfcp_module_init(void) if (!zfcp_data.gid_pn_cache) goto out_gid_cache; + zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc", + sizeof(struct zfcp_fc_els_adisc)); + if (!zfcp_data.adisc_cache) + goto out_adisc_cache; + zfcp_data.scsi_transport_template = fc_attach_transport(&zfcp_transport_functions); if (!zfcp_data.scsi_transport_template) @@ -206,6 +211,8 @@ out_ccw_register: out_misc: fc_release_transport(zfcp_data.scsi_transport_template); out_transport: + kmem_cache_destroy(zfcp_data.adisc_cache); +out_adisc_cache: kmem_cache_destroy(zfcp_data.gid_pn_cache); out_gid_cache: kmem_cache_destroy(zfcp_data.sr_buffer_cache); @@ -224,6 +231,7 @@ static void __exit zfcp_module_exit(void) ccw_driver_unregister(&zfcp_ccw_driver); misc_deregister(&zfcp_cfdc_misc); fc_release_transport(zfcp_data.scsi_transport_template); + kmem_cache_destroy(zfcp_data.adisc_cache); kmem_cache_destroy(zfcp_data.gid_pn_cache); kmem_cache_destroy(zfcp_data.sr_buffer_cache); kmem_cache_destroy(zfcp_data.qtcb_cache); -- cgit v1.2.3