From e45a64975b021b6fc9fdd0dd0b74539ae1b5aa86 Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Fri, 16 Nov 2018 11:47:48 +0100 Subject: [PATCH 01/12] s390: vfio-ap: include for test_facility() The driver uses test_facility(), but does not include the corresponding include file explicitly. The driver currently builds only thanks to the following include chain: vfio_ap_drv.c Files should not rely on such fragile implicit includes. Signed-off-by: Petr Tesarik Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Signed-off-by: Halil Pasic Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/vfio_ap_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c index 7667b38728f0..31c6c847eaca 100644 --- a/drivers/s390/crypto/vfio_ap_drv.c +++ b/drivers/s390/crypto/vfio_ap_drv.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "vfio_ap_private.h" #define VFIO_AP_ROOT_NAME "vfio_ap" From 159491f3b509bd8101199944dc7b0673b881c734 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Fri, 16 Nov 2018 15:48:10 +0100 Subject: [PATCH 02/12] s390/ap: rework assembler functions to use unions for in/out register variables The inline assembler functions ap_aqic() and ap_qact() used two variables declared on the very same register. One variable was for input only, the other for output. Looks like newer versions of the gcc don't like this. Anyway it is a better coding to use one variable (which may have a union data type) on one register for input and output. So this patch introduces unions and uses only one variable now for input and output for GR1 for the PQAP(QACT) and PQAP(QIC) invocation. Signed-off-by: Harald Freudenberger Acked-by: Ilya Leoshkevich Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/ap.h | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index 8c00fd509c45..1a6a7092d942 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -221,16 +221,22 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid, void *ind) { register unsigned long reg0 asm ("0") = qid | (3UL << 24); - register struct ap_qirq_ctrl reg1_in asm ("1") = qirqctrl; - register struct ap_queue_status reg1_out asm ("1"); + register union { + unsigned long value; + struct ap_qirq_ctrl qirqctrl; + struct ap_queue_status status; + } reg1 asm ("1"); register void *reg2 asm ("2") = ind; + reg1.qirqctrl = qirqctrl; + asm volatile( ".long 0xb2af0000" /* PQAP(AQIC) */ - : "=d" (reg1_out) - : "d" (reg0), "d" (reg1_in), "d" (reg2) + : "+d" (reg1) + : "d" (reg0), "d" (reg2) : "cc"); - return reg1_out; + + return reg1.status; } /* @@ -264,17 +270,21 @@ static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit, { register unsigned long reg0 asm ("0") = qid | (5UL << 24) | ((ifbit & 0x01) << 22); - register unsigned long reg1_in asm ("1") = apinfo->val; - register struct ap_queue_status reg1_out asm ("1"); + register union { + unsigned long value; + struct ap_queue_status status; + } reg1 asm ("1"); register unsigned long reg2 asm ("2"); + reg1.value = apinfo->val; + asm volatile( ".long 0xb2af0000" /* PQAP(QACT) */ - : "+d" (reg1_in), "=d" (reg1_out), "=d" (reg2) + : "+d" (reg1), "=d" (reg2) : "d" (reg0) : "cc"); apinfo->val = reg2; - return reg1_out; + return reg1.status; } /** From be534791011100d204602e2e0496e9e6ce8edf63 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 19 Nov 2018 11:36:13 +0100 Subject: [PATCH 03/12] s390/zcrypt: improve special ap message cmd handling There exist very few ap messages which need to have the 'special' flag enabled. This flag tells the firmware layer to do some pre- and maybe postprocessing. However, it may happen that this special flag is enabled but the firmware is unable to deal with this kind of message and thus returns with reply code 0x41. For example older firmware may not know the newest messages triggered by the zcrypt device driver and thus react with reject and the named reply code. Unfortunately this reply code is not known to the zcrypt error routines and thus default behavior is to switch the ap queue offline. This patch now makes the ap error routine aware of the reply code and so userspace is informed about the bad processing result but the queue is not switched to offline state any more. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- arch/s390/include/uapi/asm/zcrypt.h | 4 ++-- drivers/s390/crypto/zcrypt_error.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/uapi/asm/zcrypt.h b/arch/s390/include/uapi/asm/zcrypt.h index 42c81a95e97b..494c34c50716 100644 --- a/arch/s390/include/uapi/asm/zcrypt.h +++ b/arch/s390/include/uapi/asm/zcrypt.h @@ -150,8 +150,8 @@ struct ica_xcRB { * @cprb_len: CPRB header length [0x0020] * @cprb_ver_id: CPRB version id. [0x04] * @pad_000: Alignment pad bytes - * @flags: Admin cmd [0x80] or functional cmd [0x00] - * @func_id: Function id / subtype [0x5434] + * @flags: Admin bit [0x80], Special bit [0x20] + * @func_id: Function id / subtype [0x5434] "T4" * @source_id: Source id [originator id] * @target_id: Target id [usage/ctrl domain id] * @ret_code: Return code diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h index 240b27f3f5f6..f34ee41cbed8 100644 --- a/drivers/s390/crypto/zcrypt_error.h +++ b/drivers/s390/crypto/zcrypt_error.h @@ -51,6 +51,7 @@ struct error_hdr { #define REP82_ERROR_FORMAT_FIELD 0x29 #define REP82_ERROR_INVALID_COMMAND 0x30 #define REP82_ERROR_MALFORMED_MSG 0x40 +#define REP82_ERROR_INVALID_SPECIAL_CMD 0x41 #define REP82_ERROR_INVALID_DOMAIN_PRECHECK 0x42 #define REP82_ERROR_RESERVED_FIELDO 0x50 /* old value */ #define REP82_ERROR_WORD_ALIGNMENT 0x60 @@ -89,6 +90,7 @@ static inline int convert_error(struct zcrypt_queue *zq, case REP88_ERROR_MESSAGE_MALFORMD: case REP82_ERROR_INVALID_DOMAIN_PRECHECK: case REP82_ERROR_INVALID_DOMAIN_PENDING: + case REP82_ERROR_INVALID_SPECIAL_CMD: // REP88_ERROR_INVALID_KEY // '82' CEX2A // REP88_ERROR_OPERAND // '84' CEX2A // REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A From 5b39fc049ce1232a56628408058ae04f353ac705 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 25 Oct 2018 16:05:43 +0900 Subject: [PATCH 04/12] s390: use common bust_spinlocks() s390 is the only architecture that is using own bust_spinlocks() variant, while other arch-s seem to be OK with the common implementation. Heiko Carstens [1] said he would prefer s390 to use the common bust_spinlocks() as well: I did some code archaeology and this function is unchanged since ~17 years. When it was introduced it was close to identical to the x86 variant. All other architectures use the common code variant in the meantime. So if we change this I'd prefer that we switch s390 to the common code variant as well. Right now I can't see a reason for not doing that This patch removes s390 bust_spinlocks() and drops the weak attribute from the common bust_spinlocks() version. [1] lkml.kernel.org/r/20181025062800.GB4037@osiris Signed-off-by: Sergey Senozhatsky Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/mm/fault.c | 24 ------------------------ lib/bust_spinlocks.c | 6 +++--- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 2b8f32f56e0c..11613362c4e7 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -81,30 +81,6 @@ static inline int notify_page_fault(struct pt_regs *regs) return ret; } - -/* - * Unlock any spinlocks which will prevent us from getting the - * message out. - */ -void bust_spinlocks(int yes) -{ - if (yes) { - oops_in_progress = 1; - } else { - int loglevel_save = console_loglevel; - console_unblank(); - oops_in_progress = 0; - /* - * OK, the message is on the console. Now we call printk() - * without oops_in_progress set so that printk will give klogd - * a poke. Hold onto your hats... - */ - console_loglevel = 15; - printk(" "); - console_loglevel = loglevel_save; - } -} - /* * Find out which address space caused the exception. * Access register mode is impossible, ignore space == 3. diff --git a/lib/bust_spinlocks.c b/lib/bust_spinlocks.c index ab719495e2cb..8be59f84eaea 100644 --- a/lib/bust_spinlocks.c +++ b/lib/bust_spinlocks.c @@ -2,7 +2,8 @@ /* * lib/bust_spinlocks.c * - * Provides a minimal bust_spinlocks for architectures which don't have one of their own. + * Provides a minimal bust_spinlocks for architectures which don't + * have one of their own. * * bust_spinlocks() clears any spinlocks which would prevent oops, die(), BUG() * and panic() information from reaching the user. @@ -16,8 +17,7 @@ #include #include - -void __attribute__((weak)) bust_spinlocks(int yes) +void bust_spinlocks(int yes) { if (yes) { ++oops_in_progress; From 1554509b0d007287ecd4be887ae94d3730cbf2b7 Mon Sep 17 00:00:00 2001 From: Pierre Morel Date: Wed, 17 Oct 2018 11:18:39 +0200 Subject: [PATCH 05/12] vfio: ccw: Merge BUSY and BOXED states VFIO_CCW_STATE_BOXED and VFIO_CCW_STATE_BUSY have identical actions for the same events. Let's merge both into a single state to simplify the code. We choose to keep VFIO_CCW_STATE_BUSY. Signed-off-by: Pierre Morel Message-Id: <1539767923-10539-2-git-send-email-pmorel@linux.ibm.com> Reviewed-by: Eric Farman Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_fsm.c | 7 +------ drivers/s390/cio/vfio_ccw_private.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index f94aa01f9c36..cab17865aafe 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -130,7 +130,7 @@ static void fsm_io_request(struct vfio_ccw_private *private, struct mdev_device *mdev = private->mdev; char *errstr = "request"; - private->state = VFIO_CCW_STATE_BOXED; + private->state = VFIO_CCW_STATE_BUSY; memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); @@ -216,11 +216,6 @@ fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = { [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, }, - [VFIO_CCW_STATE_BOXED] = { - [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, - [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, - [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, - }, [VFIO_CCW_STATE_BUSY] = { [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index 078e46f9623d..08e9a7dc9176 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -63,7 +63,6 @@ enum vfio_ccw_state { VFIO_CCW_STATE_NOT_OPER, VFIO_CCW_STATE_STANDBY, VFIO_CCW_STATE_IDLE, - VFIO_CCW_STATE_BOXED, VFIO_CCW_STATE_BUSY, /* last element! */ NR_VFIO_CCW_STATES From 4868d2b662f55ff2e27e92c44b14016531a022a0 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 12 Dec 2018 15:59:29 +0100 Subject: [PATCH 06/12] MAINTAINERS/vfio-ccw: add Farhan and Eric, make Halil Reviewer Eric and Farhan will help with maintaining vfio-ccw. Cc: Cornelia Huck Cc: Halil Pasic Cc: Farhan Ali Cc: Eric Farman Signed-off-by: Christian Borntraeger Message-Id: <20181212145929.136522-1-borntraeger@de.ibm.com> Acked-by: Halil Pasic Acked-by: Farhan Ali Acked-by: Eric Farman Signed-off-by: Cornelia Huck --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5d17f568a3b9..2e9d876d65d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13021,7 +13021,9 @@ F: drivers/pci/hotplug/s390_pci_hpc.c S390 VFIO-CCW DRIVER M: Cornelia Huck -M: Halil Pasic +M: Farhan Ali +M: Eric Farman +R: Halil Pasic L: linux-s390@vger.kernel.org L: kvm@vger.kernel.org S: Supported From ca92b93d17f8c546d10ae175430fb22d6bd2d60b Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 1 Dec 2018 22:28:11 -0500 Subject: [PATCH 07/12] s390: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 15 +-------------- drivers/s390/cio/qdio_debug.c | 16 ++-------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 5e9ebdb0594c..397af07e4d88 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1192,20 +1192,7 @@ static int dasd_hosts_show(struct seq_file *m, void *v) return rc; } -static int dasd_hosts_open(struct inode *inode, struct file *file) -{ - struct dasd_device *device = inode->i_private; - - return single_open(file, dasd_hosts_show, device); -} - -static const struct file_operations dasd_hosts_fops = { - .owner = THIS_MODULE, - .open = dasd_hosts_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(dasd_hosts); static void dasd_hosts_exit(struct dasd_device *device) { diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 68a82f3e2e92..040061f2bfff 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -190,19 +190,7 @@ static int qstat_show(struct seq_file *m, void *v) return 0; } -static int qstat_seq_open(struct inode *inode, struct file *filp) -{ - return single_open(filp, qstat_show, - file_inode(filp)->i_private); -} - -static const struct file_operations debugfs_fops = { - .owner = THIS_MODULE, - .open = qstat_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(qstat); static char *qperf_names[] = { "Assumed adapter interrupts", @@ -306,7 +294,7 @@ static void setup_debugfs_entry(struct qdio_q *q) q->is_input_q ? "input" : "output", q->nr); q->debugfs_q = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, - q->irq_ptr->debugfs_dev, q, &debugfs_fops); + q->irq_ptr->debugfs_dev, q, &qstat_fops); if (IS_ERR(q->debugfs_q)) q->debugfs_q = NULL; } From 87ccdcfa9c706be835fea226eda0b1ae9c671413 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 3 Dec 2018 13:19:12 +0100 Subject: [PATCH 08/12] s390/drivers: fix proc/debugfs file permissions Remove write permissions for fops without a write callback. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_proc.c | 3 +-- drivers/s390/char/tape_proc.c | 7 ++----- drivers/s390/cio/qdio_debug.c | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 5cb80c645489..1770b99f607e 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -339,8 +339,7 @@ dasd_proc_init(void) dasd_proc_root_entry = proc_mkdir("dasd", NULL); if (!dasd_proc_root_entry) goto out_nodasd; - dasd_devices_entry = proc_create_seq("devices", - S_IFREG | S_IRUGO | S_IWUSR, + dasd_devices_entry = proc_create_seq("devices", 0444, dasd_proc_root_entry, &dasd_devices_seq_ops); if (!dasd_devices_entry) diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index 32a14ee31c6b..2238d9df6c47 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -111,11 +111,8 @@ static const struct seq_operations tape_proc_seq = { void tape_proc_init(void) { - tape_proc_devices = proc_create_seq("tapedevices", - S_IFREG | S_IRUGO | S_IWUSR, NULL, &tape_proc_seq); - if (tape_proc_devices == NULL) { - return; - } + tape_proc_devices = proc_create_seq("tapedevices", 0444, NULL, + &tape_proc_seq); } /* diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 040061f2bfff..d2f98e5829d4 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -293,7 +293,7 @@ static void setup_debugfs_entry(struct qdio_q *q) snprintf(name, QDIO_DEBUGFS_NAME_LEN, "%s_%d", q->is_input_q ? "input" : "output", q->nr); - q->debugfs_q = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, + q->debugfs_q = debugfs_create_file(name, 0444, q->irq_ptr->debugfs_dev, q, &qstat_fops); if (IS_ERR(q->debugfs_q)) q->debugfs_q = NULL; From 9594ca6b87d9f11e9f14ac31581e0e5d79a8e839 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 30 Oct 2018 14:04:46 +0100 Subject: [PATCH 09/12] s390/pci: remove bit_lock usage in interrupt handler The interrupt handler uses bit_spin_lock around a call to retrieve per irq data (the irq number). However this per irq data is only set during irq setup time and never changed until the irq is freed. Thus it's safe to remove the lock usage. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/pci/pci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 9f6f392a4461..6df622fb406d 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -382,9 +382,7 @@ static void zpci_irq_handler(struct airq_struct *airq) if (ai == -1UL) break; inc_irq_stat(IRQIO_MSI); - airq_iv_lock(aibv, ai); generic_handle_irq(airq_iv_get_data(aibv, ai)); - airq_iv_unlock(aibv, ai); } } } @@ -410,7 +408,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) zdev->aisb = aisb; /* Create adapter interrupt vector */ - zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK); + zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA); if (!zdev->aibv) return -ENOMEM; From 98dfd32620e970eb576ebce5ea39d905cb005e72 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 18 Oct 2018 11:11:08 +0200 Subject: [PATCH 10/12] s390/pci: fix sleeping in atomic during hotplug When triggered by pci hotplug (PEC 0x306) clp_get_state is called with spinlocks held resulting in the following warning: zpci: n/a: Event 0x306 reconfigured PCI function 0x0 BUG: sleeping function called from invalid context at mm/page_alloc.c:4324 in_atomic(): 1, irqs_disabled(): 0, pid: 98, name: kmcheck 2 locks held by kmcheck/98: Change the allocation to use GFP_ATOMIC. Cc: stable@vger.kernel.org # 4.13+ Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/pci/pci_clp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index 19b2d2a9b43d..eeb7450db18c 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -436,7 +436,7 @@ int clp_get_state(u32 fid, enum zpci_state *state) struct clp_state_data sd = {fid, ZPCI_FN_STATE_RESERVED}; int rc; - rrb = clp_alloc_block(GFP_KERNEL); + rrb = clp_alloc_block(GFP_ATOMIC); if (!rrb) return -ENOMEM; From 42a87d4103ae365e18c3be1333592ab583b8ad9d Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 26 Nov 2018 15:50:04 +0100 Subject: [PATCH 11/12] s390/zcrypt: make sysfs reset attribute trigger queue reset Until now there is no way to reset a AP queue or card. Driving a card or queue offline and online again does only toggle the 'software' online state. The only way to trigger a (hardware) reset is by running hot-unplug/hot-plug for example on the HMC. This patch makes the queue reset attribute in sysfs writable. Writing into this attribute triggers a reset on the AP queue's state machine. So the AP queue is flushed and state machine runs through the initial states which cause a reset (PQAP(RAPQ)) and a re-registration to interrupts (PQAP(AQIC)) if available. The reset sysfs attribute is writable by root only. So only an administrator is allowed to initiate a reset of AP queues. Please note that the queue's counter values are left untouched by the reset. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_queue.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index 0aa4b3ccc948..576ac08777c5 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -14,6 +14,9 @@ #include #include "ap_bus.h" +#include "ap_debug.h" + +static void __ap_flush_queue(struct ap_queue *aq); /** * ap_queue_enable_interruption(): Enable interruption on an AP queue. @@ -541,7 +544,25 @@ static ssize_t reset_show(struct device *dev, return rc; } -static DEVICE_ATTR_RO(reset); +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + + spin_lock_bh(&aq->lock); + __ap_flush_queue(aq); + aq->state = AP_STATE_RESET_START; + ap_wait(ap_sm_event(aq, AP_EVENT_POLL)); + spin_unlock_bh(&aq->lock); + + AP_DBF(DBF_INFO, "reset queue=%02x.%04x triggered by user\n", + AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid)); + + return count; +} + +static DEVICE_ATTR_RW(reset); static ssize_t interrupt_show(struct device *dev, struct device_attribute *attr, char *buf) From a7b1868a5f473fb93d912a618883cef0d43653b9 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 29 Nov 2018 11:50:16 +0100 Subject: [PATCH 12/12] s390/zcrypt: rework ap scan bus code Rework of the AP bus scan code. The ap_scan_bus() function is large, so this patch splits the code by introducing a new new function _ap_scan_bus_adapter() which deals with just one adapter and thus reduces the scan function code complexity. Now the AP bus scan can handle a type change of an crypto adapter on the fly (e.g. from CEX5 to CEX6). This may be the case with newer versions of zVM where the card may be pure virtual and a type change is just one click. However a type or function change requires to unregister all queue devices and the card device and re-register them. Comments around the AP bus scan code have been added and/or improved to provide some hopefully useful hints about what the code is actually doing. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 297 ++++++++++++++++++++--------------- 1 file changed, 170 insertions(+), 127 deletions(-) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 9f5a201c4c87..48ea0004a56d 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -299,7 +299,7 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type, ap_max_domain_id = 15; switch (*device_type) { /* For CEX2 and CEX3 the available functions - * are not refrected by the facilities bits. + * are not reflected by the facilities bits. * Instead it is coded into the type. So here * modify the function bits based on the type. */ @@ -1317,7 +1317,7 @@ static int ap_get_compatible_type(ap_qid_t qid, int rawtype, unsigned int func) } /* - * helper function to be used with bus_find_dev + * Helper function to be used with bus_find_dev * matches for the card device with the given id */ static int __match_card_device_with_id(struct device *dev, void *data) @@ -1325,7 +1325,8 @@ static int __match_card_device_with_id(struct device *dev, void *data) return is_card_dev(dev) && to_ap_card(dev)->id == (int)(long) data; } -/* helper function to be used with bus_find_dev +/* + * Helper function to be used with bus_find_dev * matches for the queue device with a given qid */ static int __match_queue_device_with_qid(struct device *dev, void *data) @@ -1333,143 +1334,185 @@ static int __match_queue_device_with_qid(struct device *dev, void *data) return is_queue_dev(dev) && to_ap_queue(dev)->qid == (int)(long) data; } +/* + * Helper function for ap_scan_bus(). + * Does the scan bus job for the given adapter id. + */ +static void _ap_scan_bus_adapter(int id) +{ + ap_qid_t qid; + unsigned int func; + struct ap_card *ac; + struct device *dev; + struct ap_queue *aq; + int rc, dom, depth, type, comp_type, borked; + + /* check if there is a card device registered with this id */ + dev = bus_find_device(&ap_bus_type, NULL, + (void *)(long) id, + __match_card_device_with_id); + ac = dev ? to_ap_card(dev) : NULL; + if (!ap_test_config_card_id(id)) { + if (dev) { + /* Card device has been removed from configuration */ + bus_for_each_dev(&ap_bus_type, NULL, + (void *)(long) id, + __ap_queue_devices_with_id_unregister); + device_unregister(dev); + put_device(dev); + } + return; + } + + /* + * This card id is enabled in the configuration. If we already have + * a card device with this id, check if type and functions are still + * the very same. Also verify that at least one queue is available. + */ + if (ac) { + /* find the first valid queue */ + for (dom = 0; dom < AP_DOMAINS; dom++) { + qid = AP_MKQID(id, dom); + if (ap_query_queue(qid, &depth, &type, &func) == 0) + break; + } + borked = 0; + if (dom >= AP_DOMAINS) { + /* no accessible queue on this card */ + borked = 1; + } else if (ac->raw_hwtype != type) { + /* card type has changed */ + AP_DBF(DBF_INFO, "card=%02x type changed.\n", id); + borked = 1; + } else if (ac->functions != func) { + /* card functions have changed */ + AP_DBF(DBF_INFO, "card=%02x functions changed.\n", id); + borked = 1; + } + if (borked) { + /* unregister card device and associated queues */ + bus_for_each_dev(&ap_bus_type, NULL, + (void *)(long) id, + __ap_queue_devices_with_id_unregister); + device_unregister(dev); + put_device(dev); + /* go back if there is no valid queue on this card */ + if (dom >= AP_DOMAINS) + return; + ac = NULL; + } + } + + /* + * Go through all possible queue ids. Check and maybe create or release + * queue devices for this card. If there exists no card device yet, + * create a card device also. + */ + for (dom = 0; dom < AP_DOMAINS; dom++) { + qid = AP_MKQID(id, dom); + dev = bus_find_device(&ap_bus_type, NULL, + (void *)(long) qid, + __match_queue_device_with_qid); + aq = dev ? to_ap_queue(dev) : NULL; + if (!ap_test_config_domain(dom)) { + if (dev) { + /* Queue device exists but has been + * removed from configuration. + */ + device_unregister(dev); + put_device(dev); + } + continue; + } + /* try to fetch infos about this queue */ + rc = ap_query_queue(qid, &depth, &type, &func); + if (dev) { + if (rc == -ENODEV) + borked = 1; + else { + spin_lock_bh(&aq->lock); + borked = aq->state == AP_STATE_BORKED; + spin_unlock_bh(&aq->lock); + } + if (borked) /* Remove broken device */ + device_unregister(dev); + put_device(dev); + continue; + } + if (rc) + continue; + /* a new queue device is needed, check out comp type */ + comp_type = ap_get_compatible_type(qid, type, func); + if (!comp_type) + continue; + /* maybe a card device needs to be created first */ + if (!ac) { + ac = ap_card_create(id, depth, type, comp_type, func); + if (!ac) + continue; + ac->ap_dev.device.bus = &ap_bus_type; + ac->ap_dev.device.parent = ap_root_device; + dev_set_name(&ac->ap_dev.device, "card%02x", id); + /* Register card device with AP bus */ + rc = device_register(&ac->ap_dev.device); + if (rc) { + put_device(&ac->ap_dev.device); + ac = NULL; + break; + } + /* get it and thus adjust reference counter */ + get_device(&ac->ap_dev.device); + } + /* now create the new queue device */ + aq = ap_queue_create(qid, comp_type); + if (!aq) + continue; + aq->card = ac; + aq->ap_dev.device.bus = &ap_bus_type; + aq->ap_dev.device.parent = &ac->ap_dev.device; + dev_set_name(&aq->ap_dev.device, "%02x.%04x", id, dom); + /* Register queue device */ + rc = device_register(&aq->ap_dev.device); + if (rc) { + put_device(&aq->ap_dev.device); + continue; + } + } /* end domain loop */ + + if (ac) + put_device(&ac->ap_dev.device); +} + /** * ap_scan_bus(): Scan the AP bus for new devices * Runs periodically, workqueue timer (ap_config_time) */ static void ap_scan_bus(struct work_struct *unused) { - struct ap_queue *aq; - struct ap_card *ac; - struct device *dev; - ap_qid_t qid; - int comp_type, depth = 0, type = 0; - unsigned int func = 0; - int rc, id, dom, borked, domains, defdomdevs = 0; + int id; AP_DBF(DBF_DEBUG, "%s running\n", __func__); ap_query_configuration(ap_configuration); ap_select_domain(); - for (id = 0; id < AP_DEVICES; id++) { - /* check if device is registered */ - dev = bus_find_device(&ap_bus_type, NULL, - (void *)(long) id, - __match_card_device_with_id); - ac = dev ? to_ap_card(dev) : NULL; - if (!ap_test_config_card_id(id)) { - if (dev) { - /* Card device has been removed from - * configuration, remove the belonging - * queue devices. - */ - bus_for_each_dev(&ap_bus_type, NULL, - (void *)(long) id, - __ap_queue_devices_with_id_unregister); - /* now remove the card device */ - device_unregister(dev); - put_device(dev); - } - continue; - } - /* According to the configuration there should be a card - * device, so check if there is at least one valid queue - * and maybe create queue devices and the card device. - */ - domains = 0; - for (dom = 0; dom < AP_DOMAINS; dom++) { - qid = AP_MKQID(id, dom); - dev = bus_find_device(&ap_bus_type, NULL, - (void *)(long) qid, - __match_queue_device_with_qid); - aq = dev ? to_ap_queue(dev) : NULL; - if (!ap_test_config_domain(dom)) { - if (dev) { - /* Queue device exists but has been - * removed from configuration. - */ - device_unregister(dev); - put_device(dev); - } - continue; - } - rc = ap_query_queue(qid, &depth, &type, &func); - if (dev) { - spin_lock_bh(&aq->lock); - if (rc == -ENODEV || - /* adapter reconfiguration */ - (ac && ac->functions != func)) - aq->state = AP_STATE_BORKED; - borked = aq->state == AP_STATE_BORKED; - spin_unlock_bh(&aq->lock); - if (borked) /* Remove broken device */ - device_unregister(dev); - put_device(dev); - if (!borked) { - domains++; - if (dom == ap_domain_index) - defdomdevs++; - continue; - } - } - if (rc) - continue; - /* a new queue device is needed, check out comp type */ - comp_type = ap_get_compatible_type(qid, type, func); - if (!comp_type) - continue; - /* maybe a card device needs to be created first */ - if (!ac) { - ac = ap_card_create(id, depth, type, - comp_type, func); - if (!ac) - continue; - ac->ap_dev.device.bus = &ap_bus_type; - ac->ap_dev.device.parent = ap_root_device; - dev_set_name(&ac->ap_dev.device, - "card%02x", id); - /* Register card with AP bus */ - rc = device_register(&ac->ap_dev.device); - if (rc) { - put_device(&ac->ap_dev.device); - ac = NULL; - break; - } - /* get it and thus adjust reference counter */ - get_device(&ac->ap_dev.device); - } - /* now create the new queue device */ - aq = ap_queue_create(qid, comp_type); - if (!aq) - continue; - aq->card = ac; - aq->ap_dev.device.bus = &ap_bus_type; - aq->ap_dev.device.parent = &ac->ap_dev.device; - dev_set_name(&aq->ap_dev.device, - "%02x.%04x", id, dom); - /* Register device */ - rc = device_register(&aq->ap_dev.device); - if (rc) { - put_device(&aq->ap_dev.device); - continue; - } - domains++; - if (dom == ap_domain_index) - defdomdevs++; - } /* end domain loop */ - if (ac) { - /* remove card dev if there are no queue devices */ - if (!domains) - device_unregister(&ac->ap_dev.device); - put_device(&ac->ap_dev.device); - } - } /* end device loop */ + /* loop over all possible adapters */ + for (id = 0; id < AP_DEVICES; id++) + _ap_scan_bus_adapter(id); - if (ap_domain_index >= 0 && defdomdevs < 1) - AP_DBF(DBF_INFO, - "no queue device with default domain %d available\n", - ap_domain_index); + /* check if there is at least one queue available with default domain */ + if (ap_domain_index >= 0) { + struct device *dev = + bus_find_device(&ap_bus_type, NULL, + (void *)(long) ap_domain_index, + __match_queue_device_with_qid); + if (dev) + put_device(dev); + else + AP_DBF(DBF_INFO, + "no queue device with default domain %d available\n", + ap_domain_index); + } mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); }