From 29c53de014430bbf7febb402afd3f8149b6de047 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 30 Nov 2020 10:19:57 +0200 Subject: s390/cio: remove ccw_device_add() wrapper Set the bus type when initializing the cdev structure. The device core won't act on it until we call device_add(). Signed-off-by: Julian Wiedmann Reviewed-by: Vineeth Vijayan Signed-off-by: Vasily Gorbik --- drivers/s390/cio/device.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 4b0a7cbb2096..df46373d67ec 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -621,14 +621,6 @@ static const struct attribute_group *ccwdev_attr_groups[] = { NULL, }; -static int ccw_device_add(struct ccw_device *cdev) -{ - struct device *dev = &cdev->dev; - - dev->bus = &ccw_bus_type; - return device_add(dev); -} - static int match_dev_id(struct device *dev, const void *data) { struct ccw_device *cdev = to_ccwdev(dev); @@ -739,6 +731,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, cdev->ccwlock = sch->lock; cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; + cdev->dev.bus = &ccw_bus_type; cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); @@ -840,7 +833,7 @@ static void io_subchannel_register(struct ccw_device *cdev) kobject_uevent(&sch->dev.kobj, KOBJ_ADD); } /* make it known to the system */ - ret = ccw_device_add(cdev); + ret = device_add(&cdev->dev); if (ret) { CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n", cdev->private->dev_id.ssid, @@ -1052,7 +1045,7 @@ static int io_subchannel_probe(struct subchannel *sch) kobject_uevent(&sch->dev.kobj, KOBJ_ADD); } cdev = sch_get_cdev(sch); - rc = ccw_device_add(cdev); + rc = device_add(&cdev->dev); if (rc) { /* Release online reference. */ put_device(&cdev->dev); -- cgit v1.2.3 From 4520a91a976e3f5e74d3d27ccc66a7a669826373 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 9 Dec 2020 11:24:13 +0100 Subject: s390/cio: use dma helpers for setting masks Bypassing the DMA API is bad style, even when we don't expect any actual problems. Let's utilize the right API helpers for setting the DMA masks and check for returned errors, so that we benefit from common sanity checks. io_subchannel_allocate_dev() required some extra massaging, so that we can return an errno other than -ENOMEM. Signed-off-by: Julian Wiedmann Reviewed-by: Halil Pasic Signed-off-by: Vasily Gorbik --- drivers/s390/cio/css.c | 20 ++++++++++++++------ drivers/s390/cio/device.c | 26 ++++++++++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 94c6470de635..253ab4e7a415 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -225,18 +225,23 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid, INIT_WORK(&sch->todo_work, css_sch_todo); sch->dev.release = &css_subchannel_release; + sch->dev.dma_mask = &sch->dma_mask; device_initialize(&sch->dev); /* - * The physical addresses of some the dma structures that can + * The physical addresses for some of the dma structures that can * belong to a subchannel need to fit 31 bit width (e.g. ccw). */ - sch->dev.coherent_dma_mask = DMA_BIT_MASK(31); + ret = dma_set_coherent_mask(&sch->dev, DMA_BIT_MASK(31)); + if (ret) + goto err; /* * But we don't have such restrictions imposed on the stuff that * is handled by the streaming API. */ - sch->dma_mask = DMA_BIT_MASK(64); - sch->dev.dma_mask = &sch->dma_mask; + ret = dma_set_mask(&sch->dev, DMA_BIT_MASK(64)); + if (ret) + goto err; + return sch; err: @@ -970,8 +975,11 @@ static int __init setup_css(int nr) * css->device as the device argument with the DMA API) * and are fine with 64 bit addresses. */ - css->device.coherent_dma_mask = DMA_BIT_MASK(64); - css->device.dma_mask = &css->device.coherent_dma_mask; + ret = dma_coerce_mask_and_coherent(&css->device, DMA_BIT_MASK(64)); + if (ret) { + kfree(css); + goto out_err; + } mutex_init(&css->mutex); ret = chsc_get_cssid_iid(nr, &css->cssid, &css->iid); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index df46373d67ec..3f026021e95e 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -679,33 +679,47 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch) { struct ccw_device *cdev; struct gen_pool *dma_pool; + int ret; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); - if (!cdev) + if (!cdev) { + ret = -ENOMEM; goto err_cdev; + } cdev->private = kzalloc(sizeof(struct ccw_device_private), GFP_KERNEL | GFP_DMA); - if (!cdev->private) + if (!cdev->private) { + ret = -ENOMEM; goto err_priv; - cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask; + } + cdev->dev.dma_mask = sch->dev.dma_mask; + ret = dma_set_coherent_mask(&cdev->dev, sch->dev.coherent_dma_mask); + if (ret) + goto err_coherent_mask; + dma_pool = cio_gp_dma_create(&cdev->dev, 1); - if (!dma_pool) + if (!dma_pool) { + ret = -ENOMEM; goto err_dma_pool; + } cdev->private->dma_pool = dma_pool; cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev, sizeof(*cdev->private->dma_area)); - if (!cdev->private->dma_area) + if (!cdev->private->dma_area) { + ret = -ENOMEM; goto err_dma_area; + } return cdev; err_dma_area: cio_gp_dma_destroy(dma_pool, &cdev->dev); err_dma_pool: +err_coherent_mask: kfree(cdev->private); err_priv: kfree(cdev); err_cdev: - return ERR_PTR(-ENOMEM); + return ERR_PTR(ret); } static void ccw_device_todo(struct work_struct *work); -- cgit v1.2.3 From aa0028e67952c32bd6a51cc4781860fe4e3e292f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 18 Jan 2021 11:32:22 +0000 Subject: s390/tape: Fix spelling mistake in function name tape_3590_erp_succeded Rename tape_3590_erp_succeded to tape_3590_erp_succeeded to fix a spelling mistake in the function name. Signed-off-by: Colin Ian King Signed-off-by: Vasily Gorbik Link: https://lore.kernel.org/r/20210118113222.71708-1-colin.king@canonical.com Signed-off-by: Vasily Gorbik --- drivers/s390/char/tape_3590.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index ecf8c5006a0e..0d484fe43d7e 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -761,7 +761,7 @@ tape_3590_done(struct tape_device *device, struct tape_request *request) * This function is called, when error recovery was successful */ static inline int -tape_3590_erp_succeded(struct tape_device *device, struct tape_request *request) +tape_3590_erp_succeeded(struct tape_device *device, struct tape_request *request) { DBF_EVENT(3, "Error Recovery successful for %s\n", tape_op_verbose[request->op]); @@ -831,7 +831,7 @@ tape_3590_erp_basic(struct tape_device *device, struct tape_request *request, case SENSE_BRA_PER: return tape_3590_erp_failed(device, request, irb, rc); case SENSE_BRA_CONT: - return tape_3590_erp_succeded(device, request); + return tape_3590_erp_succeeded(device, request); case SENSE_BRA_RE: return tape_3590_erp_retry(device, request, irb); case SENSE_BRA_DRE: -- cgit v1.2.3 From 1daafea411f36cfa52eb58c2e7f9e2254fd42b28 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Fri, 15 Jan 2021 08:56:19 +0100 Subject: s390/crypto: improve retry logic in case of master key change A master key change on a CCA card may cause an immediately following request to derive an protected key from a secure key to fail with error condition 8/2290. The recommendation from firmware is to retry with 1 second sleep. So now the low level cca functions return -EAGAIN when this error condition is seen and the paes retry function will evaluate the return value. Seeing EAGAIN and running in process context results in trying to sleep for 1 s now. Signed-off-by: Harald Freudenberger Reviewed-by: Ingo Franzki Signed-off-by: Vasily Gorbik --- arch/s390/crypto/paes_s390.c | 28 ++++++++++++++++++++-------- drivers/s390/crypto/zcrypt_ccamisc.c | 15 ++++++++++++--- 2 files changed, 32 insertions(+), 11 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c index f3caeb17c85b..a279b7d23a5e 100644 --- a/arch/s390/crypto/paes_s390.c +++ b/arch/s390/crypto/paes_s390.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +129,9 @@ static inline int __paes_keyblob2pkey(struct key_blob *kb, /* try three times in case of failure */ for (i = 0; i < 3; i++) { + if (i > 0 && ret == -EAGAIN && in_task()) + if (msleep_interruptible(1000)) + return -EINTR; ret = pkey_keyblob2pkey(kb->key, kb->keylen, pk); if (ret == 0) break; @@ -138,10 +142,12 @@ static inline int __paes_keyblob2pkey(struct key_blob *kb, static inline int __paes_convert_key(struct s390_paes_ctx *ctx) { + int ret; struct pkey_protkey pkey; - if (__paes_keyblob2pkey(&ctx->kb, &pkey)) - return -EINVAL; + ret = __paes_keyblob2pkey(&ctx->kb, &pkey); + if (ret) + return ret; spin_lock_bh(&ctx->pk_lock); memcpy(&ctx->pk, &pkey, sizeof(pkey)); @@ -169,10 +175,12 @@ static void ecb_paes_exit(struct crypto_skcipher *tfm) static inline int __ecb_paes_set_key(struct s390_paes_ctx *ctx) { + int rc; unsigned long fc; - if (__paes_convert_key(ctx)) - return -EINVAL; + rc = __paes_convert_key(ctx); + if (rc) + return rc; /* Pick the correct function code based on the protected key type */ fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KM_PAES_128 : @@ -282,10 +290,12 @@ static void cbc_paes_exit(struct crypto_skcipher *tfm) static inline int __cbc_paes_set_key(struct s390_paes_ctx *ctx) { + int rc; unsigned long fc; - if (__paes_convert_key(ctx)) - return -EINVAL; + rc = __paes_convert_key(ctx); + if (rc) + return rc; /* Pick the correct function code based on the protected key type */ fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMC_PAES_128 : @@ -577,10 +587,12 @@ static void ctr_paes_exit(struct crypto_skcipher *tfm) static inline int __ctr_paes_set_key(struct s390_paes_ctx *ctx) { + int rc; unsigned long fc; - if (__paes_convert_key(ctx)) - return -EINVAL; + rc = __paes_convert_key(ctx); + if (rc) + return rc; /* Pick the correct function code based on the protected key type */ fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMCTR_PAES_128 : diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index b1046811450f..d68c0ed5e0dd 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -662,7 +662,10 @@ int cca_sec2protkey(u16 cardnr, u16 domain, __func__, (int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rscode); - rc = -EIO; + if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290) + rc = -EAGAIN; + else + rc = -EIO; goto out; } if (prepcblk->ccp_rscode != 0) { @@ -1275,7 +1278,10 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey, __func__, (int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rscode); - rc = -EIO; + if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290) + rc = -EAGAIN; + else + rc = -EIO; goto out; } if (prepcblk->ccp_rscode != 0) { @@ -1441,7 +1447,10 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key, __func__, (int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rscode); - rc = -EIO; + if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290) + rc = -EAGAIN; + else + rc = -EIO; goto out; } if (prepcblk->ccp_rscode != 0) { -- cgit v1.2.3 From 1ecbcfd57ed6ee11ec39eac9b6516883c925c558 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 2 Jun 2020 14:09:10 +0200 Subject: s390/qdio: remove Input tasklet code Both qeth and zfcp have fully moved to the polling-driven flow for Input Queues with commit 0a6e634535f1 ("s390/qdio: extend polling support to multiple queues") and commit 0b524abc2dd1 ("scsi: zfcp: Lift Input Queue tasklet from qdio"). So remove the tasklet code for Input Queues, streamline the IRQ handlers and push the tasklet struct into struct qdio_output_q. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/qdio.h | 2 +- drivers/s390/cio/qdio.h | 16 +++++---- drivers/s390/cio/qdio_debug.c | 3 -- drivers/s390/cio/qdio_main.c | 72 ++++------------------------------------- drivers/s390/cio/qdio_setup.c | 18 ++--------- drivers/s390/cio/qdio_thinint.c | 36 ++++----------------- 6 files changed, 27 insertions(+), 120 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index 19e84c95d1e7..f96454f5d4cd 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -336,7 +336,7 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int, * @no_output_qs: number of output queues * @input_handler: handler to be called for input queues * @output_handler: handler to be called for output queues - * @irq_poll: Data IRQ polling handler (NULL when not supported) + * @irq_poll: Data IRQ polling handler * @scan_threshold: # of in-use buffers that triggers scan on output queue * @int_parm: interruption parameter * @input_sbal_addr_array: per-queue array, each element points to 128 SBALs diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index cd2df4ff8e0e..b4e7152fd586 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -139,9 +139,6 @@ struct qdio_dev_perf_stat { unsigned int qdio_int; unsigned int pci_request_int; - unsigned int tasklet_inbound; - unsigned int tasklet_inbound_resched; - unsigned int tasklet_inbound_resched2; unsigned int tasklet_outbound; unsigned int siga_read; @@ -193,6 +190,8 @@ struct qdio_output_q { struct qdio_outbuf_state *sbal_state; /* timer to check for more outbound work */ struct timer_list timer; + /* tasklet to check for completions */ + struct tasklet_struct tasklet; }; /* @@ -222,7 +221,6 @@ struct qdio_q { /* last scan of the queue */ u64 timestamp; - struct tasklet_struct tasklet; struct qdio_queue_perf_stat q_stats; struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q] ____cacheline_aligned; @@ -324,6 +322,14 @@ static inline int multicast_outbound(struct qdio_q *q) (q->nr == q->irq_ptr->nr_output_qs - 1); } +static inline void qdio_deliver_irq(struct qdio_irq *irq) +{ + if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq->poll_state)) + irq->irq_poll(irq->cdev, irq->int_parm); + else + QDIO_PERF_STAT_INC(irq, int_discarded); +} + #define pci_out_supported(irq) ((irq)->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) #define is_qebsm(q) (q->irq_ptr->sch_token != 0) @@ -359,13 +365,11 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr); void qdio_shutdown_thinint(struct qdio_irq *irq_ptr); void tiqdio_add_device(struct qdio_irq *irq_ptr); void tiqdio_remove_device(struct qdio_irq *irq_ptr); -void tiqdio_inbound_processing(unsigned long q); int qdio_thinint_init(void); void qdio_thinint_exit(void); int test_nonshared_ind(struct qdio_irq *); /* prototypes for setup */ -void qdio_inbound_processing(unsigned long data); void qdio_outbound_processing(unsigned long data); void qdio_outbound_timer(struct timer_list *t); void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 863d17c802ca..728abf7e9ccb 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -197,9 +197,6 @@ static char *qperf_names[] = { "Assumed adapter interrupts", "QDIO interrupts", "Requested PCIs", - "Inbound tasklet runs", - "Inbound tasklet resched", - "Inbound tasklet resched2", "Outbound tasklet runs", "SIGA read", "SIGA write", diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index f9a31c7819ae..a4dc5e283750 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -575,51 +575,12 @@ static void qdio_kick_handler(struct qdio_q *q, unsigned int start, static inline int qdio_tasklet_schedule(struct qdio_q *q) { if (likely(q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE)) { - tasklet_schedule(&q->tasklet); + tasklet_schedule(&q->u.out.tasklet); return 0; } return -EPERM; } -static void __qdio_inbound_processing(struct qdio_q *q) -{ - unsigned int start = q->first_to_check; - int count; - - qperf_inc(q, tasklet_inbound); - - count = qdio_inbound_q_moved(q, start); - if (count == 0) - return; - - qdio_kick_handler(q, start, count); - start = add_buf(start, count); - q->first_to_check = start; - - if (!qdio_inbound_q_done(q, start)) { - /* means poll time is not yet over */ - qperf_inc(q, tasklet_inbound_resched); - if (!qdio_tasklet_schedule(q)) - return; - } - - qdio_stop_polling(q); - /* - * We need to check again to not lose initiative after - * resetting the ACK state. - */ - if (!qdio_inbound_q_done(q, start)) { - qperf_inc(q, tasklet_inbound_resched2); - qdio_tasklet_schedule(q); - } -} - -void qdio_inbound_processing(unsigned long data) -{ - struct qdio_q *q = (struct qdio_q *)data; - __qdio_inbound_processing(q); -} - static void qdio_check_pending(struct qdio_q *q, unsigned int index) { unsigned char state; @@ -825,19 +786,6 @@ static inline void qdio_check_outbound_pci_queues(struct qdio_irq *irq) qdio_tasklet_schedule(out); } -void tiqdio_inbound_processing(unsigned long data) -{ - struct qdio_q *q = (struct qdio_q *)data; - - if (need_siga_sync(q) && need_siga_sync_after_ai(q)) - qdio_sync_queues(q); - - /* The interrupt could be caused by a PCI request: */ - qdio_check_outbound_pci_queues(q->irq_ptr); - - __qdio_inbound_processing(q); -} - static inline void qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { @@ -865,15 +813,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) if (unlikely(irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) return; - if (irq_ptr->irq_poll) { - if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state)) - irq_ptr->irq_poll(irq_ptr->cdev, irq_ptr->int_parm); - else - QDIO_PERF_STAT_INC(irq_ptr, int_discarded); - } else { - for_each_input_queue(irq_ptr, q, i) - tasklet_schedule(&q->tasklet); - } + qdio_deliver_irq(irq_ptr); if (!pci_out_supported(irq_ptr) || !irq_ptr->scan_threshold) return; @@ -1016,12 +956,9 @@ static void qdio_shutdown_queues(struct qdio_irq *irq_ptr) struct qdio_q *q; int i; - for_each_input_queue(irq_ptr, q, i) - tasklet_kill(&q->tasklet); - for_each_output_queue(irq_ptr, q, i) { del_timer_sync(&q->u.out.timer); - tasklet_kill(&q->tasklet); + tasklet_kill(&q->u.out.tasklet); } } @@ -1263,6 +1200,9 @@ int qdio_establish(struct ccw_device *cdev, !init_data->output_sbal_addr_array) return -EINVAL; + if (!init_data->irq_poll) + return -EINVAL; + mutex_lock(&irq_ptr->setup_mutex); qdio_trace_init_data(irq_ptr, init_data); qdio_setup_irq(irq_ptr, init_data); diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index a5b2e16b7aa8..3571ca62a74c 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -259,14 +259,6 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_storage_lists(q, irq_ptr, qdio_init->input_sbal_addr_array[i], i); - - if (is_thinint_irq(irq_ptr)) { - tasklet_init(&q->tasklet, tiqdio_inbound_processing, - (unsigned long) q); - } else { - tasklet_init(&q->tasklet, qdio_inbound_processing, - (unsigned long) q); - } } for_each_output_queue(irq_ptr, q, i) { @@ -280,7 +272,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_storage_lists(q, irq_ptr, qdio_init->output_sbal_addr_array[i], i); - tasklet_init(&q->tasklet, qdio_outbound_processing, + tasklet_init(&q->u.out.tasklet, qdio_outbound_processing, (unsigned long) q); timer_setup(&q->u.out.timer, qdio_outbound_timer, 0); } @@ -483,12 +475,8 @@ int qdio_setup_irq(struct qdio_irq *irq_ptr, struct qdio_initialize *init_data) ccw_device_get_schid(cdev, &irq_ptr->schid); setup_queues(irq_ptr, init_data); - if (init_data->irq_poll) { - irq_ptr->irq_poll = init_data->irq_poll; - set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state); - } else { - irq_ptr->irq_poll = NULL; - } + irq_ptr->irq_poll = init_data->irq_poll; + set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state); setup_qib(irq_ptr, init_data); set_impl_params(irq_ptr, init_data->qib_param_field_format, diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 7a440e4328cd..d495bf0200aa 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -106,32 +106,6 @@ static inline u32 clear_shared_ind(void) return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); } -static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) -{ - struct qdio_q *q; - int i; - - if (!references_shared_dsci(irq)) - xchg(irq->dsci, 0); - - if (irq->irq_poll) { - if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq->poll_state)) - irq->irq_poll(irq->cdev, irq->int_parm); - else - QDIO_PERF_STAT_INC(irq, int_discarded); - - return; - } - - for_each_input_queue(irq, q, i) { - /* - * Call inbound processing but not directly - * since that could starve other thinint queues. - */ - tasklet_schedule(&q->tasklet); - } -} - /** * tiqdio_thinint_handler - thin interrupt handler for qdio * @airq: pointer to adapter interrupt descriptor @@ -153,10 +127,14 @@ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) if (unlikely(references_shared_dsci(irq))) { if (!si_used) continue; - } else if (!*irq->dsci) - continue; + } else { + if (!*irq->dsci) + continue; + + xchg(irq->dsci, 0); + } - tiqdio_call_inq_handlers(irq); + qdio_deliver_irq(irq); QDIO_PERF_STAT_INC(irq, adapter_int); } -- cgit v1.2.3 From ed645696e07a402723320b13bc3756844db5de30 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 25 Jan 2021 09:46:59 +0100 Subject: s390/qdio: remove qdio_inbound_q_moved() wrapper It's used in just one place, inline it. Signed-off-by: Julian Wiedmann Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio_main.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a4dc5e283750..60ac07742f3d 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -508,11 +508,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) } } -static int qdio_inbound_q_moved(struct qdio_q *q, unsigned int start) -{ - return get_inbound_buffer_frontier(q, start); -} - static inline int qdio_inbound_q_done(struct qdio_q *q, unsigned int start) { unsigned char state = 0; @@ -1497,7 +1492,7 @@ static int __qdio_inspect_queue(struct qdio_q *q, unsigned int *bufnr, unsigned int start = q->first_to_check; int count; - count = q->is_input_q ? qdio_inbound_q_moved(q, start) : + count = q->is_input_q ? get_inbound_buffer_frontier(q, start) : qdio_outbound_q_moved(q, start); if (count == 0) return 0; -- cgit v1.2.3 From 6bb7a51b605a36e5371a38bfa20173690251b484 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 1 Oct 2020 09:47:47 +0200 Subject: s390/qdio: adopt new tasklet API Convert the Output Queue tasklet code to take a tasklet_struct as parameter. Then initialize the tasklet with tasklet_setup() to indicate that we follow the new model. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio.h | 2 +- drivers/s390/cio/qdio_main.c | 11 +++-------- drivers/s390/cio/qdio_setup.c | 3 +-- 3 files changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index b4e7152fd586..4889681370f3 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -370,7 +370,7 @@ void qdio_thinint_exit(void); int test_nonshared_ind(struct qdio_irq *); /* prototypes for setup */ -void qdio_outbound_processing(unsigned long data); +void qdio_outbound_tasklet(struct tasklet_struct *t); void qdio_outbound_timer(struct timer_list *t); void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 60ac07742f3d..2cc489c9f986 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -716,8 +716,10 @@ retry: return cc; } -static void __qdio_outbound_processing(struct qdio_q *q) +void qdio_outbound_tasklet(struct tasklet_struct *t) { + struct qdio_output_q *out_q = from_tasklet(out_q, t, tasklet); + struct qdio_q *q = container_of(out_q, struct qdio_q, u.out); unsigned int start = q->first_to_check; int count; @@ -754,13 +756,6 @@ sched: qdio_tasklet_schedule(q); } -/* outbound tasklet */ -void qdio_outbound_processing(unsigned long data) -{ - struct qdio_q *q = (struct qdio_q *)data; - __qdio_outbound_processing(q); -} - void qdio_outbound_timer(struct timer_list *t) { struct qdio_q *q = from_timer(q, t, u.out.timer); diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 3571ca62a74c..c8b9620bc688 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -272,8 +272,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_storage_lists(q, irq_ptr, qdio_init->output_sbal_addr_array[i], i); - tasklet_init(&q->u.out.tasklet, qdio_outbound_processing, - (unsigned long) q); + tasklet_setup(&q->u.out.tasklet, qdio_outbound_tasklet); timer_setup(&q->u.out.timer, qdio_outbound_timer, 0); } } -- cgit v1.2.3 From 954d6235be412e3de33a43e68ab39342f5eccf9b Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 30 Sep 2020 10:09:07 +0200 Subject: s390/qdio: make thinint registration symmetric tiqdio_add_device() adds the device to the tiq_list of eligible targets for a data IRQ, which gets walked on each QDIO Adapter Interrupt to inspect their DSCIs. But currently the tiqdio_add_device() / tiqdio_remove_device() calls are not symmetric - the device is removed within qdio_shutdown(), but only added by qdio_activate(). So depending on the call sequence and encountered errors, we might be trying to remove a list entry in qdio_shutdown() that was never even added to the list. This required additional INIT_LIST_HEAD() calls to ensure that the list entry was always in a consistent state. All drivers now fence the IRQ delivery via qdio_start_irq() / qdio_stop_irq(), so we can nicely integrate this tiq_list management with the other steps needed for QDIO Adapter IRQ (de-)registration (qdio_establish_thinint() / qdio_shutdown_thinint()). As the naming suggests these get called during qdio_establish() and qdio_shutdown(), with proper symmetry and roll-back after errors. With this we longer need to worry about misplaced list removals, and thus can clean up the list API abuse (INIT_LIST_HEAD() should not be called on list entries). Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio.h | 2 -- drivers/s390/cio/qdio_main.c | 5 ----- drivers/s390/cio/qdio_thinint.c | 30 ++++++++++++------------------ 3 files changed, 12 insertions(+), 25 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 4889681370f3..4a4e4de45bc9 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -363,8 +363,6 @@ extern u64 last_ai_time; /* prototypes for thin interrupt */ int qdio_establish_thinint(struct qdio_irq *irq_ptr); void qdio_shutdown_thinint(struct qdio_irq *irq_ptr); -void tiqdio_add_device(struct qdio_irq *irq_ptr); -void tiqdio_remove_device(struct qdio_irq *irq_ptr); int qdio_thinint_init(void); void qdio_thinint_exit(void); int test_nonshared_ind(struct qdio_irq *); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 2cc489c9f986..3b1e8ed4ba14 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -986,7 +986,6 @@ int qdio_shutdown(struct ccw_device *cdev, int how) */ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); - tiqdio_remove_device(irq_ptr); qdio_shutdown_queues(irq_ptr); qdio_shutdown_debug_entries(irq_ptr); @@ -1104,7 +1103,6 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs, if (rc) goto err_queues; - INIT_LIST_HEAD(&irq_ptr->entry); cdev->private->qdio_data = irq_ptr; qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); return 0; @@ -1287,9 +1285,6 @@ int qdio_activate(struct ccw_device *cdev) goto out; } - if (is_thinint_irq(irq_ptr)) - tiqdio_add_device(irq_ptr); - /* wait for subchannel to become active */ msleep(5); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index d495bf0200aa..e1b923633372 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -66,22 +66,6 @@ static void put_indicator(u32 *addr) atomic_dec(&ind->count); } -void tiqdio_add_device(struct qdio_irq *irq_ptr) -{ - mutex_lock(&tiq_list_lock); - list_add_rcu(&irq_ptr->entry, &tiq_list); - mutex_unlock(&tiq_list_lock); -} - -void tiqdio_remove_device(struct qdio_irq *irq_ptr) -{ - mutex_lock(&tiq_list_lock); - list_del_rcu(&irq_ptr->entry); - mutex_unlock(&tiq_list_lock); - synchronize_rcu(); - INIT_LIST_HEAD(&irq_ptr->entry); -} - static inline int references_shared_dsci(struct qdio_irq *irq_ptr) { return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; @@ -186,10 +170,15 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr) DBF_HEX(&irq_ptr->dsci, sizeof(void *)); rc = set_subchannel_ind(irq_ptr, 0); - if (rc) + if (rc) { put_indicator(irq_ptr->dsci); + return rc; + } - return rc; + mutex_lock(&tiq_list_lock); + list_add_rcu(&irq_ptr->entry, &tiq_list); + mutex_unlock(&tiq_list_lock); + return 0; } void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) @@ -197,6 +186,11 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) if (!is_thinint_irq(irq_ptr)) return; + mutex_lock(&tiq_list_lock); + list_del_rcu(&irq_ptr->entry); + mutex_unlock(&tiq_list_lock); + synchronize_rcu(); + /* reset adapter interrupt indicators */ set_subchannel_ind(irq_ptr, 1); put_indicator(irq_ptr->dsci); -- cgit v1.2.3 From bd83917155c1e60a6634dfef708972076b068c6e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 15 Sep 2020 10:04:39 +0300 Subject: s390/qdio: track time of last data IRQ for each device We currently track the time of the most recent QDIO Adapter Interrupt. This is a system-wide timestamp (as such interrupts are not bound to one specific qdio device). If interrupt processing stalls on one device but is functional for a different device, the timestamp continues to be updated and is of no help for problem diagnosis. So for debugging purposes also track the time of the last Data IRQ on a per-device level. Collect this data in the legacy non-AI path as well. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio.h | 1 + drivers/s390/cio/qdio_debug.c | 5 +++-- drivers/s390/cio/qdio_main.c | 1 + drivers/s390/cio/qdio_thinint.c | 4 +++- 4 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 4a4e4de45bc9..da29c3729cd9 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -252,6 +252,7 @@ struct qdio_irq { struct ccw_device *cdev; struct list_head entry; /* list of thinint devices */ struct dentry *debugfs_dev; + u64 last_data_irq_time; unsigned long int_parm; struct subchannel_id schid; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 728abf7e9ccb..d091ae12c367 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -105,8 +105,9 @@ static int qstat_show(struct seq_file *m, void *v) if (!q) return 0; - seq_printf(m, "Timestamp: %Lx Last AI: %Lx\n", - q->timestamp, last_ai_time); + seq_printf(m, "Timestamp: %llx\n", q->timestamp); + seq_printf(m, "Last Data IRQ: %llx Last AI: %llx\n", + q->irq_ptr->last_data_irq_time, last_ai_time); seq_printf(m, "nr_used: %d ftc: %d\n", atomic_read(&q->nr_buf_used), q->first_to_check); if (q->is_input_q) { diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 3b1e8ed4ba14..c56b690f0a93 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -804,6 +804,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) return; qdio_deliver_irq(irq_ptr); + irq_ptr->last_data_irq_time = S390_lowcore.int_clock; if (!pci_out_supported(irq_ptr) || !irq_ptr->scan_threshold) return; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index e1b923633372..8e09bf3a2fcd 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -97,10 +97,11 @@ static inline u32 clear_shared_ind(void) */ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) { + u64 irq_time = S390_lowcore.int_clock; u32 si_used = clear_shared_ind(); struct qdio_irq *irq; - last_ai_time = S390_lowcore.int_clock; + last_ai_time = irq_time; inc_irq_stat(IRQIO_QAI); /* protect tiq_list entries, only changed in activate or shutdown */ @@ -119,6 +120,7 @@ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) } qdio_deliver_irq(irq); + irq->last_data_irq_time = irq_time; QDIO_PERF_STAT_INC(irq, adapter_int); } -- cgit v1.2.3 From d39fae45c97c67b1b4da04773f2bb5a2f29088c4 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 21 Jan 2021 16:03:08 +0100 Subject: s390/zcrypt: return EIO when msg retry limit reached When a msg is retried because the lower ap layer returns -EAGAIN there is a retry limit (currently 10). When this limit is reached the last return code from the lower layer is returned, causing the userspace to get -1 on the ioctl with errno EAGAIN. This EAGAIN is misleading here. After 10 retry attempts the userspace should receive a clear failure indication like EINVAL or EIO or ENODEV. However, the reason why these retries all fail is unclear. On an invalid message EINVAL would be returned by the lower layer, and if devices go away or are not available an ENODEV is seen. So this patch now reworks the retry loops to return EIO to userspace when the retry limit is reached. Fixes: 91ffc519c199 ("s390/zcrypt: introduce msg tracking in zcrypt functions") Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_api.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 10206e4498d0..52eaf51c9bb6 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1438,6 +1438,8 @@ static int icarsamodexpo_ioctl(struct ap_perms *perms, unsigned long arg) if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) { ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSAMODEXPO rc=%d\n", rc); return rc; @@ -1481,6 +1483,8 @@ static int icarsacrt_ioctl(struct ap_perms *perms, unsigned long arg) if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) { ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSACRT rc=%d\n", rc); return rc; @@ -1524,6 +1528,8 @@ static int zsecsendcprb_ioctl(struct ap_perms *perms, unsigned long arg) if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDCPRB rc=%d status=0x%x\n", rc, xcRB.status); @@ -1568,6 +1574,8 @@ static int zsendep11cprb_ioctl(struct ap_perms *perms, unsigned long arg) if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDEP11CPRB rc=%d\n", rc); if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) @@ -1744,6 +1752,8 @@ static long trans_modexpo32(struct ap_perms *perms, struct file *filp, if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) return rc; return put_user(mex64.outputdatalength, @@ -1795,6 +1805,8 @@ static long trans_modexpo_crt32(struct ap_perms *perms, struct file *filp, if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; if (rc) return rc; return put_user(crt64.outputdatalength, @@ -1865,6 +1877,8 @@ static long trans_xcRB32(struct ap_perms *perms, struct file *filp, if (rc == -EAGAIN) tr.again_counter++; } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); + if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX) + rc = -EIO; xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length; xcRB32.reply_data_length = xcRB64.reply_data_length; xcRB32.status = xcRB64.status; -- cgit v1.2.3 From 3bf526e036c9be08e8d3eb7b48c3b27d3d082332 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Sat, 30 Jan 2021 12:28:30 +0100 Subject: s390/qdio: inline qdio_kick_handler() We don't kick the handler for Input Queues anymore. Move the remaining code into its only caller. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio.h | 1 - drivers/s390/cio/qdio_debug.c | 1 - drivers/s390/cio/qdio_main.c | 35 ++++++++++++----------------------- 3 files changed, 12 insertions(+), 25 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index da29c3729cd9..84425e294e36 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -146,7 +146,6 @@ struct qdio_dev_perf_stat { unsigned int siga_sync; unsigned int inbound_call; - unsigned int inbound_handler; unsigned int stop_polling; unsigned int inbound_queue_full; unsigned int outbound_call; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index d091ae12c367..00384f58f218 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -203,7 +203,6 @@ static char *qperf_names[] = { "SIGA write", "SIGA sync", "Inbound calls", - "Inbound handler", "Inbound stop_polling", "Inbound queue full", "Outbound calls", diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index c56b690f0a93..a83101d9ec4a 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -545,28 +545,6 @@ static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q, return phys_aob; } -static void qdio_kick_handler(struct qdio_q *q, unsigned int start, - unsigned int count) -{ - if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) - return; - - if (q->is_input_q) { - qperf_inc(q, inbound_handler); - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count); - } else { - qperf_inc(q, outbound_handler); - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x", - start, count); - } - - q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count, - q->irq_ptr->int_parm); - - /* for the next time */ - q->qdio_error = 0; -} - static inline int qdio_tasklet_schedule(struct qdio_q *q) { if (likely(q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE)) { @@ -729,7 +707,18 @@ void qdio_outbound_tasklet(struct tasklet_struct *t) count = qdio_outbound_q_moved(q, start); if (count) { q->first_to_check = add_buf(start, count); - qdio_kick_handler(q, start, count); + + if (q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE) { + qperf_inc(q, outbound_handler); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x", + start, count); + + q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, + start, count, q->irq_ptr->int_parm); + + /* for the next time */ + q->qdio_error = 0; + } } if (queue_type(q) == QDIO_ZFCP_QFMT && !pci_out_supported(q->irq_ptr) && -- cgit v1.2.3 From 540936df443859244e1a76331524600c35b225d0 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Sat, 30 Jan 2021 12:44:17 +0100 Subject: s390/qdio: rework q->qdio_error indication When inspecting a queue, any error is currently returned back through the queue's qdio_error field. Turn this into a proper variable that gets passed through the call chain, so that the lifetime is clear and the error state can be accessed along the way. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- drivers/s390/cio/qdio.h | 3 --- drivers/s390/cio/qdio_main.c | 32 ++++++++++++++++---------------- 2 files changed, 16 insertions(+), 19 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 84425e294e36..34bf2f197c71 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -214,9 +214,6 @@ struct qdio_q { /* number of buffers in use by the adapter */ atomic_t nr_buf_used; - /* error condition during a data transfer */ - unsigned int qdio_error; - /* last scan of the queue */ u64 timestamp; diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a83101d9ec4a..4252f43ef214 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -420,8 +420,6 @@ static inline void account_sbals(struct qdio_q *q, unsigned int count) static void process_buffer_error(struct qdio_q *q, unsigned int start, int count) { - q->qdio_error = QDIO_ERROR_SLSB_STATE; - /* special handling for no target buffer empty */ if (queue_type(q) == QDIO_IQDIO_QFMT && !q->is_input_q && q->sbal[start]->element[15].sflags == 0x10) { @@ -450,7 +448,8 @@ static inline void inbound_handle_work(struct qdio_q *q, unsigned int start, q->u.in.batch_count += count; } -static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) +static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start, + unsigned int *error) { unsigned char state = 0; int count; @@ -484,6 +483,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in err:%1d %02x", q->nr, count); + *error = QDIO_ERROR_SLSB_STATE; process_buffer_error(q, start, count); inbound_handle_work(q, start, count, false); if (atomic_sub_return(count, &q->nr_buf_used) == 0) @@ -567,7 +567,8 @@ static void qdio_check_pending(struct qdio_q *q, unsigned int index) } } -static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start) +static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, + unsigned int *error) { unsigned char state = 0; int count; @@ -601,6 +602,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start) account_sbals(q, count); return count; case SLSB_P_OUTPUT_ERROR: + *error = QDIO_ERROR_SLSB_STATE; process_buffer_error(q, start, count); atomic_sub(count, &q->nr_buf_used); if (q->irq_ptr->perf_stat_enabled) @@ -631,11 +633,12 @@ static inline int qdio_outbound_q_done(struct qdio_q *q) return atomic_read(&q->nr_buf_used) == 0; } -static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start) +static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start, + unsigned int *error) { int count; - count = get_outbound_buffer_frontier(q, start); + count = get_outbound_buffer_frontier(q, start, error); if (count) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); @@ -699,12 +702,13 @@ void qdio_outbound_tasklet(struct tasklet_struct *t) struct qdio_output_q *out_q = from_tasklet(out_q, t, tasklet); struct qdio_q *q = container_of(out_q, struct qdio_q, u.out); unsigned int start = q->first_to_check; + unsigned int error = 0; int count; qperf_inc(q, tasklet_outbound); WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0); - count = qdio_outbound_q_moved(q, start); + count = qdio_outbound_q_moved(q, start, &error); if (count) { q->first_to_check = add_buf(start, count); @@ -713,11 +717,8 @@ void qdio_outbound_tasklet(struct tasklet_struct *t) DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x", start, count); - q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, - start, count, q->irq_ptr->int_parm); - - /* for the next time */ - q->qdio_error = 0; + q->handler(q->irq_ptr->cdev, error, q->nr, start, + count, q->irq_ptr->int_parm); } } @@ -1472,17 +1473,16 @@ static int __qdio_inspect_queue(struct qdio_q *q, unsigned int *bufnr, unsigned int start = q->first_to_check; int count; - count = q->is_input_q ? get_inbound_buffer_frontier(q, start) : - qdio_outbound_q_moved(q, start); + *error = 0; + count = q->is_input_q ? get_inbound_buffer_frontier(q, start, error) : + qdio_outbound_q_moved(q, start, error); if (count == 0) return 0; *bufnr = start; - *error = q->qdio_error; /* for the next time */ q->first_to_check = add_buf(start, count); - q->qdio_error = 0; return count; } -- cgit v1.2.3 From 7940eaf2e956ce3d67ac9efb5b621adbb823e049 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Sat, 30 Jan 2021 13:04:53 +0100 Subject: s390/qdio: improve handling of PENDING buffers for QEBSM devices For QEBSM devices the 'merge_pending' mechanism in get_buf_states() doesn't apply, and we can actually get SLSB_P_OUTPUT_PENDING returned. So for this case propagating the PENDING state to the driver via the queue's sbal_state doesn't make sense and creates unnecessary overhead. Instead introduce a new QDIO_ERROR_* flag that gets passed to the driver, and triggers the same processing as if the buffers were flagged as QDIO_OUTBUF_STATE_FLAG_PENDING. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/qdio.h | 1 + drivers/s390/cio/qdio_main.c | 11 +++++++++-- drivers/s390/net/qeth_core_main.c | 9 ++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index f96454f5d4cd..c85f75a3d452 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -315,6 +315,7 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int, #define QDIO_ERROR_GET_BUF_STATE 0x0002 #define QDIO_ERROR_SET_BUF_STATE 0x0004 #define QDIO_ERROR_SLSB_STATE 0x0100 +#define QDIO_ERROR_SLSB_PENDING 0x0200 #define QDIO_ERROR_FATAL 0x00ff #define QDIO_ERROR_TEMPORARY 0xff00 diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 4252f43ef214..0ad5a4c1bb08 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -571,6 +571,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, unsigned int *error) { unsigned char state = 0; + unsigned int i; int count; q->timestamp = get_tod_clock_fast(); @@ -591,8 +592,14 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, return 0; switch (state) { - case SLSB_P_OUTPUT_EMPTY: case SLSB_P_OUTPUT_PENDING: + /* detach the utilized QAOBs: */ + for (i = 0; i < count; i++) + q->u.out.aobs[QDIO_BUFNR(start + i)] = NULL; + + *error = QDIO_ERROR_SLSB_PENDING; + fallthrough; + case SLSB_P_OUTPUT_EMPTY: /* the adapter got it */ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %02x", q->nr, count); @@ -643,7 +650,7 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start, if (count) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); - if (q->u.out.use_cq) { + if (q->u.out.use_cq && *error != QDIO_ERROR_SLSB_PENDING) { unsigned int i; for (i = 0; i < count; i++) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index cf18d87da41e..13056cc2377b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6068,14 +6068,17 @@ int qeth_poll(struct napi_struct *napi, int budget) EXPORT_SYMBOL_GPL(qeth_poll); static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, - unsigned int bidx, bool error, int budget) + unsigned int bidx, unsigned int qdio_error, + int budget) { struct qeth_qdio_out_buffer *buffer = queue->bufs[bidx]; u8 sflags = buffer->buffer->element[15].sflags; struct qeth_card *card = queue->card; + bool error = !!qdio_error; - if (queue->bufstates && (queue->bufstates[bidx].flags & - QDIO_OUTBUF_STATE_FLAG_PENDING)) { + if ((qdio_error == QDIO_ERROR_SLSB_PENDING) || + (queue->bufstates && (queue->bufstates[bidx].flags & + QDIO_OUTBUF_STATE_FLAG_PENDING))) { WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); QETH_CARD_TEXT_(card, 5, "pel%u", bidx); -- cgit v1.2.3 From 2223318c2862edc7f5b282939b850b19fc934ec4 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Sat, 30 Jan 2021 13:22:56 +0100 Subject: s390/qdio: remove 'merge_pending' mechanism For non-QEBSM devices, get_buf_states() merges PENDING and EMPTY buffers into a single group of finished buffers. To allow the upper-layer driver to differentiate between the two states, qdio_check_pending() looks at each buffer's state again and sets the sbal_state flag to QDIO_OUTBUF_STATE_FLAG_PENDING accordingly. So effectively we're spending overhead on _every_ Output Queue inspection, just to avoid some additional TX completion calls in case a group of buffers has completed with mixed EMPTY / PENDING state. Given that PENDING buffers should rarely occur, this is a bad trade-off. In particular so as the additional checks in get_buf_states() affect _all_ device types (even those that don't use the PENDING state). Rip it all out, and just report the PENDING completions separately as we already do for QEBSM devices. Signed-off-by: Julian Wiedmann Reviewed-by: Benjamin Block Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/qdio.h | 4 --- drivers/s390/cio/qdio_main.c | 59 ++++++--------------------------------- drivers/s390/net/qeth_core_main.c | 4 +-- 3 files changed, 10 insertions(+), 57 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index c85f75a3d452..d9215c7106f0 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -250,17 +250,13 @@ struct slsb { * struct qdio_outbuf_state - SBAL related asynchronous operation information * (for communication with upper layer programs) * (only required for use with completion queues) - * @flags: flags indicating state of buffer * @user: pointer to upper layer program's state information related to SBAL * (stored in user1 data of QAOB) */ struct qdio_outbuf_state { - u8 flags; void *user; }; -#define QDIO_OUTBUF_STATE_FLAG_PENDING 0x01 - #define CHSC_AC1_INITIATE_INPUTQ 0x80 diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 0ad5a4c1bb08..03a011619908 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -202,7 +202,7 @@ again: */ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, unsigned char *state, unsigned int count, - int auto_ack, int merge_pending) + int auto_ack) { unsigned char __state = 0; int i = 1; @@ -217,18 +217,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, if (__state & SLSB_OWNER_CU) goto out; - if (merge_pending && __state == SLSB_P_OUTPUT_PENDING) - __state = SLSB_P_OUTPUT_EMPTY; - for (; i < count; i++) { bufnr = next_buf(bufnr); - /* merge PENDING into EMPTY: */ - if (merge_pending && - q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING && - __state == SLSB_P_OUTPUT_EMPTY) - continue; - /* stop if next state differs from initial state: */ if (q->slsb.val[bufnr] != __state) break; @@ -242,7 +233,7 @@ out: static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state, int auto_ack) { - return get_buf_states(q, bufnr, state, 1, auto_ack, 0); + return get_buf_states(q, bufnr, state, 1, auto_ack); } /* wrap-around safe setting of slsb states, returns number of changed buffers */ @@ -464,7 +455,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start, * No siga sync here, as a PCI or we after a thin interrupt * already sync'ed the queues. */ - count = get_buf_states(q, start, &state, count, 1, 0); + count = get_buf_states(q, start, &state, count, 1); if (!count) return 0; @@ -541,7 +532,6 @@ static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q, WARN_ON_ONCE(phys_aob & 0xFF); } - q->sbal_state[bufnr].flags = 0; return phys_aob; } @@ -554,19 +544,6 @@ static inline int qdio_tasklet_schedule(struct qdio_q *q) return -EPERM; } -static void qdio_check_pending(struct qdio_q *q, unsigned int index) -{ - unsigned char state; - - if (get_buf_state(q, index, &state, 0) > 0 && - state == SLSB_P_OUTPUT_PENDING && - q->u.out.aobs[index]) { - q->u.out.sbal_state[index].flags |= - QDIO_OUTBUF_STATE_FLAG_PENDING; - q->u.out.aobs[index] = NULL; - } -} - static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, unsigned int *error) { @@ -587,7 +564,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, if (!count) return 0; - count = get_buf_states(q, start, &state, count, 0, q->u.out.use_cq); + count = get_buf_states(q, start, &state, count, 0); if (!count) return 0; @@ -609,6 +586,9 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, account_sbals(q, count); return count; case SLSB_P_OUTPUT_ERROR: + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out error:%1d %02x", + q->nr, count); + *error = QDIO_ERROR_SLSB_STATE; process_buffer_error(q, start, count); atomic_sub(count, &q->nr_buf_used); @@ -640,27 +620,6 @@ static inline int qdio_outbound_q_done(struct qdio_q *q) return atomic_read(&q->nr_buf_used) == 0; } -static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start, - unsigned int *error) -{ - int count; - - count = get_outbound_buffer_frontier(q, start, error); - - if (count) { - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); - - if (q->u.out.use_cq && *error != QDIO_ERROR_SLSB_PENDING) { - unsigned int i; - - for (i = 0; i < count; i++) - qdio_check_pending(q, QDIO_BUFNR(start + i)); - } - } - - return count; -} - static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count, unsigned long aob) { @@ -715,7 +674,7 @@ void qdio_outbound_tasklet(struct tasklet_struct *t) qperf_inc(q, tasklet_outbound); WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0); - count = qdio_outbound_q_moved(q, start, &error); + count = get_outbound_buffer_frontier(q, start, &error); if (count) { q->first_to_check = add_buf(start, count); @@ -1482,7 +1441,7 @@ static int __qdio_inspect_queue(struct qdio_q *q, unsigned int *bufnr, *error = 0; count = q->is_input_q ? get_inbound_buffer_frontier(q, start, error) : - qdio_outbound_q_moved(q, start, error); + get_outbound_buffer_frontier(q, start, error); if (count == 0) return 0; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 13056cc2377b..068e2d9b1eb8 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6076,9 +6076,7 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, struct qeth_card *card = queue->card; bool error = !!qdio_error; - if ((qdio_error == QDIO_ERROR_SLSB_PENDING) || - (queue->bufstates && (queue->bufstates[bidx].flags & - QDIO_OUTBUF_STATE_FLAG_PENDING))) { + if (qdio_error == QDIO_ERROR_SLSB_PENDING) { WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); QETH_CARD_TEXT_(card, 5, "pel%u", bidx); -- cgit v1.2.3