summaryrefslogtreecommitdiff
path: root/drivers/s390/scsi/zfcp_aux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/scsi/zfcp_aux.c')
-rw-r--r--drivers/s390/scsi/zfcp_aux.c176
1 files changed, 82 insertions, 94 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 1b9e4aee914b..01f927ae61b5 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -1,9 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* zfcp device driver
*
* Module interface and handling of zfcp data structures.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2020
*/
/*
@@ -24,22 +25,22 @@
* Martin Petermann
* Sven Schuetz
* Steffen Maier
+ * Benjamin Block
*/
-#define KMSG_COMPONENT "zfcp"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zfcp: " fmt
-#include <linux/miscdevice.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "zfcp_ext.h"
#include "zfcp_fc.h"
#include "zfcp_reqlist.h"
+#include "zfcp_diag.h"
#define ZFCP_BUS_ID_SIZE 20
-MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
+MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("FCP HBA driver");
MODULE_LICENSE("GPL");
@@ -101,14 +102,14 @@ static void __init zfcp_init_device_setup(char *devstr)
token = strsep(&str, ",");
if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE)
goto err_out;
- strncpy(busid, token, ZFCP_BUS_ID_SIZE);
+ strscpy(busid, token, ZFCP_BUS_ID_SIZE);
token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0, (unsigned long long *) &wwpn))
+ if (!token || kstrtoull(token, 0, (unsigned long long *) &wwpn))
goto err_out;
token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun))
+ if (!token || kstrtoull(token, 0, (unsigned long long *) &lun))
goto err_out;
kfree(str_saved);
@@ -124,6 +125,9 @@ static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
+ if (zfcp_experimental_dix)
+ pr_warn("DIX is enabled. It is experimental and might cause problems\n");
+
zfcp_fsf_qtcb_cache = zfcp_cache_hw_align("zfcp_fsf_qtcb",
sizeof(struct fsf_qtcb));
if (!zfcp_fsf_qtcb_cache)
@@ -248,43 +252,36 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
- if (adapter->pool.erp_req)
- mempool_destroy(adapter->pool.erp_req);
- if (adapter->pool.scsi_req)
- mempool_destroy(adapter->pool.scsi_req);
- if (adapter->pool.scsi_abort)
- mempool_destroy(adapter->pool.scsi_abort);
- if (adapter->pool.qtcb_pool)
- mempool_destroy(adapter->pool.qtcb_pool);
- if (adapter->pool.status_read_req)
- mempool_destroy(adapter->pool.status_read_req);
- if (adapter->pool.sr_data)
- mempool_destroy(adapter->pool.sr_data);
- if (adapter->pool.gid_pn)
- mempool_destroy(adapter->pool.gid_pn);
+ mempool_destroy(adapter->pool.erp_req);
+ mempool_destroy(adapter->pool.scsi_req);
+ mempool_destroy(adapter->pool.scsi_abort);
+ mempool_destroy(adapter->pool.qtcb_pool);
+ mempool_destroy(adapter->pool.status_read_req);
+ mempool_destroy(adapter->pool.sr_data);
+ mempool_destroy(adapter->pool.gid_pn);
}
/**
* zfcp_status_read_refill - refill the long running status_read_requests
* @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled
*
- * Returns: 0 on success, 1 otherwise
- *
- * if there are 16 or more status_read requests missing an adapter_reopen
- * is triggered
+ * Return:
+ * * 0 on success meaning at least one status read is pending
+ * * 1 if posting failed and not a single status read buffer is pending,
+ * also triggers adapter reopen recovery
*/
int zfcp_status_read_refill(struct zfcp_adapter *adapter)
{
- while (atomic_read(&adapter->stat_miss) > 0)
+ while (atomic_add_unless(&adapter->stat_miss, -1, 0))
if (zfcp_fsf_status_read(adapter->qdio)) {
+ atomic_inc(&adapter->stat_miss); /* undo add -1 */
if (atomic_read(&adapter->stat_miss) >=
adapter->stat_read_buf_num) {
zfcp_erp_adapter_reopen(adapter, 0, "axsref1");
return 1;
}
break;
- } else
- atomic_dec(&adapter->stat_miss);
+ }
return 0;
}
@@ -294,6 +291,14 @@ static void _zfcp_status_read_scheduler(struct work_struct *work)
stat_work));
}
+static void zfcp_version_change_lost_work(struct work_struct *work)
+{
+ struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
+ version_change_lost_work);
+
+ zfcp_fsf_exchange_config_data_sync(adapter->qdio, NULL);
+}
+
static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
{
struct zfcp_adapter *adapter =
@@ -306,15 +311,13 @@ static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
static int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter)
{
- char name[TASK_COMM_LEN];
-
- snprintf(name, sizeof(name), "zfcp_q_%s",
- dev_name(&adapter->ccw_device->dev));
- adapter->work_queue = create_singlethread_workqueue(name);
+ adapter->work_queue =
+ alloc_ordered_workqueue("zfcp_q_%s", WQ_MEM_RECLAIM,
+ dev_name(&adapter->ccw_device->dev));
+ if (!adapter->work_queue)
+ return -ENOMEM;
- if (adapter->work_queue)
- return 0;
- return -ENOMEM;
+ return 0;
}
static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter)
@@ -353,8 +356,17 @@ 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);
+ INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update);
+ INIT_WORK(&adapter->version_change_lost_work,
+ zfcp_version_change_lost_work);
+
+ adapter->next_port_scan = jiffies;
+
+ adapter->erp_action.adapter = adapter;
+
+ if (zfcp_diag_adapter_setup(adapter))
+ goto failed;
if (zfcp_qdio_setup(adapter))
goto failed;
@@ -398,21 +410,33 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
dev_set_drvdata(&ccw_device->dev, adapter);
- if (sysfs_create_group(&ccw_device->dev.kobj,
- &zfcp_sysfs_adapter_attrs))
- goto failed;
+ if (device_add_groups(&ccw_device->dev, zfcp_sysfs_adapter_attr_groups))
+ goto err_sysfs;
/* report size limit per scatter-gather segment */
- adapter->dma_parms.max_segment_size = ZFCP_QDIO_SBALE_LEN;
adapter->ccw_device->dev.dma_parms = &adapter->dma_parms;
adapter->stat_read_buf_num = FSF_STATUS_READS_RECOM;
- if (!zfcp_scsi_adapter_register(adapter))
- return adapter;
+ return adapter;
+err_sysfs:
failed:
- zfcp_adapter_unregister(adapter);
+ /* TODO: make this more fine-granular */
+ cancel_delayed_work_sync(&adapter->scan_work);
+ cancel_work_sync(&adapter->stat_work);
+ cancel_work_sync(&adapter->ns_up_work);
+ cancel_work_sync(&adapter->version_change_lost_work);
+ zfcp_destroy_adapter_work_queue(adapter);
+
+ zfcp_fc_wka_ports_force_offline(adapter->gs);
+ zfcp_scsi_adapter_unregister(adapter);
+
+ zfcp_erp_thread_kill(adapter);
+ zfcp_dbf_adapter_unregister(adapter);
+ zfcp_qdio_destroy(adapter->qdio);
+
+ zfcp_ccw_adapter_put(adapter); /* final put to release */
return ERR_PTR(-ENOMEM);
}
@@ -420,14 +444,15 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter)
{
struct ccw_device *cdev = adapter->ccw_device;
- cancel_work_sync(&adapter->scan_work);
+ cancel_delayed_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
cancel_work_sync(&adapter->ns_up_work);
+ cancel_work_sync(&adapter->version_change_lost_work);
zfcp_destroy_adapter_work_queue(adapter);
zfcp_fc_wka_ports_force_offline(adapter->gs);
zfcp_scsi_adapter_unregister(adapter);
- sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs);
+ device_remove_groups(&cdev->dev, zfcp_sysfs_adapter_attr_groups);
zfcp_erp_thread_kill(adapter);
zfcp_dbf_adapter_unregister(adapter);
@@ -450,6 +475,7 @@ void zfcp_adapter_release(struct kref *ref)
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
zfcp_fc_gs_destroy(adapter);
zfcp_free_low_mem_buffers(adapter);
+ zfcp_diag_adapter_free(adapter);
kfree(adapter->req_list);
kfree(adapter->fc_stats);
kfree(adapter->stats_reset_data);
@@ -489,12 +515,12 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
if (port) {
put_device(&port->dev);
retval = -EEXIST;
- goto err_out;
+ goto err_put;
}
port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
if (!port)
- goto err_out;
+ goto err_put;
rwlock_init(&port->unit_list_lock);
INIT_LIST_HEAD(&port->unit_list);
@@ -512,9 +538,12 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
port->dev.groups = zfcp_port_attr_groups;
port->dev.release = zfcp_port_release;
+ port->erp_action.adapter = adapter;
+ port->erp_action.port = port;
+
if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) {
kfree(port);
- goto err_out;
+ goto err_put;
}
retval = -EINVAL;
@@ -527,53 +556,12 @@ 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_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
+ atomic_or(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
return port;
-err_out:
+err_put:
zfcp_ccw_adapter_put(adapter);
+err_out:
return ERR_PTR(retval);
}
-
-/**
- * zfcp_sg_free_table - free memory used by scatterlists
- * @sg: pointer to scatterlist
- * @count: number of scatterlist which are to be free'ed
- * the scatterlist are expected to reference pages always
- */
-void zfcp_sg_free_table(struct scatterlist *sg, int count)
-{
- int i;
-
- for (i = 0; i < count; i++, sg++)
- if (sg)
- free_page((unsigned long) sg_virt(sg));
- else
- break;
-}
-
-/**
- * zfcp_sg_setup_table - init scatterlist and allocate, assign buffers
- * @sg: pointer to struct scatterlist
- * @count: number of scatterlists which should be assigned with buffers
- * of size page
- *
- * Returns: 0 on success, -ENOMEM otherwise
- */
-int zfcp_sg_setup_table(struct scatterlist *sg, int count)
-{
- void *addr;
- int i;
-
- sg_init_table(sg, count);
- for (i = 0; i < count; i++, sg++) {
- addr = (void *) get_zeroed_page(GFP_KERNEL);
- if (!addr) {
- zfcp_sg_free_table(sg, i);
- return -ENOMEM;
- }
- sg_set_buf(sg, addr, PAGE_SIZE);
- }
- return 0;
-}