diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_qdio.c')
| -rw-r--r-- | drivers/s390/scsi/zfcp_qdio.c | 145 |
1 files changed, 119 insertions, 26 deletions
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 3a7f3374d10a..e15a1eabe42d 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -7,9 +7,9 @@ * Copyright IBM Corp. 2002, 2020 */ -#define KMSG_COMPONENT "zfcp" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "zfcp: " fmt +#include <linux/lockdep.h> #include <linux/slab.h> #include <linux/module.h> #include "zfcp_ext.h" @@ -19,6 +19,9 @@ static bool enable_multibuffer = true; module_param_named(datarouter, enable_multibuffer, bool, 0400); MODULE_PARM_DESC(datarouter, "Enable hardware data router support (default on)"); +#define ZFCP_QDIO_REQUEST_RESCAN_MSECS (MSEC_PER_SEC * 10) +#define ZFCP_QDIO_REQUEST_SCAN_MSECS MSEC_PER_SEC + static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *dbftag, unsigned int qdio_err) { @@ -65,19 +68,43 @@ static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err, { struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm; - if (unlikely(qdio_err)) { - zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err); - return; + zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err); +} + +static void zfcp_qdio_request_tasklet(struct tasklet_struct *tasklet) +{ + struct zfcp_qdio *qdio = from_tasklet(qdio, tasklet, request_tasklet); + struct ccw_device *cdev = qdio->adapter->ccw_device; + unsigned int start, error; + int completed; + + completed = qdio_inspect_output_queue(cdev, 0, &start, &error); + if (completed > 0) { + if (error) { + zfcp_qdio_handler_error(qdio, "qdreqt1", error); + } else { + /* cleanup all SBALs being program-owned now */ + zfcp_qdio_zero_sbals(qdio->req_q, start, completed); + + spin_lock_irq(&qdio->stat_lock); + zfcp_qdio_account(qdio); + spin_unlock_irq(&qdio->stat_lock); + atomic_add(completed, &qdio->req_q_free); + wake_up(&qdio->req_q_wq); + } } - /* cleanup all SBALs being program-owned now */ - zfcp_qdio_zero_sbals(qdio->req_q, idx, count); + if (atomic_read(&qdio->req_q_free) < QDIO_MAX_BUFFERS_PER_Q) + timer_reduce(&qdio->request_timer, + jiffies + msecs_to_jiffies(ZFCP_QDIO_REQUEST_RESCAN_MSECS)); +} - spin_lock_irq(&qdio->stat_lock); - zfcp_qdio_account(qdio); - spin_unlock_irq(&qdio->stat_lock); - atomic_add(count, &qdio->req_q_free); - wake_up(&qdio->req_q_wq); +static void zfcp_qdio_request_timer(struct timer_list *timer) +{ + struct zfcp_qdio *qdio = timer_container_of(qdio, timer, + request_timer); + + tasklet_schedule(&qdio->request_tasklet); } static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, @@ -98,7 +125,7 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, memset(pl, 0, ZFCP_QDIO_MAX_SBALS_PER_REQ * sizeof(void *)); sbale = qdio->res_q[idx]->element; - req_id = sbale->addr; + req_id = dma64_to_u64(sbale->addr); scount = min(sbale->scount + 1, ZFCP_QDIO_MAX_SBALS_PER_REQ + 1); /* incl. signaling SBAL */ @@ -127,10 +154,40 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, /* * put SBALs back to response queue */ - if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, idx, count)) + if (qdio_add_bufs_to_input_queue(cdev, 0, idx, count)) zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2"); } +static void zfcp_qdio_irq_tasklet(struct tasklet_struct *tasklet) +{ + struct zfcp_qdio *qdio = from_tasklet(qdio, tasklet, irq_tasklet); + struct ccw_device *cdev = qdio->adapter->ccw_device; + unsigned int start, error; + int completed; + + if (atomic_read(&qdio->req_q_free) < QDIO_MAX_BUFFERS_PER_Q) + tasklet_schedule(&qdio->request_tasklet); + + /* Check the Response Queue: */ + completed = qdio_inspect_input_queue(cdev, 0, &start, &error); + if (completed < 0) + return; + if (completed > 0) + zfcp_qdio_int_resp(cdev, error, 0, start, completed, + (unsigned long) qdio); + + if (qdio_start_irq(cdev)) + /* More work pending: */ + tasklet_schedule(&qdio->irq_tasklet); +} + +static void zfcp_qdio_poll(struct ccw_device *cdev, unsigned long data) +{ + struct zfcp_qdio *qdio = (struct zfcp_qdio *) data; + + tasklet_schedule(&qdio->irq_tasklet); +} + static struct qdio_buffer_element * zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { @@ -199,7 +256,7 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, q_req->sbal_number); return -EINVAL; } - sbale->addr = sg_phys(sg); + sbale->addr = u64_to_dma64(sg_phys(sg)); sbale->length = sg->length; } return 0; @@ -246,7 +303,7 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) } /** - * zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO + * zfcp_qdio_send - send req to QDIO * @qdio: pointer to struct zfcp_qdio * @q_req: pointer to struct zfcp_qdio_req * Returns: 0 on success, error otherwise @@ -256,21 +313,38 @@ int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) int retval; u8 sbal_number = q_req->sbal_number; + /* + * This should actually be a spin_lock_bh(stat_lock), to protect against + * Request Queue completion processing in tasklet context. + * But we can't do so (and are safe), as we always get called with IRQs + * disabled by spin_lock_irq[save](req_q_lock). + */ + lockdep_assert_irqs_disabled(); spin_lock(&qdio->stat_lock); zfcp_qdio_account(qdio); spin_unlock(&qdio->stat_lock); - retval = do_QDIO(qdio->adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, - q_req->sbal_first, sbal_number); + atomic_sub(sbal_number, &qdio->req_q_free); + + retval = qdio_add_bufs_to_output_queue(qdio->adapter->ccw_device, 0, + q_req->sbal_first, sbal_number, + NULL); if (unlikely(retval)) { + /* Failed to submit the IO, roll back our modifications. */ + atomic_add(sbal_number, &qdio->req_q_free); zfcp_qdio_zero_sbals(qdio->req_q, q_req->sbal_first, sbal_number); return retval; } + if (atomic_read(&qdio->req_q_free) <= 2 * ZFCP_QDIO_MAX_SBALS_PER_REQ) + tasklet_schedule(&qdio->request_tasklet); + else + timer_reduce(&qdio->request_timer, + jiffies + msecs_to_jiffies(ZFCP_QDIO_REQUEST_SCAN_MSECS)); + /* account for transferred buffers */ - atomic_sub(sbal_number, &qdio->req_q_free); qdio->req_q_idx += sbal_number; qdio->req_q_idx %= QDIO_MAX_BUFFERS_PER_Q; @@ -311,7 +385,7 @@ free_req_q: } /** - * zfcp_close_qdio - close qdio queues for an adapter + * zfcp_qdio_close - close qdio queues for an adapter * @qdio: pointer to structure zfcp_qdio */ void zfcp_qdio_close(struct zfcp_qdio *qdio) @@ -322,13 +396,20 @@ void zfcp_qdio_close(struct zfcp_qdio *qdio) if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return; - /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */ + /* + * Clear QDIOUP flag, thus qdio_add_bufs_to_output_queue() is not called + * during qdio_shutdown(). + */ spin_lock_irq(&qdio->req_q_lock); atomic_andnot(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status); spin_unlock_irq(&qdio->req_q_lock); wake_up(&qdio->req_q_wq); + tasklet_disable(&qdio->irq_tasklet); + tasklet_disable(&qdio->request_tasklet); + timer_delete_sync(&qdio->request_timer); + qdio_stop_irq(adapter->ccw_device); qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR); /* cleanup used outbound sbals */ @@ -377,8 +458,6 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) &qdio->adapter->status); init_data.q_format = QDIO_ZFCP_QFMT; - memcpy(init_data.adapter_name, dev_name(&cdev->dev), 8); - ASCEBC(init_data.adapter_name, 8); init_data.qib_rflags = QIB_RFLAGS_ENABLE_DATA_DIV; if (enable_multibuffer) init_data.qdr_ac |= QDR_AC_MULTI_BUFFER_ENABLE; @@ -386,11 +465,10 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) init_data.no_output_qs = 1; init_data.input_handler = zfcp_qdio_int_resp; init_data.output_handler = zfcp_qdio_int_req; + init_data.irq_poll = zfcp_qdio_poll; init_data.int_parm = (unsigned long) qdio; init_data.input_sbal_addr_array = input_sbals; init_data.output_sbal_addr_array = output_sbals; - init_data.scan_threshold = - QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2; if (qdio_establish(cdev, &init_data)) goto failed_establish; @@ -424,7 +502,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) sbale->addr = 0; } - if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q)) + if (qdio_add_bufs_to_input_queue(cdev, 0, 0, QDIO_MAX_BUFFERS_PER_Q)) goto failed_qdio; /* set index of first available SBALS / number of available SBALS */ @@ -432,6 +510,13 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); atomic_or(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); + /* Enable processing for Request Queue completions: */ + tasklet_enable(&qdio->request_tasklet); + /* Enable processing for QDIO interrupts: */ + tasklet_enable(&qdio->irq_tasklet); + /* This results in a qdio_start_irq(): */ + tasklet_schedule(&qdio->irq_tasklet); + zfcp_qdio_shost_update(adapter, qdio); return 0; @@ -449,6 +534,9 @@ void zfcp_qdio_destroy(struct zfcp_qdio *qdio) if (!qdio) return; + tasklet_kill(&qdio->irq_tasklet); + tasklet_kill(&qdio->request_tasklet); + if (qdio->adapter->ccw_device) qdio_free(qdio->adapter->ccw_device); @@ -474,6 +562,11 @@ int zfcp_qdio_setup(struct zfcp_adapter *adapter) spin_lock_init(&qdio->req_q_lock); spin_lock_init(&qdio->stat_lock); + timer_setup(&qdio->request_timer, zfcp_qdio_request_timer, 0); + tasklet_setup(&qdio->irq_tasklet, zfcp_qdio_irq_tasklet); + tasklet_setup(&qdio->request_tasklet, zfcp_qdio_request_tasklet); + tasklet_disable(&qdio->irq_tasklet); + tasklet_disable(&qdio->request_tasklet); adapter->qdio = qdio; return 0; |
