So far, zfcp allocated all resources required for FCP
adapters/subchannels when the device was discovered in the ccw_probe
callback. If there are lots of unused FCP subchannels attached to a
system, this is a waste of resources. To alleviate this, defer the
resource allocation to the first call to ccw_set_online. To avoid
disruptions during possible following calls to ccw_set_offline and
then ccw_set_online, keep the adapter resources until the device is
finally being removed via ccw_remove. While doing this, also manage
the zfcp erp thread together with all other adapter resources in
zfcp_adapter_enqueue/dequeue.
Reviewed-by: Swen Schillig <swen@vnet.ibm.com>
Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
rwlock_init(&adapter->erp_lock);
rwlock_init(&adapter->abort_lock);
rwlock_init(&adapter->erp_lock);
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);
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later);
+ zfcp_erp_thread_kill(adapter);
+erp_thread_failed:
zfcp_fc_gs_destroy(adapter);
generic_services_failed:
zfcp_destroy_adapter_work_queue(adapter);
zfcp_fc_gs_destroy(adapter);
generic_services_failed:
zfcp_destroy_adapter_work_queue(adapter);
return;
zfcp_fc_gs_destroy(adapter);
return;
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_destroy_adapter_work_queue(adapter);
zfcp_dbf_adapter_unregister(adapter->dbf);
zfcp_free_low_mem_buffers(adapter);
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+ if (!adapter)
+ return 0;
+
down(&zfcp_data.config_sema);
zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
down(&zfcp_data.config_sema);
zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+ if (!adapter)
+ return 0;
+
zfcp_erp_modify_adapter_status(adapter, "ccresu1", NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
zfcp_erp_modify_adapter_status(adapter, "ccresu1", NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: 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 *ccw_device)
{
- int retval = 0;
-
- down(&zfcp_data.config_sema);
- if (zfcp_adapter_enqueue(ccw_device)) {
- dev_err(&ccw_device->dev,
- "Setting up data structures for the "
- "FCP adapter failed\n");
- retval = -EINVAL;
- }
- up(&zfcp_data.config_sema);
- return retval;
LIST_HEAD(port_remove_lh);
ccw_device_set_offline(ccw_device);
LIST_HEAD(port_remove_lh);
ccw_device_set_offline(ccw_device);
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
+ if (!adapter)
+ goto out;
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
zfcp_adapter_dequeue(adapter);
wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
zfcp_adapter_dequeue(adapter);
up(&zfcp_data.config_sema);
}
up(&zfcp_data.config_sema);
}
* zfcp_ccw_set_online - set_online function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* zfcp_ccw_set_online - set_online function of zfcp driver
* @ccw_device: 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)
{
struct zfcp_adapter *adapter;
*/
static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
- retval = zfcp_erp_thread_setup(adapter);
- if (retval)
- goto out;
+ if (!adapter) {
+ ret = zfcp_adapter_enqueue(ccw_device);
+ if (ret) {
+ dev_err(&ccw_device->dev,
+ "Setting up data structures for the "
+ "FCP adapter failed\n");
+ goto out;
+ }
+ adapter = dev_get_drvdata(&ccw_device->dev);
+ }
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
"ccsonl2", NULL);
zfcp_erp_wait(adapter);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
"ccsonl2", NULL);
zfcp_erp_wait(adapter);
up(&zfcp_data.config_sema);
up(&zfcp_data.config_sema);
- flush_work(&adapter->scan_work);
- return 0;
-
- out:
- up(&zfcp_data.config_sema);
- return retval;
+ if (!ret)
+ flush_work(&adapter->scan_work);
+ return ret;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
+ if (!adapter)
+ goto out;
+
zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
zfcp_erp_wait(adapter);
zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
zfcp_erp_wait(adapter);
- zfcp_erp_thread_kill(adapter);
up(&zfcp_data.config_sema);
up(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&cdev->dev);
zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
zfcp_erp_wait(adapter);
adapter = dev_get_drvdata(&cdev->dev);
zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
zfcp_erp_wait(adapter);
+ zfcp_erp_thread_kill(adapter);
up(&zfcp_data.config_sema);
}
up(&zfcp_data.config_sema);
}
a_status = atomic_read(&adapter->status);
if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
return 0;
a_status = atomic_read(&adapter->status);
if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
return 0;
+ if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) &&
+ !(a_status & ZFCP_STATUS_COMMON_OPEN))
+ return 0; /* shutdown requested for closed adapter */
{
kthread_stop(adapter->erp_thread);
adapter->erp_thread = NULL;
{
kthread_stop(adapter->erp_thread);
adapter->erp_thread = NULL;
+ WARN_ON(!list_empty(&adapter->erp_ready_head));
+ WARN_ON(!list_empty(&adapter->erp_running_head));