From 98ce70b76942626fc36c1a972fe1c5a303ac716d Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Tue, 2 Mar 2021 17:28:12 +0100 Subject: s390/sclp: increase sclp console line length Kernel and console/TTY messages written to the SCLP line mode console are wrapped at 80 characters per line by the associated SCLP driver. This makes long lines of output difficult to read, and requires editing of wrapped lines copied from the output device. Neither the firmware interface used to access the SCLP console, nor the HMC "Operating System Messages" web interface that displays these messages require such a length limit. Also other operating systems such as z/VM do not impose similar limits on messages they emit to the same console device. This patch therefore increases the limit to 320 characters per line to make SCLP line mode console output more readable. As a result 99% of lines written during a typical boot will not be wrapped, compared to about 50% wrapped lines at 80 characters per line. Another positive side-effect of this change is that the HMC console interface is able to keep more messages in its history buffer due to fewer line-breaks being generated. In a worst case scenario this means that a 4k console buffer is emitted with the last ~400 bytes empty (320 text + 78 headers). This is more than offset by the fact that each line that is not truncated saves 78 header bytes in the buffer. As a result the actual number of emitted buffers should be about the same as with the 80 character limit. This patch also removes the differentiation between line lengths of SCLP line mode output on z/VM and non-z/VM systems. While the z/VM hypervisor adds a prefix in front of each line ('xx: ' where xx is the number of the CPU issuing the message), adjusting Linux line lengths did not significantly increase readability of console output, and makes even less of a difference with longer lines. Signed-off-by: Peter Oberparleiter Tested-by: Niklas Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/sclp_con.c | 19 ++++--------------- drivers/s390/char/sclp_tty.c | 9 ++------- 2 files changed, 6 insertions(+), 22 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 8966a1c1b548..8c5c95bf89e0 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -41,8 +41,8 @@ static int sclp_con_suspended; static int sclp_con_queue_running; /* Output format for console messages */ -static unsigned short sclp_con_columns; -static unsigned short sclp_con_width_htab; +#define SCLP_CON_COLUMNS 320 +#define SPACES_PER_TAB 8 static void sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) @@ -189,8 +189,8 @@ sclp_console_write(struct console *console, const char *message, } page = sclp_con_pages.next; list_del((struct list_head *) page); - sclp_conbuf = sclp_make_buffer(page, sclp_con_columns, - sclp_con_width_htab); + sclp_conbuf = sclp_make_buffer(page, SCLP_CON_COLUMNS, + SPACES_PER_TAB); } /* try to write the string to the current output buffer */ written = sclp_write(sclp_conbuf, (const unsigned char *) @@ -333,17 +333,6 @@ sclp_console_init(void) sclp_conbuf = NULL; timer_setup(&sclp_con_timer, sclp_console_timeout, 0); - /* Set output format */ - if (MACHINE_IS_VM) - /* - * save 4 characters for the CPU number - * written at start of each line by VM/CP - */ - sclp_con_columns = 76; - else - sclp_con_columns = 80; - sclp_con_width_htab = 8; - /* enable printk-access to this driver */ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); register_reboot_notifier(&on_reboot_nb); diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 013bcc331305..4af8084df169 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -54,8 +54,8 @@ static unsigned short int sclp_tty_chars_count; struct tty_driver *sclp_tty_driver; static int sclp_tty_tolower; -static int sclp_tty_columns = 80; +#define SCLP_TTY_COLUMNS 320 #define SPACES_PER_TAB 8 #define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */ @@ -193,7 +193,7 @@ static int sclp_tty_write_string(const unsigned char *str, int count, int may_fa } page = sclp_tty_pages.next; list_del((struct list_head *) page); - sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns, + sclp_ttybuf = sclp_make_buffer(page, SCLP_TTY_COLUMNS, SPACES_PER_TAB); } /* try to write the string to the current output buffer */ @@ -531,11 +531,6 @@ sclp_tty_init(void) sclp_ttybuf = NULL; sclp_tty_buffer_count = 0; if (MACHINE_IS_VM) { - /* - * save 4 characters for the CPU number - * written at start of each line by VM/CP - */ - sclp_tty_columns = 76; /* case input lines to lowercase */ sclp_tty_tolower = 1; } -- cgit v1.2.3 From 64a715ab4e91593465f62c8d9584dcc0279e5145 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Thu, 4 Feb 2021 07:03:00 +0100 Subject: s390/cio: introduce CIO debugfs directory This patch introduces an s390 Common I/O layer debugfs directory that is intended to provide access to debugging-related CIO functions and data. The directory is created as /sys/kernel/debug/s390/cio Signed-off-by: Vineeth Vijayan Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens --- drivers/s390/cio/Makefile | 2 +- drivers/s390/cio/cio_debug.h | 3 +++ drivers/s390/cio/cio_debugfs.c | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/cio/cio_debugfs.c (limited to 'drivers/s390') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index a9235f111e79..8622dba974c2 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -8,7 +8,7 @@ CFLAGS_trace.o := -I$(src) CFLAGS_vfio_ccw_trace.o := -I$(src) obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ - fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o + fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o cio_debugfs.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index 7bdbe73707c2..e6dcbd1be244 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -26,4 +26,7 @@ static inline void CIO_HEX_EVENT(int level, void *data, int length) debug_event(cio_debug_trace_id, level, data, length); } +/* For the CIO debugfs related features */ +extern struct dentry *cio_debugfs_dir; + #endif diff --git a/drivers/s390/cio/cio_debugfs.c b/drivers/s390/cio/cio_debugfs.c new file mode 100644 index 000000000000..0a3656fb5ad0 --- /dev/null +++ b/drivers/s390/cio/cio_debugfs.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * S/390 common I/O debugfs interface + * + * Copyright IBM Corp. 2021 + * Author(s): Vineeth Vijayan + */ + +#include +#include "cio_debug.h" + +struct dentry *cio_debugfs_dir; + +/* Create the debugfs directory for CIO under the arch_debugfs_dir + * i.e /sys/kernel/debug/s390/cio + */ +static int __init cio_debugfs_init(void) +{ + cio_debugfs_dir = debugfs_create_dir("cio", arch_debugfs_dir); + + return 0; +} +subsys_initcall(cio_debugfs_init); -- cgit v1.2.3 From a4f17cc726712a52122ad38540bc3ff3a052d1a4 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Sun, 7 Feb 2021 20:40:58 +0100 Subject: s390/cio: add CRW inject functionality This patch introduces the mechanism to inject artificial events to the CIO layer. One of the main-event type which triggers the CommonIO operations are Channel Report events. When a malfunction or other condition affecting channel-subsystem operation is recognized, a Channel Report Word (consisting of one or more CRWs) describing the condition is made pending for retrieval and analysis by the program. The CRW contains information concerning the identity and state of a facility following the detection of the malfunction or other condition. The patch introduces two debugfs interfaces which can be used to inject 'artificial' events from the userspace. It is intended to provide an easy means to increase the test coverage for CIO code. And this functionality can be enabled via a new configuration option CONFIG_CIO_INJECT. The newly introduces debugfs interfaces can be used as mentioned below to generate different fake-events. To use the crw_inject, first we should enable it by using enable_inject interface. i.e echo 1 > /sys/kernel/debug/s390/cio/enable_inject After the first step, user can simulate CRW as follows: echo \ > /sys/kernel/debug/s390/cio/crw_inject Example: A permanent error ERC on CHPID 0x60 would look like this: echo 0 0 0 4 0 6 0x60 > /sys/kernel/debug/s390/cio/crw_inject and an initialized ERC on the same CHPID: echo 0 0 0 4 0 2 0x60 > /sys/kernel/debug/s390/cio/crw_inject Signed-off-by: Vineeth Vijayan Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens --- arch/s390/Kconfig.debug | 8 ++ arch/s390/configs/debug_defconfig | 1 + drivers/s390/cio/Makefile | 2 + drivers/s390/cio/cio_inject.c | 171 ++++++++++++++++++++++++++++++++++++++ drivers/s390/cio/cio_inject.h | 18 ++++ drivers/s390/cio/ioasm.c | 23 ++++- 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/cio/cio_inject.c create mode 100644 drivers/s390/cio/cio_inject.h (limited to 'drivers/s390') diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug index ef96c25fa921..9ea6e61d5858 100644 --- a/arch/s390/Kconfig.debug +++ b/arch/s390/Kconfig.debug @@ -15,3 +15,11 @@ config DEBUG_ENTRY exits or otherwise impact performance. If unsure, say N. + +config CIO_INJECT + bool "CIO Inject interfaces" + depends on DEBUG_KERNEL && DEBUG_FS + help + This option provides a debugging facility to inject certain artificial events + and instruction responses to the CIO layer of Linux kernel. The newly created + debugfs user-interfaces will be at /sys/kernel/debug/s390/cio/* diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index dc0b69058ac4..b2d2db1b3410 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -829,6 +829,7 @@ CONFIG_HIST_TRIGGERS=y CONFIG_FTRACE_STARTUP_TEST=y # CONFIG_EVENT_TRACE_STARTUP_TEST is not set CONFIG_DEBUG_ENTRY=y +CONFIG_CIO_INJECT=y CONFIG_NOTIFIER_ERROR_INJECTION=m CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m CONFIG_FAULT_INJECTION=y diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index 8622dba974c2..3bd1c245183f 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -23,3 +23,5 @@ obj-$(CONFIG_QDIO) += qdio.o vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o \ vfio_ccw_async.o vfio_ccw_trace.o vfio_ccw_chp.o obj-$(CONFIG_VFIO_CCW) += vfio_ccw.o + +obj-$(CONFIG_CIO_INJECT) += cio_inject.o diff --git a/drivers/s390/cio/cio_inject.c b/drivers/s390/cio/cio_inject.c new file mode 100644 index 000000000000..8613fa937237 --- /dev/null +++ b/drivers/s390/cio/cio_inject.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CIO inject interface + * + * Copyright IBM Corp. 2021 + * Author(s): Vineeth Vijayan + */ + +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include + +#include "cio_inject.h" +#include "cio_debug.h" + +static DEFINE_SPINLOCK(crw_inject_lock); +DEFINE_STATIC_KEY_FALSE(cio_inject_enabled); +static struct crw *crw_inject_data; + +/** + * crw_inject : Initiate the artificial CRW inject + * @crw: The data which needs to be injected as new CRW. + * + * The CRW handler is called, which will use the provided artificial + * data instead of the CRW from the underlying hardware. + * + * Return: 0 on success + */ +static int crw_inject(struct crw *crw) +{ + int rc = 0; + struct crw *copy; + unsigned long flags; + + copy = kmemdup(crw, sizeof(*crw), GFP_KERNEL); + if (!copy) + return -ENOMEM; + + spin_lock_irqsave(&crw_inject_lock, flags); + if (crw_inject_data) { + kfree(copy); + rc = -EBUSY; + } else { + crw_inject_data = copy; + } + spin_unlock_irqrestore(&crw_inject_lock, flags); + + if (!rc) + crw_handle_channel_report(); + + return rc; +} + +/** + * stcrw_get_injected: Copy the artificial CRW data to CRW struct. + * @crw: The target CRW pointer. + * + * Retrieve an injected CRW data. Return 0 on success, 1 if no + * injected-CRW is available. The function reproduces the return + * code of the actual STCRW function. + */ +int stcrw_get_injected(struct crw *crw) +{ + int rc = 1; + unsigned long flags; + + spin_lock_irqsave(&crw_inject_lock, flags); + if (crw_inject_data) { + memcpy(crw, crw_inject_data, sizeof(*crw)); + kfree(crw_inject_data); + crw_inject_data = NULL; + rc = 0; + } + spin_unlock_irqrestore(&crw_inject_lock, flags); + + return rc; +} + +/* The debugfs write handler for crw_inject nodes operation */ +static ssize_t crw_inject_write(struct file *file, const char __user *buf, + size_t lbuf, loff_t *ppos) +{ + u32 slct, oflw, chn, rsc, anc, erc, rsid; + struct crw crw; + char *buffer; + int rc; + + if (!static_branch_likely(&cio_inject_enabled)) { + pr_warn("CIO inject is not enabled - ignoring CRW inject\n"); + return -EINVAL; + } + + buffer = vmemdup_user(buf, lbuf); + if (IS_ERR(buffer)) + return -ENOMEM; + + rc = sscanf(buffer, "%x %x %x %x %x %x %x", &slct, &oflw, &chn, &rsc, &anc, + &erc, &rsid); + + kvfree(buffer); + if (rc != 7) { + pr_warn("crw_inject: Invalid format (need )\n"); + return -EINVAL; + } + + memset(&crw, 0, sizeof(crw)); + crw.slct = slct; + crw.oflw = oflw; + crw.chn = chn; + crw.rsc = rsc; + crw.anc = anc; + crw.erc = erc; + crw.rsid = rsid; + + rc = crw_inject(&crw); + if (rc) + return rc; + + return lbuf; +} + +/* Debugfs write handler for inject_enable node*/ +static ssize_t enable_inject_write(struct file *file, const char __user *buf, + size_t lbuf, loff_t *ppos) +{ + unsigned long en = 0; + int rc; + + rc = kstrtoul_from_user(buf, lbuf, 10, &en); + if (rc) + return rc; + + switch (en) { + case 0: + static_branch_disable(&cio_inject_enabled); + break; + case 1: + static_branch_enable(&cio_inject_enabled); + break; + } + + return lbuf; +} + +static const struct file_operations crw_fops = { + .owner = THIS_MODULE, + .write = crw_inject_write, +}; + +static const struct file_operations cio_en_fops = { + .owner = THIS_MODULE, + .write = enable_inject_write, +}; + +static int __init cio_inject_init(void) +{ + /* enable_inject node enables the static branching */ + debugfs_create_file("enable_inject", 0200, cio_debugfs_dir, + NULL, &cio_en_fops); + + debugfs_create_file("crw_inject", 0200, cio_debugfs_dir, + NULL, &crw_fops); + return 0; +} + +device_initcall(cio_inject_init); diff --git a/drivers/s390/cio/cio_inject.h b/drivers/s390/cio/cio_inject.h new file mode 100644 index 000000000000..914a3f4a3c63 --- /dev/null +++ b/drivers/s390/cio/cio_inject.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright IBM Corp. 2021 + * Author(s): Vineeth Vijayan + */ + +#ifndef CIO_CRW_INJECT_H +#define CIO_CRW_INJECT_H + +#ifdef CONFIG_CIO_INJECT + +#include + +DECLARE_STATIC_KEY_FALSE(cio_inject_enabled); +int stcrw_get_injected(struct crw *crw); + +#endif +#endif diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c index 08eb10283b18..4c5244d6052b 100644 --- a/drivers/s390/cio/ioasm.c +++ b/drivers/s390/cio/ioasm.c @@ -12,6 +12,7 @@ #include "ioasm.h" #include "orb.h" #include "cio.h" +#include "cio_inject.h" static inline int __stsch(struct subchannel_id schid, struct schib *addr) { @@ -260,7 +261,7 @@ int xsch(struct subchannel_id schid) return ccode; } -int stcrw(struct crw *crw) +static inline int __stcrw(struct crw *crw) { int ccode; @@ -271,6 +272,26 @@ int stcrw(struct crw *crw) : "=d" (ccode), "=m" (*crw) : "a" (crw) : "cc"); + return ccode; +} + +static inline int _stcrw(struct crw *crw) +{ +#ifdef CONFIG_CIO_INJECT + if (static_branch_unlikely(&cio_inject_enabled)) { + if (stcrw_get_injected(crw) == 0) + return 0; + } +#endif + + return __stcrw(crw); +} + +int stcrw(struct crw *crw) +{ + int ccode; + + ccode = _stcrw(crw); trace_s390_cio_stcrw(crw, ccode); return ccode; -- cgit v1.2.3 From 396c100472dd63bb1a5389d9dfb25a94943c41c9 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Sat, 30 Jan 2021 14:56:20 +0100 Subject: s390/qdio: let driver manage the QAOB We are spending way too much effort on qdio-internal bookkeeping for QAOB management & caching, and it's still not robust. Once qdio's TX path has detached the QAOB from a PENDING buffer, we lost all track of it until it shows up in a CQ notification again. So if the device is torn down before that notification arrives, we leak the QAOB. Just have the driver take care of it, and simply pass down a QAOB if they want a TX with async-completion capability. For a buffer in PENDING state that requires the QAOB for final completion, qeth can now also try to recycle the buffer's QAOB rather than unconditionally freeing it. This also eliminates the qdio_outbuf_state array, which was only needed to transfer the aob->user1 tag from the driver to the qdio layer. Signed-off-by: Julian Wiedmann Acked-by: Benjamin Block Signed-off-by: Heiko Carstens --- arch/s390/include/asm/qdio.h | 22 ++------ drivers/s390/cio/qdio.h | 10 ---- drivers/s390/cio/qdio_main.c | 63 +++-------------------- drivers/s390/cio/qdio_setup.c | 49 +----------------- drivers/s390/net/qeth_core.h | 3 +- drivers/s390/net/qeth_core_main.c | 102 ++++++++++++++++++-------------------- drivers/s390/scsi/zfcp_qdio.c | 7 +-- 7 files changed, 66 insertions(+), 190 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index d9215c7106f0..8fc52679543d 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -246,21 +246,8 @@ struct slsb { u8 val[QDIO_MAX_BUFFERS_PER_Q]; } __attribute__ ((packed, aligned(256))); -/** - * struct qdio_outbuf_state - SBAL related asynchronous operation information - * (for communication with upper layer programs) - * (only required for use with completion queues) - * @user: pointer to upper layer program's state information related to SBAL - * (stored in user1 data of QAOB) - */ -struct qdio_outbuf_state { - void *user; -}; - -#define CHSC_AC1_INITIATE_INPUTQ 0x80 - - /* qdio adapter-characteristics-1 flag */ +#define CHSC_AC1_INITIATE_INPUTQ 0x80 #define AC1_SIGA_INPUT_NEEDED 0x40 /* process input queues */ #define AC1_SIGA_OUTPUT_NEEDED 0x20 /* process output queues */ #define AC1_SIGA_SYNC_NEEDED 0x10 /* ask hypervisor to sync */ @@ -338,7 +325,6 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int, * @int_parm: interruption parameter * @input_sbal_addr_array: per-queue array, each element points to 128 SBALs * @output_sbal_addr_array: per-queue array, each element points to 128 SBALs - * @output_sbal_state_array: no_output_qs * 128 state info (for CQ or NULL) */ struct qdio_initialize { unsigned char q_format; @@ -357,7 +343,6 @@ struct qdio_initialize { unsigned long int_parm; struct qdio_buffer ***input_sbal_addr_array; struct qdio_buffer ***output_sbal_addr_array; - struct qdio_outbuf_state *output_sbal_state_array; }; #define QDIO_STATE_INACTIVE 0x00000002 /* after qdio_cleanup */ @@ -378,9 +363,10 @@ extern int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs, extern int qdio_establish(struct ccw_device *cdev, struct qdio_initialize *init_data); extern int qdio_activate(struct ccw_device *); +extern struct qaob *qdio_allocate_aob(void); extern void qdio_release_aob(struct qaob *); -extern int do_QDIO(struct ccw_device *, unsigned int, int, unsigned int, - unsigned int); +extern int do_QDIO(struct ccw_device *cdev, unsigned int callflags, int q_nr, + unsigned int bufnr, unsigned int count, struct qaob *aob); extern int qdio_start_irq(struct ccw_device *cdev); extern int qdio_stop_irq(struct ccw_device *cdev); extern int qdio_get_next_buffers(struct ccw_device *, int, int *, int *); diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 34bf2f197c71..0e0044d70844 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -181,12 +181,6 @@ struct qdio_input_q { struct qdio_output_q { /* PCIs are enabled for the queue */ int pci_out_enabled; - /* cq: use asynchronous output buffers */ - int use_cq; - /* cq: aobs used for particual SBAL */ - struct qaob **aobs; - /* cq: sbal state related to asynchronous operation */ - struct qdio_outbuf_state *sbal_state; /* timer to check for more outbound work */ struct timer_list timer; /* tasklet to check for completions */ @@ -379,12 +373,8 @@ int qdio_setup_irq(struct qdio_irq *irq_ptr, struct qdio_initialize *init_data); void qdio_shutdown_irq(struct qdio_irq *irq); void qdio_print_subchannel_info(struct qdio_irq *irq_ptr); void qdio_free_queues(struct qdio_irq *irq_ptr); -void qdio_free_async_data(struct qdio_irq *irq_ptr); int qdio_setup_init(void); void qdio_setup_exit(void); -int qdio_enable_async_operation(struct qdio_output_q *q); -void qdio_disable_async_operation(struct qdio_output_q *q); -struct qaob *qdio_allocate_aob(void); int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 03a011619908..307ce7ff5ca4 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -517,24 +517,6 @@ static inline int qdio_inbound_q_done(struct qdio_q *q, unsigned int start) return 1; } -static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q, - int bufnr) -{ - unsigned long phys_aob = 0; - - if (!q->aobs[bufnr]) { - struct qaob *aob = qdio_allocate_aob(); - q->aobs[bufnr] = aob; - } - if (q->aobs[bufnr]) { - q->aobs[bufnr]->user1 = (u64) q->sbal_state[bufnr].user; - phys_aob = virt_to_phys(q->aobs[bufnr]); - WARN_ON_ONCE(phys_aob & 0xFF); - } - - return phys_aob; -} - static inline int qdio_tasklet_schedule(struct qdio_q *q) { if (likely(q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE)) { @@ -548,7 +530,6 @@ 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(); @@ -570,10 +551,6 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start, switch (state) { 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: @@ -999,7 +976,6 @@ int qdio_free(struct ccw_device *cdev) cdev->private->qdio_data = NULL; mutex_unlock(&irq_ptr->setup_mutex); - qdio_free_async_data(irq_ptr); qdio_free_queues(irq_ptr); free_page((unsigned long) irq_ptr->qdr); free_page(irq_ptr->chsc_page); @@ -1075,28 +1051,6 @@ err_dbf: } EXPORT_SYMBOL_GPL(qdio_allocate); -static void qdio_detect_hsicq(struct qdio_irq *irq_ptr) -{ - struct qdio_q *q = irq_ptr->input_qs[0]; - int i, use_cq = 0; - - if (irq_ptr->nr_input_qs > 1 && queue_type(q) == QDIO_IQDIO_QFMT) - use_cq = 1; - - for_each_output_queue(irq_ptr, q, i) { - if (use_cq) { - if (multicast_outbound(q)) - continue; - if (qdio_enable_async_operation(&q->u.out) < 0) { - use_cq = 0; - continue; - } - } else - qdio_disable_async_operation(&q->u.out); - } - DBF_EVENT("use_cq:%d", use_cq); -} - static void qdio_trace_init_data(struct qdio_irq *irq, struct qdio_initialize *data) { @@ -1191,8 +1145,6 @@ int qdio_establish(struct ccw_device *cdev, qdio_setup_ssqd_info(irq_ptr); - qdio_detect_hsicq(irq_ptr); - /* qebsm is now setup if available, initialize buffer states */ qdio_init_buf_states(irq_ptr); @@ -1297,9 +1249,11 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags, * @callflags: flags * @bufnr: first buffer to process * @count: how many buffers are filled + * @aob: asynchronous operation block */ static int handle_outbound(struct qdio_q *q, unsigned int callflags, - unsigned int bufnr, unsigned int count) + unsigned int bufnr, unsigned int count, + struct qaob *aob) { const unsigned int scan_threshold = q->irq_ptr->scan_threshold; unsigned char state = 0; @@ -1320,11 +1274,9 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, q->u.out.pci_out_enabled = 0; if (queue_type(q) == QDIO_IQDIO_QFMT) { - unsigned long phys_aob = 0; - - if (q->u.out.use_cq && count == 1) - phys_aob = qdio_aob_for_buffer(&q->u.out, bufnr); + unsigned long phys_aob = aob ? virt_to_phys(aob) : 0; + WARN_ON_ONCE(!IS_ALIGNED(phys_aob, 256)); rc = qdio_kick_outbound_q(q, count, phys_aob); } else if (need_siga_sync(q)) { rc = qdio_siga_sync_q(q); @@ -1359,9 +1311,10 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, * @q_nr: queue number * @bufnr: buffer number * @count: how many buffers to process + * @aob: asynchronous operation block (outbound only) */ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, - int q_nr, unsigned int bufnr, unsigned int count) + int q_nr, unsigned int bufnr, unsigned int count, struct qaob *aob) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; @@ -1383,7 +1336,7 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, callflags, bufnr, count); else if (callflags & QDIO_FLAG_SYNC_OUTPUT) return handle_outbound(irq_ptr->output_qs[q_nr], - callflags, bufnr, count); + callflags, bufnr, count, aob); return -EINVAL; } EXPORT_SYMBOL_GPL(do_QDIO); diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index c8b9620bc688..da67e4979402 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -30,6 +30,7 @@ struct qaob *qdio_allocate_aob(void) { return kmem_cache_zalloc(qdio_aob_cache, GFP_ATOMIC); } +EXPORT_SYMBOL_GPL(qdio_allocate_aob); void qdio_release_aob(struct qaob *aob) { @@ -247,8 +248,6 @@ static void setup_queues(struct qdio_irq *irq_ptr, struct qdio_initialize *qdio_init) { struct qdio_q *q; - struct qdio_outbuf_state *output_sbal_state_array = - qdio_init->output_sbal_state_array; int i; for_each_input_queue(irq_ptr, q, i) { @@ -265,9 +264,6 @@ static void setup_queues(struct qdio_irq *irq_ptr, DBF_EVENT("outq:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); - q->u.out.sbal_state = output_sbal_state_array; - output_sbal_state_array += QDIO_MAX_BUFFERS_PER_Q; - q->is_input_q = 0; setup_storage_lists(q, irq_ptr, qdio_init->output_sbal_addr_array[i], i); @@ -372,30 +368,6 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) DBF_EVENT("3:%4x qib:%4x", irq_ptr->ssqd_desc.qdioac3, irq_ptr->qib.ac); } -void qdio_free_async_data(struct qdio_irq *irq_ptr) -{ - struct qdio_q *q; - int i; - - for (i = 0; i < irq_ptr->max_output_qs; i++) { - q = irq_ptr->output_qs[i]; - if (q->u.out.use_cq) { - unsigned int n; - - for (n = 0; n < QDIO_MAX_BUFFERS_PER_Q; n++) { - struct qaob *aob = q->u.out.aobs[n]; - - if (aob) { - qdio_release_aob(aob); - q->u.out.aobs[n] = NULL; - } - } - - qdio_disable_async_operation(&q->u.out); - } - } -} - static void qdio_fill_qdr_desc(struct qdesfmt0 *desc, struct qdio_q *queue) { desc->sliba = virt_to_phys(queue->slib); @@ -545,25 +517,6 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr) printk(KERN_INFO "%s", s); } -int qdio_enable_async_operation(struct qdio_output_q *outq) -{ - outq->aobs = kcalloc(QDIO_MAX_BUFFERS_PER_Q, sizeof(struct qaob *), - GFP_KERNEL); - if (!outq->aobs) { - outq->use_cq = 0; - return -ENOMEM; - } - outq->use_cq = 1; - return 0; -} - -void qdio_disable_async_operation(struct qdio_output_q *q) -{ - kfree(q->aobs); - q->aobs = NULL; - q->use_cq = 0; -} - int __init qdio_setup_init(void) { int rc; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 91acff493612..fd9b869d278e 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -437,6 +437,7 @@ struct qeth_qdio_out_buffer { struct qeth_qdio_out_q *q; struct list_head list_entry; + struct qaob *aob; }; struct qeth_card; @@ -499,7 +500,6 @@ struct qeth_out_q_stats { struct qeth_qdio_out_q { struct qdio_buffer *qdio_bufs[QDIO_MAX_BUFFERS_PER_Q]; struct qeth_qdio_out_buffer *bufs[QDIO_MAX_BUFFERS_PER_Q]; - struct qdio_outbuf_state *bufstates; /* convenience pointer */ struct list_head pending_bufs; struct qeth_out_q_stats stats; spinlock_t lock; @@ -563,7 +563,6 @@ struct qeth_qdio_info { /* output */ unsigned int no_out_queues; struct qeth_qdio_out_q *out_qs[QETH_MAX_OUT_QUEUES]; - struct qdio_outbuf_state *out_bufstates; /* priority queueing */ int do_prio_queueing; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index a814698387bc..175b82b98f36 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -369,8 +369,7 @@ static int qeth_cq_init(struct qeth_card *card) QDIO_MAX_BUFFERS_PER_Q); card->qdio.c_q->next_buf_to_init = 127; rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, - card->qdio.no_in_queues - 1, 0, - 127); + card->qdio.no_in_queues - 1, 0, 127, NULL); if (rc) { QETH_CARD_TEXT_(card, 2, "1err%d", rc); goto out; @@ -383,48 +382,22 @@ out: static int qeth_alloc_cq(struct qeth_card *card) { - int rc; - if (card->options.cq == QETH_CQ_ENABLED) { - int i; - struct qdio_outbuf_state *outbuf_states; - QETH_CARD_TEXT(card, 2, "cqon"); card->qdio.c_q = qeth_alloc_qdio_queue(); if (!card->qdio.c_q) { - rc = -1; - goto kmsg_out; + dev_err(&card->gdev->dev, "Failed to create completion queue\n"); + return -ENOMEM; } + card->qdio.no_in_queues = 2; - card->qdio.out_bufstates = - kcalloc(card->qdio.no_out_queues * - QDIO_MAX_BUFFERS_PER_Q, - sizeof(struct qdio_outbuf_state), - GFP_KERNEL); - outbuf_states = card->qdio.out_bufstates; - if (outbuf_states == NULL) { - rc = -1; - goto free_cq_out; - } - for (i = 0; i < card->qdio.no_out_queues; ++i) { - card->qdio.out_qs[i]->bufstates = outbuf_states; - outbuf_states += QDIO_MAX_BUFFERS_PER_Q; - } } else { QETH_CARD_TEXT(card, 2, "nocq"); card->qdio.c_q = NULL; card->qdio.no_in_queues = 1; } QETH_CARD_TEXT_(card, 2, "iqc%d", card->qdio.no_in_queues); - rc = 0; -out: - return rc; -free_cq_out: - qeth_free_qdio_queue(card->qdio.c_q); - card->qdio.c_q = NULL; -kmsg_out: - dev_err(&card->gdev->dev, "Failed to create completion queue\n"); - goto out; + return 0; } static void qeth_free_cq(struct qeth_card *card) @@ -434,8 +407,6 @@ static void qeth_free_cq(struct qeth_card *card) qeth_free_qdio_queue(card->qdio.c_q); card->qdio.c_q = NULL; } - kfree(card->qdio.out_bufstates); - card->qdio.out_bufstates = NULL; } static enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, @@ -487,12 +458,12 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, switch (atomic_xchg(&buffer->state, new_state)) { case QETH_QDIO_BUF_PRIMED: /* Faster than TX completion code, let it handle the async - * completion for us. + * completion for us. It will also recycle the QAOB. */ break; case QETH_QDIO_BUF_PENDING: /* TX completion code is active and will handle the async - * completion for us. + * completion for us. It will also recycle the QAOB. */ break; case QETH_QDIO_BUF_NEED_QAOB: @@ -501,7 +472,7 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, qeth_notify_skbs(buffer->q, buffer, notification); /* Free dangling allocations. The attached skbs are handled by - * qeth_tx_complete_pending_bufs(). + * qeth_tx_complete_pending_bufs(), and so is the QAOB. */ for (i = 0; i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card); @@ -520,8 +491,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, default: WARN_ON_ONCE(1); } - - qdio_release_aob(aob); } static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u8 flags, u32 len, @@ -1451,6 +1420,13 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); } +static void qeth_free_out_buf(struct qeth_qdio_out_buffer *buf) +{ + if (buf->aob) + qdio_release_aob(buf->aob); + kmem_cache_free(qeth_qdio_outbuf_cache, buf); +} + static void qeth_tx_complete_pending_bufs(struct qeth_card *card, struct qeth_qdio_out_q *queue, bool drain) @@ -1468,7 +1444,7 @@ static void qeth_tx_complete_pending_bufs(struct qeth_card *card, qeth_tx_complete_buf(buf, drain, 0); list_del(&buf->list_entry); - kmem_cache_free(qeth_qdio_outbuf_cache, buf); + qeth_free_out_buf(buf); } } } @@ -1485,7 +1461,7 @@ static void qeth_drain_output_queue(struct qeth_qdio_out_q *q, bool free) qeth_clear_output_buffer(q, q->bufs[j], true, 0); if (free) { - kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); + qeth_free_out_buf(q->bufs[j]); q->bufs[j] = NULL; } } @@ -2637,7 +2613,7 @@ static struct qeth_qdio_out_q *qeth_alloc_output_queue(void) err_out_bufs: while (i > 0) - kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[--i]); + qeth_free_out_buf(q->bufs[--i]); qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); err_qdio_bufs: kfree(q); @@ -3024,7 +3000,8 @@ static int qeth_init_qdio_queues(struct qeth_card *card) } card->qdio.in_q->next_buf_to_init = QDIO_BUFNR(rx_bufs); - rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, rx_bufs); + rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, rx_bufs, + NULL); if (rc) { QETH_CARD_TEXT_(card, 2, "1err%d", rc); return rc; @@ -3516,7 +3493,7 @@ static unsigned int qeth_rx_refill_queue(struct qeth_card *card, } rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, - queue->next_buf_to_init, count); + queue->next_buf_to_init, count, NULL); if (rc) { QETH_CARD_TEXT(card, 2, "qinberr"); } @@ -3625,6 +3602,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, struct qeth_qdio_out_buffer *buf = queue->bufs[index]; unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT; struct qeth_card *card = queue->card; + struct qaob *aob = NULL; int rc; int i; @@ -3637,16 +3615,24 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, SBAL_EFLAGS_LAST_ENTRY; queue->coalesced_frames += buf->frames; - if (queue->bufstates) - queue->bufstates[bidx].user = buf; - if (IS_IQD(card)) { skb_queue_walk(&buf->skb_list, skb) skb_tx_timestamp(skb); } } - if (!IS_IQD(card)) { + if (IS_IQD(card)) { + if (card->options.cq == QETH_CQ_ENABLED && + !qeth_iqd_is_mcast_queue(card, queue) && + count == 1) { + if (!buf->aob) + buf->aob = qdio_allocate_aob(); + if (buf->aob) { + aob = buf->aob; + aob->user1 = (u64) buf; + } + } + } else { if (!queue->do_pack) { if ((atomic_read(&queue->used_buffers) >= (QETH_HIGH_WATERMARK_PACK - @@ -3677,8 +3663,8 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, } QETH_TXQ_STAT_INC(queue, doorbell); - rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, - queue->queue_no, index, count); + rc = do_QDIO(CARD_DDEV(card), qdio_flags, queue->queue_no, index, count, + aob); switch (rc) { case 0: @@ -3814,8 +3800,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, qeth_scrub_qdio_buffer(buffer, QDIO_MAX_ELEMENTS_PER_BUFFER); } rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, - card->qdio.c_q->next_buf_to_init, - count); + cq->next_buf_to_init, count, NULL); if (rc) { dev_warn(&card->gdev->dev, "QDIO reported an error, rc=%i\n", rc); @@ -5270,7 +5255,6 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = in_sbal_ptrs; init_data.output_sbal_addr_array = out_sbal_ptrs; - init_data.output_sbal_state_array = card->qdio.out_bufstates; init_data.scan_threshold = IS_IQD(card) ? 0 : 32; if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, @@ -6069,7 +6053,15 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, bool error = !!qdio_error; if (qdio_error == QDIO_ERROR_SLSB_PENDING) { - WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); + struct qaob *aob = buffer->aob; + + if (!aob) { + netdev_WARN_ONCE(card->dev, + "Pending TX buffer %#x without QAOB on TX queue %u\n", + bidx, queue->queue_no); + qeth_schedule_recovery(card); + return; + } QETH_CARD_TEXT_(card, 5, "pel%u", bidx); @@ -6125,6 +6117,8 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, default: WARN_ON_ONCE(1); } + + memset(aob, 0, sizeof(*aob)); } else if (card->options.cq == QETH_CQ_ENABLED) { qeth_notify_skbs(queue, buffer, qeth_compute_cq_notification(sflags, 0)); diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 23ab16d65f2a..049596cbfb5d 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -128,7 +128,7 @@ 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 (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, idx, count, NULL)) zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2"); } @@ -298,7 +298,7 @@ int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) atomic_sub(sbal_number, &qdio->req_q_free); retval = do_QDIO(qdio->adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, - q_req->sbal_first, sbal_number); + q_req->sbal_first, sbal_number, NULL); if (unlikely(retval)) { /* Failed to submit the IO, roll back our modifications. */ @@ -463,7 +463,8 @@ 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 (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q, + NULL)) goto failed_qdio; /* set index of first available SBALS / number of available SBALS */ -- cgit v1.2.3 From 1034c96c5e28b6a27d058a0e00c968695fcf3bf0 Mon Sep 17 00:00:00 2001 From: Shixin Liu Date: Mon, 29 Mar 2021 17:40:18 +0800 Subject: s390/cio: use DEFINE_SPINLOCK() for spinlock static spinlock can be initialized automatically with DEFINE_SPINLOCK() rather than explicitly calling spin_lock_init(). Signed-off-by: Shixin Liu Acked-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- drivers/s390/cio/css.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 253ab4e7a415..f01ef6325039 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -651,13 +651,12 @@ static void css_sch_todo(struct work_struct *work) } static struct idset *slow_subchannel_set; -static spinlock_t slow_subchannel_lock; +static DEFINE_SPINLOCK(slow_subchannel_lock); static wait_queue_head_t css_eval_wq; static atomic_t css_eval_scheduled; static int __init slow_subchannel_init(void) { - spin_lock_init(&slow_subchannel_lock); atomic_set(&css_eval_scheduled, 0); init_waitqueue_head(&css_eval_wq); slow_subchannel_set = idset_sch_new(); -- cgit v1.2.3 From 4e774d59e59956c45c02cfcc23f85a26be8d8bea Mon Sep 17 00:00:00 2001 From: Shixin Liu Date: Mon, 29 Mar 2021 17:40:19 +0800 Subject: s390/cio: use DECLARE_WAIT_QUEUE_HEAD() for wait_queue wait_queue_head_t can be initialized automatically with DECLARE_WAIT_QUEUE_HEAD() rather than explicitly calling init_waitqueue_head(). Signed-off-by: Shixin Liu Acked-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- drivers/s390/cio/css.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index f01ef6325039..a974943c27da 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -652,13 +652,12 @@ static void css_sch_todo(struct work_struct *work) static struct idset *slow_subchannel_set; static DEFINE_SPINLOCK(slow_subchannel_lock); -static wait_queue_head_t css_eval_wq; +static DECLARE_WAIT_QUEUE_HEAD(css_eval_wq); static atomic_t css_eval_scheduled; static int __init slow_subchannel_init(void) { atomic_set(&css_eval_scheduled, 0); - init_waitqueue_head(&css_eval_wq); slow_subchannel_set = idset_sch_new(); if (!slow_subchannel_set) { CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); -- cgit v1.2.3 From 0cc00c8d40500c4c8fe058dc014bdaf44a82f4f7 Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Thu, 25 Mar 2021 08:46:40 -0400 Subject: s390/vfio-ap: fix circular lockdep when setting/clearing crypto masks This patch fixes a lockdep splat introduced by commit f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated"). The lockdep splat only occurs when starting a Secure Execution guest. Crypto virtualization (vfio_ap) is not yet supported for SE guests; however, in order to avoid this problem when support becomes available, this fix is being provided. The circular locking dependency was introduced when the setting of the masks in the guest's APCB was executed while holding the matrix_dev->lock. While the lock is definitely needed to protect the setting/unsetting of the matrix_mdev->kvm pointer, it is not necessarily critical for setting the masks; so, the matrix_dev->lock will be released while the masks are being set or cleared. Keep in mind, however, that another process that takes the matrix_dev->lock can get control while the masks in the guest's APCB are being set or cleared as a result of the driver being notified that the KVM pointer has been set or unset. This could result in invalid access to the matrix_mdev->kvm pointer by the intervening process. To avoid this scenario, two new fields are being added to the ap_matrix_mdev struct: struct ap_matrix_mdev { ... bool kvm_busy; wait_queue_head_t wait_for_kvm; ... }; The functions that handle notification that the KVM pointer value has been set or cleared will set the kvm_busy flag to true until they are done processing at which time they will set it to false and wake up the tasks on the matrix_mdev->wait_for_kvm wait queue. Functions that require access to matrix_mdev->kvm will sleep on the wait queue until they are awakened at which time they can safely access the matrix_mdev->kvm field. Fixes: f21916ec4826 ("s390/vfio-ap: clean up vfio_ap resources when KVM pointer invalidated") Cc: stable@vger.kernel.org Signed-off-by: Tony Krowiak Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 308 +++++++++++++++++++++++----------- drivers/s390/crypto/vfio_ap_private.h | 2 + 2 files changed, 215 insertions(+), 95 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 1ffdd411201c..6946a7e26eff 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -294,6 +294,19 @@ static int handle_pqap(struct kvm_vcpu *vcpu) matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook, struct ap_matrix_mdev, pqap_hook); + /* + * If the KVM pointer is in the process of being set, wait until the + * process has completed. + */ + wait_event_cmd(matrix_mdev->wait_for_kvm, + !matrix_mdev->kvm_busy, + mutex_unlock(&matrix_dev->lock), + mutex_lock(&matrix_dev->lock)); + + /* If the there is no guest using the mdev, there is nothing to do */ + if (!matrix_mdev->kvm) + goto out_unlock; + q = vfio_ap_get_queue(matrix_mdev, apqn); if (!q) goto out_unlock; @@ -337,6 +350,7 @@ static int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev) matrix_mdev->mdev = mdev; vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix); + init_waitqueue_head(&matrix_mdev->wait_for_kvm); mdev_set_drvdata(mdev, matrix_mdev); matrix_mdev->pqap_hook.hook = handle_pqap; matrix_mdev->pqap_hook.owner = THIS_MODULE; @@ -351,17 +365,23 @@ static int vfio_ap_mdev_remove(struct mdev_device *mdev) { struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); - if (matrix_mdev->kvm) + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * un-assignment of control domain. + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + mutex_unlock(&matrix_dev->lock); return -EBUSY; + } - mutex_lock(&matrix_dev->lock); vfio_ap_mdev_reset_queues(mdev); list_del(&matrix_mdev->node); - mutex_unlock(&matrix_dev->lock); - kfree(matrix_mdev); mdev_set_drvdata(mdev, NULL); atomic_inc(&matrix_dev->available_instances); + mutex_unlock(&matrix_dev->lock); return 0; } @@ -606,24 +626,31 @@ static ssize_t assign_adapter_store(struct device *dev, struct mdev_device *mdev = mdev_from_dev(dev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); - /* If the guest is running, disallow assignment of adapter */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * un-assignment of adapter + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &apid); if (ret) - return ret; + goto done; - if (apid > matrix_mdev->matrix.apm_max) - return -ENODEV; + if (apid > matrix_mdev->matrix.apm_max) { + ret = -ENODEV; + goto done; + } /* * Set the bit in the AP mask (APM) corresponding to the AP adapter * number (APID). The bits in the mask, from most significant to least * significant bit, correspond to APIDs 0-255. */ - mutex_lock(&matrix_dev->lock); - ret = vfio_ap_mdev_verify_queues_reserved_for_apid(matrix_mdev, apid); if (ret) goto done; @@ -672,22 +699,31 @@ static ssize_t unassign_adapter_store(struct device *dev, struct mdev_device *mdev = mdev_from_dev(dev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); - /* If the guest is running, disallow un-assignment of adapter */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * un-assignment of adapter + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &apid); if (ret) - return ret; + goto done; - if (apid > matrix_mdev->matrix.apm_max) - return -ENODEV; + if (apid > matrix_mdev->matrix.apm_max) { + ret = -ENODEV; + goto done; + } - mutex_lock(&matrix_dev->lock); clear_bit_inv((unsigned long)apid, matrix_mdev->matrix.apm); + ret = count; +done: mutex_unlock(&matrix_dev->lock); - - return count; + return ret; } static DEVICE_ATTR_WO(unassign_adapter); @@ -753,17 +789,24 @@ static ssize_t assign_domain_store(struct device *dev, struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); unsigned long max_apqi = matrix_mdev->matrix.aqm_max; - /* If the guest is running, disallow assignment of domain */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * assignment of domain + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &apqi); if (ret) - return ret; - if (apqi > max_apqi) - return -ENODEV; - - mutex_lock(&matrix_dev->lock); + goto done; + if (apqi > max_apqi) { + ret = -ENODEV; + goto done; + } ret = vfio_ap_mdev_verify_queues_reserved_for_apqi(matrix_mdev, apqi); if (ret) @@ -814,22 +857,32 @@ static ssize_t unassign_domain_store(struct device *dev, struct mdev_device *mdev = mdev_from_dev(dev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); - /* If the guest is running, disallow un-assignment of domain */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * un-assignment of domain + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &apqi); if (ret) - return ret; + goto done; - if (apqi > matrix_mdev->matrix.aqm_max) - return -ENODEV; + if (apqi > matrix_mdev->matrix.aqm_max) { + ret = -ENODEV; + goto done; + } - mutex_lock(&matrix_dev->lock); clear_bit_inv((unsigned long)apqi, matrix_mdev->matrix.aqm); - mutex_unlock(&matrix_dev->lock); + ret = count; - return count; +done: + mutex_unlock(&matrix_dev->lock); + return ret; } static DEVICE_ATTR_WO(unassign_domain); @@ -858,27 +911,36 @@ static ssize_t assign_control_domain_store(struct device *dev, struct mdev_device *mdev = mdev_from_dev(dev); struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); - /* If the guest is running, disallow assignment of control domain */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * assignment of control domain. + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &id); if (ret) - return ret; + goto done; - if (id > matrix_mdev->matrix.adm_max) - return -ENODEV; + if (id > matrix_mdev->matrix.adm_max) { + ret = -ENODEV; + goto done; + } /* Set the bit in the ADM (bitmask) corresponding to the AP control * domain number (id). The bits in the mask, from most significant to * least significant, correspond to IDs 0 up to the one less than the * number of control domains that can be assigned. */ - mutex_lock(&matrix_dev->lock); set_bit_inv(id, matrix_mdev->matrix.adm); + ret = count; +done: mutex_unlock(&matrix_dev->lock); - - return count; + return ret; } static DEVICE_ATTR_WO(assign_control_domain); @@ -908,21 +970,30 @@ static ssize_t unassign_control_domain_store(struct device *dev, struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); unsigned long max_domid = matrix_mdev->matrix.adm_max; - /* If the guest is running, disallow un-assignment of control domain */ - if (matrix_mdev->kvm) - return -EBUSY; + mutex_lock(&matrix_dev->lock); + + /* + * If the KVM pointer is in flux or the guest is running, disallow + * un-assignment of control domain. + */ + if (matrix_mdev->kvm_busy || matrix_mdev->kvm) { + ret = -EBUSY; + goto done; + } ret = kstrtoul(buf, 0, &domid); if (ret) - return ret; - if (domid > max_domid) - return -ENODEV; + goto done; + if (domid > max_domid) { + ret = -ENODEV; + goto done; + } - mutex_lock(&matrix_dev->lock); clear_bit_inv(domid, matrix_mdev->matrix.adm); + ret = count; +done: mutex_unlock(&matrix_dev->lock); - - return count; + return ret; } static DEVICE_ATTR_WO(unassign_control_domain); @@ -1027,8 +1098,15 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = { * @matrix_mdev: a mediated matrix device * @kvm: reference to KVM instance * - * Verifies no other mediated matrix device has @kvm and sets a reference to - * it in @matrix_mdev->kvm. + * Sets all data for @matrix_mdev that are needed to manage AP resources + * for the guest whose state is represented by @kvm. + * + * Note: The matrix_dev->lock must be taken prior to calling + * this function; however, the lock will be temporarily released while the + * guest's AP configuration is set to avoid a potential lockdep splat. + * The kvm->lock is taken to set the guest's AP configuration which, under + * certain circumstances, will result in a circular lock dependency if this is + * done under the @matrix_mdev->lock. * * Return 0 if no other mediated matrix device has a reference to @kvm; * otherwise, returns an -EPERM. @@ -1038,14 +1116,25 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev, { struct ap_matrix_mdev *m; - list_for_each_entry(m, &matrix_dev->mdev_list, node) { - if ((m != matrix_mdev) && (m->kvm == kvm)) - return -EPERM; - } + if (kvm->arch.crypto.crycbd) { + list_for_each_entry(m, &matrix_dev->mdev_list, node) { + if (m != matrix_mdev && m->kvm == kvm) + return -EPERM; + } - matrix_mdev->kvm = kvm; - kvm_get_kvm(kvm); - kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook; + kvm_get_kvm(kvm); + matrix_mdev->kvm_busy = true; + mutex_unlock(&matrix_dev->lock); + kvm_arch_crypto_set_masks(kvm, + matrix_mdev->matrix.apm, + matrix_mdev->matrix.aqm, + matrix_mdev->matrix.adm); + mutex_lock(&matrix_dev->lock); + kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook; + matrix_mdev->kvm = kvm; + matrix_mdev->kvm_busy = false; + wake_up_all(&matrix_mdev->wait_for_kvm); + } return 0; } @@ -1079,51 +1168,65 @@ static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +/** + * vfio_ap_mdev_unset_kvm + * + * @matrix_mdev: a matrix mediated device + * + * Performs clean-up of resources no longer needed by @matrix_mdev. + * + * Note: The matrix_dev->lock must be taken prior to calling + * this function; however, the lock will be temporarily released while the + * guest's AP configuration is cleared to avoid a potential lockdep splat. + * The kvm->lock is taken to clear the guest's AP configuration which, under + * certain circumstances, will result in a circular lock dependency if this is + * done under the @matrix_mdev->lock. + * + */ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev) { - kvm_arch_crypto_clear_masks(matrix_mdev->kvm); - matrix_mdev->kvm->arch.crypto.pqap_hook = NULL; - vfio_ap_mdev_reset_queues(matrix_mdev->mdev); - kvm_put_kvm(matrix_mdev->kvm); - matrix_mdev->kvm = NULL; + /* + * If the KVM pointer is in the process of being set, wait until the + * process has completed. + */ + wait_event_cmd(matrix_mdev->wait_for_kvm, + !matrix_mdev->kvm_busy, + mutex_unlock(&matrix_dev->lock), + mutex_lock(&matrix_dev->lock)); + + if (matrix_mdev->kvm) { + matrix_mdev->kvm_busy = true; + mutex_unlock(&matrix_dev->lock); + kvm_arch_crypto_clear_masks(matrix_mdev->kvm); + mutex_lock(&matrix_dev->lock); + vfio_ap_mdev_reset_queues(matrix_mdev->mdev); + matrix_mdev->kvm->arch.crypto.pqap_hook = NULL; + kvm_put_kvm(matrix_mdev->kvm); + matrix_mdev->kvm = NULL; + matrix_mdev->kvm_busy = false; + wake_up_all(&matrix_mdev->wait_for_kvm); + } } static int vfio_ap_mdev_group_notifier(struct notifier_block *nb, unsigned long action, void *data) { - int ret, notify_rc = NOTIFY_OK; + int notify_rc = NOTIFY_OK; struct ap_matrix_mdev *matrix_mdev; if (action != VFIO_GROUP_NOTIFY_SET_KVM) return NOTIFY_OK; - matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier); mutex_lock(&matrix_dev->lock); + matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier); - if (!data) { - if (matrix_mdev->kvm) - vfio_ap_mdev_unset_kvm(matrix_mdev); - goto notify_done; - } - - ret = vfio_ap_mdev_set_kvm(matrix_mdev, data); - if (ret) { - notify_rc = NOTIFY_DONE; - goto notify_done; - } - - /* If there is no CRYCB pointer, then we can't copy the masks */ - if (!matrix_mdev->kvm->arch.crypto.crycbd) { + if (!data) + vfio_ap_mdev_unset_kvm(matrix_mdev); + else if (vfio_ap_mdev_set_kvm(matrix_mdev, data)) notify_rc = NOTIFY_DONE; - goto notify_done; - } - - kvm_arch_crypto_set_masks(matrix_mdev->kvm, matrix_mdev->matrix.apm, - matrix_mdev->matrix.aqm, - matrix_mdev->matrix.adm); -notify_done: mutex_unlock(&matrix_dev->lock); + return notify_rc; } @@ -1258,8 +1361,7 @@ static void vfio_ap_mdev_release(struct mdev_device *mdev) struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev); mutex_lock(&matrix_dev->lock); - if (matrix_mdev->kvm) - vfio_ap_mdev_unset_kvm(matrix_mdev); + vfio_ap_mdev_unset_kvm(matrix_mdev); mutex_unlock(&matrix_dev->lock); vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, @@ -1293,6 +1395,7 @@ static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev, unsigned int cmd, unsigned long arg) { int ret; + struct ap_matrix_mdev *matrix_mdev; mutex_lock(&matrix_dev->lock); switch (cmd) { @@ -1300,6 +1403,21 @@ static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev, ret = vfio_ap_mdev_get_device_info(arg); break; case VFIO_DEVICE_RESET: + matrix_mdev = mdev_get_drvdata(mdev); + if (WARN(!matrix_mdev, "Driver data missing from mdev!!")) { + ret = -EINVAL; + break; + } + + /* + * If the KVM pointer is in the process of being set, wait until + * the process has completed. + */ + wait_event_cmd(matrix_mdev->wait_for_kvm, + !matrix_mdev->kvm_busy, + mutex_unlock(&matrix_dev->lock), + mutex_lock(&matrix_dev->lock)); + ret = vfio_ap_mdev_reset_queues(mdev); break; default: diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h index 28e9d9989768..f82a6396acae 100644 --- a/drivers/s390/crypto/vfio_ap_private.h +++ b/drivers/s390/crypto/vfio_ap_private.h @@ -83,6 +83,8 @@ struct ap_matrix_mdev { struct ap_matrix matrix; struct notifier_block group_notifier; struct notifier_block iommu_notifier; + bool kvm_busy; + wait_queue_head_t wait_for_kvm; struct kvm *kvm; struct kvm_s390_module_hook pqap_hook; struct mdev_device *mdev; -- cgit v1.2.3 From 7dd8ed09430465d137330e0810a2a90e06770898 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Tue, 30 Mar 2021 20:46:57 +0200 Subject: s390: use DEFINE_SPINLOCK for initialization For static initialization of spinlock_t variable, use DEFINE_SPINLOCK instead of explicitly calling spin_lock_init(). Signed-off-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- drivers/s390/char/con3215.c | 3 +-- drivers/s390/char/sclp_con.c | 3 +-- drivers/s390/char/sclp_tty.c | 3 +-- drivers/s390/char/sclp_vt220.c | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 671efee612af..9d3359b5b20e 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -99,7 +99,7 @@ static DEFINE_SPINLOCK(raw3215_device_lock); /* list of free request structures */ static struct raw3215_req *raw3215_freelist; /* spinlock to protect free list */ -static spinlock_t raw3215_freelist_lock; +static DEFINE_SPINLOCK(raw3215_freelist_lock); static struct tty_driver *tty3215_driver; @@ -850,7 +850,6 @@ static int __init con3215_init(void) /* allocate 3215 request structures */ raw3215_freelist = NULL; - spin_lock_init(&raw3215_freelist_lock); for (i = 0; i < NR_3215_REQ; i++) { req = kzalloc(sizeof(struct raw3215_req), GFP_KERNEL | GFP_DMA); if (!req) diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 8c5c95bf89e0..f5e34e1b6c9e 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -26,7 +26,7 @@ #define sclp_console_name "ttyS" /* Lock to guard over changes to global variables */ -static spinlock_t sclp_con_lock; +static DEFINE_SPINLOCK(sclp_con_lock); /* List of free pages that can be used for console output buffering */ static struct list_head sclp_con_pages; /* List of full struct sclp_buffer structures ready for output */ @@ -329,7 +329,6 @@ sclp_console_init(void) list_add_tail(page, &sclp_con_pages); } INIT_LIST_HEAD(&sclp_con_outqueue); - spin_lock_init(&sclp_con_lock); sclp_conbuf = NULL; timer_setup(&sclp_con_timer, sclp_console_timeout, 0); diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 4af8084df169..cf73fc3fd0f9 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -35,7 +35,7 @@ */ /* Lock to guard over changes to global variables. */ -static spinlock_t sclp_tty_lock; +static DEFINE_SPINLOCK(sclp_tty_lock); /* List of free pages that can be used for console output buffering. */ static struct list_head sclp_tty_pages; /* List of full struct sclp_buffer structures ready for output. */ @@ -526,7 +526,6 @@ sclp_tty_init(void) list_add_tail((struct list_head *) page, &sclp_tty_pages); } INIT_LIST_HEAD(&sclp_tty_outqueue); - spin_lock_init(&sclp_tty_lock); timer_setup(&sclp_tty_timer, sclp_tty_timeout, 0); sclp_ttybuf = NULL; sclp_tty_buffer_count = 0; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 047f812d1a1c..3a6e0af436f4 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -61,7 +61,7 @@ static struct tty_driver *sclp_vt220_driver; static struct tty_port sclp_vt220_port; /* Lock to protect internal data from concurrent access */ -static spinlock_t sclp_vt220_lock; +static DEFINE_SPINLOCK(sclp_vt220_lock); /* List of empty pages to be used as write request buffers */ static struct list_head sclp_vt220_empty; @@ -693,7 +693,6 @@ static int __init __sclp_vt220_init(int num_pages) sclp_vt220_init_count++; if (sclp_vt220_init_count != 1) return 0; - spin_lock_init(&sclp_vt220_lock); INIT_LIST_HEAD(&sclp_vt220_empty); INIT_LIST_HEAD(&sclp_vt220_outqueue); timer_setup(&sclp_vt220_timer, sclp_vt220_timeout, 0); -- cgit v1.2.3 From 8bc00c04d87ee151fb8fe18ed7e7af8c785843f2 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Wed, 31 Mar 2021 15:43:40 +0200 Subject: s390/sclp: use LIST_HEAD for Initialization For static initialization of list_head variable, use LIST_HEAD instead of INIT_LIST_HEAD function. Suggested-by: Julian Wiedmann Signed-off-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- drivers/s390/char/sclp.c | 6 ++---- drivers/s390/char/sclp_con.c | 6 ++---- drivers/s390/char/sclp_tty.c | 6 ++---- drivers/s390/char/sclp_vt220.c | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index d2ab3f07c008..986bbbc23d0a 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -37,10 +37,10 @@ static sccb_mask_t sclp_receive_mask; static sccb_mask_t sclp_send_mask; /* List of registered event listeners and senders. */ -static struct list_head sclp_reg_list; +static LIST_HEAD(sclp_reg_list); /* List of queued requests. */ -static struct list_head sclp_req_queue; +static LIST_HEAD(sclp_req_queue); /* Data for read and and init requests. */ static struct sclp_req sclp_read_req; @@ -1178,8 +1178,6 @@ sclp_init(void) sclp_init_sccb = (void *) __get_free_page(GFP_ATOMIC | GFP_DMA); BUG_ON(!sclp_read_sccb || !sclp_init_sccb); /* Set up variables */ - INIT_LIST_HEAD(&sclp_req_queue); - INIT_LIST_HEAD(&sclp_reg_list); list_add(&sclp_state_change_event.list, &sclp_reg_list); timer_setup(&sclp_request_timer, NULL, 0); timer_setup(&sclp_queue_timer, sclp_req_queue_timeout, 0); diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index f5e34e1b6c9e..9b852a47ccc1 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -28,9 +28,9 @@ /* Lock to guard over changes to global variables */ static DEFINE_SPINLOCK(sclp_con_lock); /* List of free pages that can be used for console output buffering */ -static struct list_head sclp_con_pages; +static LIST_HEAD(sclp_con_pages); /* List of full struct sclp_buffer structures ready for output */ -static struct list_head sclp_con_outqueue; +static LIST_HEAD(sclp_con_outqueue); /* Pointer to current console buffer */ static struct sclp_buffer *sclp_conbuf; /* Timer for delayed output of console messages */ @@ -323,12 +323,10 @@ sclp_console_init(void) if (rc) return rc; /* Allocate pages for output buffering */ - INIT_LIST_HEAD(&sclp_con_pages); for (i = 0; i < sclp_console_pages; i++) { page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); list_add_tail(page, &sclp_con_pages); } - INIT_LIST_HEAD(&sclp_con_outqueue); sclp_conbuf = NULL; timer_setup(&sclp_con_timer, sclp_console_timeout, 0); diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index cf73fc3fd0f9..4456ceb23bd2 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -37,9 +37,9 @@ /* Lock to guard over changes to global variables. */ static DEFINE_SPINLOCK(sclp_tty_lock); /* List of free pages that can be used for console output buffering. */ -static struct list_head sclp_tty_pages; +static LIST_HEAD(sclp_tty_pages); /* List of full struct sclp_buffer structures ready for output. */ -static struct list_head sclp_tty_outqueue; +static LIST_HEAD(sclp_tty_outqueue); /* Counter how many buffers are emitted. */ static int sclp_tty_buffer_count; /* Pointer to current console buffer. */ @@ -516,7 +516,6 @@ sclp_tty_init(void) return rc; } /* Allocate pages for output buffering */ - INIT_LIST_HEAD(&sclp_tty_pages); for (i = 0; i < MAX_KMEM_PAGES; i++) { page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (page == NULL) { @@ -525,7 +524,6 @@ sclp_tty_init(void) } list_add_tail((struct list_head *) page, &sclp_tty_pages); } - INIT_LIST_HEAD(&sclp_tty_outqueue); timer_setup(&sclp_tty_timer, sclp_tty_timeout, 0); sclp_ttybuf = NULL; sclp_tty_buffer_count = 0; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 3a6e0af436f4..7f4445b0f819 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -64,10 +64,10 @@ static struct tty_port sclp_vt220_port; static DEFINE_SPINLOCK(sclp_vt220_lock); /* List of empty pages to be used as write request buffers */ -static struct list_head sclp_vt220_empty; +static LIST_HEAD(sclp_vt220_empty); /* List of pending requests */ -static struct list_head sclp_vt220_outqueue; +static LIST_HEAD(sclp_vt220_outqueue); /* Suspend mode flag */ static int sclp_vt220_suspended; @@ -693,8 +693,6 @@ static int __init __sclp_vt220_init(int num_pages) sclp_vt220_init_count++; if (sclp_vt220_init_count != 1) return 0; - INIT_LIST_HEAD(&sclp_vt220_empty); - INIT_LIST_HEAD(&sclp_vt220_outqueue); timer_setup(&sclp_vt220_timer, sclp_vt220_timeout, 0); tty_port_init(&sclp_vt220_port); sclp_vt220_current_request = NULL; -- cgit v1.2.3 From 3081e6160565078b3a37ebb33bd8301ab18dd6d7 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Mon, 29 Mar 2021 20:38:07 +0200 Subject: s390/cio: use DECLARE_WAIT_QUEUE_HEAD for static work_queue_head_t Use DECLARE_WAIT_QUEUE_HEAD to declare and statically initialize the work_queue_head_t. Signed-off-by: Vineeth Vijayan Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens --- drivers/s390/cio/chp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 8d0de6adcad0..e42113825415 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -50,7 +50,7 @@ static unsigned long chp_info_expires; static struct work_struct cfg_work; /* Wait queue for configure completion events. */ -static wait_queue_head_t cfg_wait_queue; +static DECLARE_WAIT_QUEUE_HEAD(cfg_wait_queue); /* Set vary state for given chpid. */ static void set_chp_logically_online(struct chp_id chpid, int onoff) @@ -829,7 +829,6 @@ static int __init chp_init(void) if (ret) return ret; INIT_WORK(&cfg_work, cfg_func); - init_waitqueue_head(&cfg_wait_queue); if (info_update()) return 0; /* Register available channel-paths. */ -- cgit v1.2.3 From 70fac8088cfad9f3b379c9082832b4d7532c16c2 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 15 Apr 2021 11:22:03 +0200 Subject: s390/zcrypt: fix zcard and zqueue hot-unplug memleak Tests with kvm and a kmemdebug kernel showed, that on hot unplug the zcard and zqueue structs for the unplugged card or queue are not properly freed because of a mismatch with get/put for the embedded kref counter. This fix now adjusts the handling of the kref counters. With init the kref counter starts with 1. This initial value needs to drop to zero with the unregister of the card or queue to trigger the release and free the object. Fixes: 29c2680fd2bf ("s390/ap: fix ap devices reference counting") Reported-by: Marc Hartmayer Signed-off-by: Harald Freudenberger Cc: stable@vger.kernel.org Reviewed-by: Julian Wiedmann Signed-off-by: Heiko Carstens --- drivers/s390/crypto/zcrypt_card.c | 1 + drivers/s390/crypto/zcrypt_queue.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/zcrypt_card.c b/drivers/s390/crypto/zcrypt_card.c index 33b23884b133..09fe6bb8880b 100644 --- a/drivers/s390/crypto/zcrypt_card.c +++ b/drivers/s390/crypto/zcrypt_card.c @@ -192,5 +192,6 @@ void zcrypt_card_unregister(struct zcrypt_card *zc) spin_unlock(&zcrypt_list_lock); sysfs_remove_group(&zc->card->ap_dev.device.kobj, &zcrypt_card_attr_group); + zcrypt_card_put(zc); } EXPORT_SYMBOL(zcrypt_card_unregister); diff --git a/drivers/s390/crypto/zcrypt_queue.c b/drivers/s390/crypto/zcrypt_queue.c index 5062eae73d4a..c3ffbd26b73f 100644 --- a/drivers/s390/crypto/zcrypt_queue.c +++ b/drivers/s390/crypto/zcrypt_queue.c @@ -223,5 +223,6 @@ void zcrypt_queue_unregister(struct zcrypt_queue *zq) sysfs_remove_group(&zq->queue->ap_dev.device.kobj, &zcrypt_queue_attr_group); zcrypt_card_put(zc); + zcrypt_queue_put(zq); } EXPORT_SYMBOL(zcrypt_queue_unregister); -- cgit v1.2.3