From 37564ed834aca26993b77b9b2a0119ec1ba6e00c Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Tue, 9 Feb 2021 09:33:12 -0500 Subject: s390/uv: add prot virt guest/host indication files Let's export the prot_virt_guest and prot_virt_host variables into the UV sysfs firmware interface to make them easily consumable by administrators. prot_virt_host being 1 indicates that we did the UV initialization (opt-in) prot_virt_guest being 1 indicates that the UV indicates the share and unshare ultravisor calls which is an indication that we are running as a protected guest. Signed-off-by: Janosch Frank Acked-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- arch/s390/kernel/uv.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index b2d2ad153067..04b6463d8f9f 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -406,6 +406,35 @@ static struct attribute_group uv_query_attr_group = { .attrs = uv_query_attrs, }; +static ssize_t uv_is_prot_virt_guest(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + int val = 0; + +#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST + val = prot_virt_guest; +#endif + return scnprintf(page, PAGE_SIZE, "%d\n", val); +} + +static ssize_t uv_is_prot_virt_host(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%d\n", prot_virt_host); +} + +static struct kobj_attribute uv_prot_virt_guest = + __ATTR(prot_virt_guest, 0444, uv_is_prot_virt_guest, NULL); + +static struct kobj_attribute uv_prot_virt_host = + __ATTR(prot_virt_host, 0444, uv_is_prot_virt_host, NULL); + +static const struct attribute *uv_prot_virt_attrs[] = { + &uv_prot_virt_guest.attr, + &uv_prot_virt_host.attr, + NULL, +}; + static struct kset *uv_query_kset; static struct kobject *uv_kobj; @@ -420,15 +449,21 @@ static int __init uv_info_init(void) if (!uv_kobj) return -ENOMEM; + rc = sysfs_create_files(uv_kobj, uv_prot_virt_attrs); + if (rc) + goto out_kobj; + uv_query_kset = kset_create_and_add("query", NULL, uv_kobj); if (!uv_query_kset) - goto out_kobj; + goto out_ind_files; rc = sysfs_create_group(&uv_query_kset->kobj, &uv_query_attr_group); if (!rc) return 0; kset_unregister(uv_query_kset); +out_ind_files: + sysfs_remove_files(uv_kobj, uv_prot_virt_attrs); out_kobj: kobject_del(uv_kobj); kobject_put(uv_kobj); -- cgit v1.2.3 From f6576a1b4896b984dce0e8393efeba68cc2b96c8 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Tue, 2 Mar 2021 14:55:21 +0100 Subject: s390/pci: refactor zpci function states The current zdev->state mixes the configuration states supported by CLP with an additional Online state which is used inconsistently to include enabled zPCI functions which are not yet visible to the common PCI subsytem. In preparation for a clean separation between architected configuration states and fine grained function states remove the Online function state. Where we previously checked for Online it is more accurate to check if the function is enabled to avoid an edge case where a disabled device was still treated as Online. This also simplifies checks whether a function is configured as this is now directly reflected by its function state. Reviewed-by: Matthew Rosato Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pci.h | 1 - arch/s390/pci/pci.c | 10 ++++------ drivers/pci/hotplug/s390_pci_hpc.c | 8 +------- 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index a75d94a9bcb2..5ed11936dc3b 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -85,7 +85,6 @@ enum zpci_state { ZPCI_FN_STATE_STANDBY = 0, ZPCI_FN_STATE_CONFIGURED = 1, ZPCI_FN_STATE_RESERVED = 2, - ZPCI_FN_STATE_ONLINE = 3, }; struct zpci_bar_struct { diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 91064077526d..053113bcad60 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -661,7 +661,6 @@ int zpci_enable_device(struct zpci_dev *zdev) if (rc) goto out_dma; - zdev->state = ZPCI_FN_STATE_ONLINE; return 0; out_dma: @@ -770,7 +769,7 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) return 0; error_disable: - if (zdev->state == ZPCI_FN_STATE_ONLINE) + if (zdev_enabled(zdev)) zpci_disable_device(zdev); error_destroy_iommu: zpci_destroy_iommu(zdev); @@ -787,11 +786,10 @@ void zpci_release_device(struct kref *kref) if (zdev->zbus->bus) zpci_remove_device(zdev, false); - switch (zdev->state) { - case ZPCI_FN_STATE_ONLINE: - case ZPCI_FN_STATE_CONFIGURED: + if (zdev_enabled(zdev)) zpci_disable_device(zdev); - fallthrough; + + switch (zdev->state) { case ZPCI_FN_STATE_STANDBY: if (zdev->has_hp_slot) zpci_exit_slot(zdev); diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index a047c421debe..c93c09ae4b04 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -20,12 +20,6 @@ #define SLOT_NAME_SIZE 10 -static int zpci_fn_configured(enum zpci_state state) -{ - return state == ZPCI_FN_STATE_CONFIGURED || - state == ZPCI_FN_STATE_ONLINE; -} - static inline int zdev_configure(struct zpci_dev *zdev) { int ret = sclp_pci_configure(zdev->fid); @@ -85,7 +79,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) struct pci_dev *pdev; int rc; - if (!zpci_fn_configured(zdev->state)) + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) return -EIO; pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); -- cgit v1.2.3 From a9045c2210448473a321a8bf266541e5644aaae2 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 5 Mar 2021 14:32:02 +0100 Subject: s390/pci: deconfigure device on release When zpci_release_device() is called on a zPCI function that is still configured it would not be deconfigured. Until now this hasn't caused any problems because zpci_zdev_put() is only ever called for devices in Standby or Reserved. Fix it by adding sclp_pci_deconfigure() to the switch when in Configured state. Reviewed-by: Matthew Rosato Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 053113bcad60..5d6b838c0f22 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -782,6 +782,7 @@ error: void zpci_release_device(struct kref *kref) { struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref); + int ret; if (zdev->zbus->bus) zpci_remove_device(zdev, false); @@ -790,6 +791,10 @@ void zpci_release_device(struct kref *kref) zpci_disable_device(zdev); switch (zdev->state) { + case ZPCI_FN_STATE_CONFIGURED: + ret = sclp_pci_deconfigure(zdev->fid); + zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); + fallthrough; case ZPCI_FN_STATE_STANDBY: if (zdev->has_hp_slot) zpci_exit_slot(zdev); -- cgit v1.2.3 From dee60c0dbc837ddca8abcb868e53ca3e9d11ea4c Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 16 Sep 2020 10:52:35 +0200 Subject: s390/pci: add zpci_event_hard_deconfigured() Extract the handling of PEC 0x0304 into a function and make sure we only attempt to disable the function if it is enabled. Also check for errors returned by zpci_disable_device() and leave the function alone if there are any. Reviewed-by: Matthew Rosato Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci_event.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index ac0c65cdd69d..0474ff8c6dbd 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -73,10 +73,31 @@ void zpci_event_error(void *data) __zpci_event_error(data); } +static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh) +{ + enum zpci_state state; + int rc; + + zdev->fh = fh; + /* Give the driver a hint that the function is + * already unusable. + */ + zpci_remove_device(zdev, true); + if (zdev_enabled(zdev)) { + rc = zpci_disable_device(zdev); + if (rc) + return; + } + zdev->state = ZPCI_FN_STATE_STANDBY; + if (!clp_get_state(zdev->fid, &state) && + state == ZPCI_FN_STATE_RESERVED) { + zpci_zdev_put(zdev); + } +} + static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) { struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); - enum zpci_state state; struct pci_dev *pdev; int ret; @@ -134,20 +155,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) break; case 0x0304: /* Configured -> Standby|Reserved */ - if (!zdev) - break; - /* Give the driver a hint that the function is - * already unusable. - */ - zpci_remove_device(zdev, true); - - zdev->fh = ccdf->fh; - zpci_disable_device(zdev); - zdev->state = ZPCI_FN_STATE_STANDBY; - if (!clp_get_state(ccdf->fid, &state) && - state == ZPCI_FN_STATE_RESERVED) { - zpci_zdev_put(zdev); - } + if (zdev) + zpci_event_hard_deconfigured(zdev, ccdf->fh); break; case 0x0306: /* 0x308 or 0x302 for multiple devices */ zpci_remove_reserved_devices(); -- 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 'arch') 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 2631f6b6f22ca613238a416a09e3d2771def6f88 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Tue, 3 Nov 2020 10:41:20 +0100 Subject: s390/pci: unify de-/configure for slots and events A zPCI event with PEC 0x0301 for an existing zPCI device goes through the same actions as enable_slot(). Similarly a zPCI event with PEC 0x0303 does the same steps as disable_slot(). We can thus unify both actions as zpci_configure_device() respectively zpci_deconfigure_device(). Reviewed-by: Matthew Rosato Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pci.h | 3 ++ arch/s390/pci/pci.c | 89 +++++++++++++++++++++++++++++++++++++- arch/s390/pci/pci_event.c | 44 ++++--------------- drivers/pci/hotplug/s390_pci_hpc.c | 52 +--------------------- 4 files changed, 100 insertions(+), 88 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 5ed11936dc3b..c454dfb9fc4b 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -204,6 +204,9 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state); void zpci_remove_device(struct zpci_dev *zdev, bool set_error); int zpci_enable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *); +int zpci_configure_device(struct zpci_dev *zdev, u32 fh); +int zpci_deconfigure_device(struct zpci_dev *zdev); + int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64); int zpci_unregister_ioat(struct zpci_dev *, u8); void zpci_remove_reserved_devices(void); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 5d6b838c0f22..6d84ec8f1dd6 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -668,7 +668,6 @@ out_dma: out: return rc; } -EXPORT_SYMBOL_GPL(zpci_enable_device); int zpci_disable_device(struct zpci_dev *zdev) { @@ -679,7 +678,6 @@ int zpci_disable_device(struct zpci_dev *zdev) */ return clp_disable_fh(zdev); } -EXPORT_SYMBOL_GPL(zpci_disable_device); /* zpci_remove_device - Removes the given zdev from the PCI core * @zdev: the zdev to be removed from the PCI core @@ -779,6 +777,93 @@ error: return rc; } +/** + * zpci_configure_device() - Configure a zpci_dev + * @zdev: The zpci_dev to be configured + * @fh: The general function handle supplied by the platform + * + * Configuring a device includes the configuration itself, if not done by the + * platform, enabling, scanning and adding it to the common code PCI subsystem. + * If any failure occurs, the zpci_dev is left in Standby. + * + * Return: 0 on success, or an error code otherwise + */ +int zpci_configure_device(struct zpci_dev *zdev, u32 fh) +{ + struct pci_dev *pdev; + int rc; + + zdev->fh = fh; + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) { + rc = sclp_pci_configure(zdev->fid); + zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc); + if (rc) + return rc; + zdev->state = ZPCI_FN_STATE_CONFIGURED; + } + + rc = zpci_enable_device(zdev); + if (rc) + goto error; + + /* the PCI function will be scanned once function 0 appears */ + if (!zdev->zbus->bus) + return 0; + + pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn); + if (!pdev) + goto error_disable; + + pci_bus_add_device(pdev); + pci_lock_rescan_remove(); + pci_bus_add_devices(zdev->zbus->bus); + pci_unlock_rescan_remove(); + return 0; + +error_disable: + zpci_disable_device(zdev); +error: + if (zdev->state == ZPCI_FN_STATE_CONFIGURED) { + rc = sclp_pci_deconfigure(zdev->fid); + zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, rc); + if (!rc) + zdev->state = ZPCI_FN_STATE_STANDBY; + } + return rc; +} + +/** + * zpci_deconfigure_device() - Deconfigure a zpci_dev + * @zdev: The zpci_dev to configure + * + * Deconfigure a zPCI function that is currently configured and possibly known + * to the common code PCI subsystem. + * If any failure occurs the device is left as is. + * + * Return: 0 on success, or an error code otherwise + */ +int zpci_deconfigure_device(struct zpci_dev *zdev) +{ + int rc; + + if (zdev->zbus->bus) + zpci_remove_device(zdev, false); + + if (zdev_enabled(zdev)) { + rc = zpci_disable_device(zdev); + if (rc) + return rc; + } + + rc = sclp_pci_deconfigure(zdev->fid); + zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, rc); + if (rc) + return rc; + zdev->state = ZPCI_FN_STATE_STANDBY; + + return 0; +} + void zpci_release_device(struct kref *kref) { struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref); diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 0474ff8c6dbd..2676df9816f0 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -98,8 +98,6 @@ static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh) static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) { struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); - struct pci_dev *pdev; - int ret; zpci_err("avail CCDF:\n"); zpci_err_hex(ccdf, sizeof(*ccdf)); @@ -113,46 +111,20 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) /* the configuration request may be stale */ if (zdev->state != ZPCI_FN_STATE_STANDBY) break; - zdev->fh = ccdf->fh; zdev->state = ZPCI_FN_STATE_CONFIGURED; - ret = zpci_enable_device(zdev); - if (ret) - break; - - /* the PCI function will be scanned once function 0 appears */ - if (!zdev->zbus->bus) - break; - - pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn); - if (!pdev) - break; - - pci_bus_add_device(pdev); - pci_lock_rescan_remove(); - pci_bus_add_devices(zdev->zbus->bus); - pci_unlock_rescan_remove(); + zpci_configure_device(zdev, ccdf->fh); break; case 0x0302: /* Reserved -> Standby */ - if (!zdev) { + if (!zdev) zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY); - break; - } - zdev->fh = ccdf->fh; + else + zdev->fh = ccdf->fh; break; case 0x0303: /* Deconfiguration requested */ - if (!zdev) - break; - zpci_remove_device(zdev, false); - - ret = zpci_disable_device(zdev); - if (ret) - break; - - ret = sclp_pci_deconfigure(zdev->fid); - zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); - if (!ret) - zdev->state = ZPCI_FN_STATE_STANDBY; - + if (zdev) { + zdev->fh = ccdf->fh; + zpci_deconfigure_device(zdev); + } break; case 0x0304: /* Configured -> Standby|Reserved */ if (zdev) diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index c93c09ae4b04..154532663a70 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -20,56 +20,15 @@ #define SLOT_NAME_SIZE 10 -static inline int zdev_configure(struct zpci_dev *zdev) -{ - int ret = sclp_pci_configure(zdev->fid); - - zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, ret); - if (!ret) - zdev->state = ZPCI_FN_STATE_CONFIGURED; - - return ret; -} - -static inline int zdev_deconfigure(struct zpci_dev *zdev) -{ - int ret = sclp_pci_deconfigure(zdev->fid); - - zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); - if (!ret) - zdev->state = ZPCI_FN_STATE_STANDBY; - - return ret; -} - static int enable_slot(struct hotplug_slot *hotplug_slot) { struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, hotplug_slot); - struct zpci_bus *zbus = zdev->zbus; - int rc; if (zdev->state != ZPCI_FN_STATE_STANDBY) return -EIO; - rc = zdev_configure(zdev); - if (rc) - return rc; - - rc = zpci_enable_device(zdev); - if (rc) - goto out_deconfigure; - - pci_scan_slot(zbus->bus, zdev->devfn); - pci_lock_rescan_remove(); - pci_bus_add_devices(zbus->bus); - pci_unlock_rescan_remove(); - - return rc; - -out_deconfigure: - zdev_deconfigure(zdev); - return rc; + return zpci_configure_device(zdev, zdev->fh); } static int disable_slot(struct hotplug_slot *hotplug_slot) @@ -77,7 +36,6 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, hotplug_slot); struct pci_dev *pdev; - int rc; if (zdev->state != ZPCI_FN_STATE_CONFIGURED) return -EIO; @@ -89,13 +47,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) } pci_dev_put(pdev); - zpci_remove_device(zdev, false); - - rc = zpci_disable_device(zdev); - if (rc) - return rc; - - return zdev_deconfigure(zdev); + return zpci_deconfigure_device(zdev); } static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) -- cgit v1.2.3 From 95b3a8b4014d82e79dc3ad03a1f8d6ee5f56b29d Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Tue, 26 Jan 2021 13:58:28 +0100 Subject: s390/pci: move zpci_remove_device() to bus code The zpci_remove_device() function removes the device from the PCI common code core which is an operation dealing primarily with the zbus and PCI bus code. With that and to match an upcoming refactoring of the symmetric scanning part move it to the bus code. Reviewed-by: Matthew Rosato Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pci.h | 1 - arch/s390/pci/pci.c | 37 ++----------------------------------- arch/s390/pci/pci_bus.c | 33 +++++++++++++++++++++++++++++++++ arch/s390/pci/pci_bus.h | 2 ++ arch/s390/pci/pci_event.c | 2 +- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index c454dfb9fc4b..35dec33c2801 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -201,7 +201,6 @@ extern unsigned int s390_pci_no_rid; ----------------------------------------------------------------------------- */ /* Base stuff */ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state); -void zpci_remove_device(struct zpci_dev *zdev, bool set_error); int zpci_enable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *); int zpci_configure_device(struct zpci_dev *zdev, u32 fh); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 6d84ec8f1dd6..dd14641b2d20 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -679,39 +679,6 @@ int zpci_disable_device(struct zpci_dev *zdev) return clp_disable_fh(zdev); } -/* zpci_remove_device - Removes the given zdev from the PCI core - * @zdev: the zdev to be removed from the PCI core - * @set_error: if true the device's error state is set to permanent failure - * - * Sets a zPCI device to a configured but offline state; the zPCI - * device is still accessible through its hotplug slot and the zPCI - * API but is removed from the common code PCI bus, making it - * no longer available to drivers. - */ -void zpci_remove_device(struct zpci_dev *zdev, bool set_error) -{ - struct zpci_bus *zbus = zdev->zbus; - struct pci_dev *pdev; - - if (!zdev->zbus->bus) - return; - - pdev = pci_get_slot(zbus->bus, zdev->devfn); - if (pdev) { - if (set_error) - pdev->error_state = pci_channel_io_perm_failure; - if (pdev->is_virtfn) { - zpci_iov_remove_virtfn(pdev, zdev->vfn); - /* balance pci_get_slot */ - pci_dev_put(pdev); - return; - } - pci_stop_and_remove_bus_device_locked(pdev); - /* balance pci_get_slot */ - pci_dev_put(pdev); - } -} - /** * zpci_create_device() - Create a new zpci_dev and add it to the zbus * @fid: Function ID of the device to be created @@ -847,7 +814,7 @@ int zpci_deconfigure_device(struct zpci_dev *zdev) int rc; if (zdev->zbus->bus) - zpci_remove_device(zdev, false); + zpci_bus_remove_device(zdev, false); if (zdev_enabled(zdev)) { rc = zpci_disable_device(zdev); @@ -870,7 +837,7 @@ void zpci_release_device(struct kref *kref) int ret; if (zdev->zbus->bus) - zpci_remove_device(zdev, false); + zpci_bus_remove_device(zdev, false); if (zdev_enabled(zdev)) zpci_disable_device(zdev); diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index 755b46f4c595..ace9dbbe3bc1 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -30,6 +30,39 @@ static LIST_HEAD(zbus_list); static DEFINE_SPINLOCK(zbus_list_lock); static int zpci_nb_devices; +/* zpci_bus_remove_device - Removes the given zdev from the PCI core + * @zdev: the zdev to be removed from the PCI core + * @set_error: if true the device's error state is set to permanent failure + * + * Sets a zPCI device to a configured but offline state; the zPCI + * device is still accessible through its hotplug slot and the zPCI + * API but is removed from the common code PCI bus, making it + * no longer available to drivers. + */ +void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error) +{ + struct zpci_bus *zbus = zdev->zbus; + struct pci_dev *pdev; + + if (!zdev->zbus->bus) + return; + + pdev = pci_get_slot(zbus->bus, zdev->devfn); + if (pdev) { + if (set_error) + pdev->error_state = pci_channel_io_perm_failure; + if (pdev->is_virtfn) { + zpci_iov_remove_virtfn(pdev, zdev->vfn); + /* balance pci_get_slot */ + pci_dev_put(pdev); + return; + } + pci_stop_and_remove_bus_device_locked(pdev); + /* balance pci_get_slot */ + pci_dev_put(pdev); + } +} + /* zpci_bus_scan * @zbus: the zbus holding the zdevices * @ops: the pci operations diff --git a/arch/s390/pci/pci_bus.h b/arch/s390/pci/pci_bus.h index f8dfac0b5b71..e04ca06a71b6 100644 --- a/arch/s390/pci/pci_bus.h +++ b/arch/s390/pci/pci_bus.h @@ -10,6 +10,8 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops); void zpci_bus_device_unregister(struct zpci_dev *zdev); +void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error); + void zpci_release_device(struct kref *kref); static inline void zpci_zdev_put(struct zpci_dev *zdev) { diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 2676df9816f0..9455c5be8820 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -82,7 +82,7 @@ static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh) /* Give the driver a hint that the function is * already unusable. */ - zpci_remove_device(zdev, true); + zpci_bus_remove_device(zdev, true); if (zdev_enabled(zdev)) { rc = zpci_disable_device(zdev); if (rc) -- 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 'arch') 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 5671d9718faf8c8520228c2acb91f3c0cc64192b Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Mon, 22 Mar 2021 11:55:00 +0530 Subject: s390/kernel: fix a typo s/struture/structure/ Signed-off-by: Bhaskar Chowdhury Link: https://lore.kernel.org/r/20210322062500.3109603-1-unixbhaskar@gmail.com Signed-off-by: Heiko Carstens --- arch/s390/kernel/os_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c index 0a5e4bafb6ad..5a7420b23aa8 100644 --- a/arch/s390/kernel/os_info.c +++ b/arch/s390/kernel/os_info.c @@ -52,7 +52,7 @@ void os_info_entry_add(int nr, void *ptr, u64 size) } /* - * Initialize OS info struture and set lowcore pointer + * Initialize OS info structure and set lowcore pointer */ void __init os_info_init(void) { -- cgit v1.2.3 From df2e400e07ad53a582ee934ce8384479d5ddf48b Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Tue, 23 Mar 2021 10:09:50 +0000 Subject: s390/uv: fix prot virt host indication compilation prot_virt_host is only available if CONFIG_KVM is enabled. So lets use a variable initialized to zero and overwrite it when that config option is set with prot_virt_host. Signed-off-by: Janosch Frank Fixes: 37564ed834ac ("s390/uv: add prot virt guest/host indication files") Reported-by: kernel test robot Signed-off-by: Heiko Carstens --- arch/s390/kernel/uv.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index 04b6463d8f9f..cbfbeab57c3b 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -420,7 +420,13 @@ static ssize_t uv_is_prot_virt_guest(struct kobject *kobj, static ssize_t uv_is_prot_virt_host(struct kobject *kobj, struct kobj_attribute *attr, char *page) { - return scnprintf(page, PAGE_SIZE, "%d\n", prot_virt_host); + int val = 0; + +#if IS_ENABLED(CONFIG_KVM) + val = prot_virt_host; +#endif + + return scnprintf(page, PAGE_SIZE, "%d\n", val); } static struct kobj_attribute uv_prot_virt_guest = -- cgit v1.2.3 From 84fa3962d5ff8cd23e85bea242cb32f27d879608 Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Mon, 22 Mar 2021 18:35:33 +0530 Subject: s390/crc32-vx: couple of typo fixes s/defintions/definitions/ s/intermedate/intermediate/ Signed-off-by: Bhaskar Chowdhury Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20210322130533.3805976-1-unixbhaskar@gmail.com Signed-off-by: Heiko Carstens --- arch/s390/crypto/crc32be-vx.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/s390/crypto/crc32be-vx.S b/arch/s390/crypto/crc32be-vx.S index 0099044e2c86..6b3d1009c392 100644 --- a/arch/s390/crypto/crc32be-vx.S +++ b/arch/s390/crypto/crc32be-vx.S @@ -32,7 +32,7 @@ * process particular chunks of the input data stream in parallel. * * For the CRC-32 variants, the constants are precomputed according to - * these defintions: + * these definitions: * * R1 = x4*128+64 mod P(x) * R2 = x4*128 mod P(x) @@ -189,7 +189,7 @@ ENTRY(crc32_be_vgfm_16) * Note: To compensate the division by x^32, use the vector unpack * instruction to move the leftmost word into the leftmost doubleword * of the vector register. The rightmost doubleword is multiplied - * with zero to not contribute to the intermedate results. + * with zero to not contribute to the intermediate results. */ /* T1(x) = floor( R(x) / x^32 ) GF2MUL u */ -- cgit v1.2.3 From 263df6e485445aff8f6189c1913b916b8c7f4f1d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Mar 2021 13:44:47 +0100 Subject: s390/spinlock: remove align attribute from arch_spinlock_t No need to add an align attribute for an integer. The alignment is correct anyway. Signed-off-by: Heiko Carstens --- arch/s390/include/asm/spinlock_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/include/asm/spinlock_types.h b/arch/s390/include/asm/spinlock_types.h index cfed272e4fd5..a2bbfd7df85f 100644 --- a/arch/s390/include/asm/spinlock_types.h +++ b/arch/s390/include/asm/spinlock_types.h @@ -8,7 +8,7 @@ typedef struct { int lock; -} __attribute__ ((aligned (4))) arch_spinlock_t; +} arch_spinlock_t; #define __ARCH_SPIN_LOCK_UNLOCKED { .lock = 0, } -- cgit v1.2.3 From 652d40b2f8bec14957295f999e3d329c3b53390f Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 24 Mar 2021 11:21:21 +0100 Subject: s390/pci: fix DMA cleanup on hard deconfigure In commit dee60c0dbc83 ("s390/pci: add zpci_event_hard_deconfigured()") we added a zdev_enabled() check to what was previously an uncoditional call to zpci_disable_device(). There are two problems with that. Firstly zpci_had_deconfigured() is only called on event 0x0304 for which the device is always already disabled by the platform so it is always false. Secondly zpci_disable_device() not only disables the device but also calls zpci_dma_exit_device() which is thus not called and we leak the DMA tables. Fix this by calling zpci_disable_device() unconditionally to perform Linux side cleanup including the freeing of DMA tables. Fixes: dee60c0dbc83 ("s390/pci: add zpci_event_hard_deconfigured()") Reviewed-by: Matthew Rosato Acked-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci_event.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 9455c5be8820..ae3054d85491 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "pci_bus.h" @@ -76,18 +77,16 @@ void zpci_event_error(void *data) static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh) { enum zpci_state state; - int rc; zdev->fh = fh; /* Give the driver a hint that the function is * already unusable. */ zpci_bus_remove_device(zdev, true); - if (zdev_enabled(zdev)) { - rc = zpci_disable_device(zdev); - if (rc) - return; - } + /* Even though the device is already gone we still + * need to free zPCI resources as part of the disable. + */ + zpci_disable_device(zdev); zdev->state = ZPCI_FN_STATE_STANDBY; if (!clp_get_state(zdev->fid, &state) && state == ZPCI_FN_STATE_RESERVED) { -- cgit v1.2.3 From 408f2c9c15682fc21b645fdec1f726492e235c4b Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 24 Feb 2021 11:29:36 +0100 Subject: s390/pci: expose UID uniqueness guarantee On s390 each PCI device has a user-defined ID (UID) exposed under /sys/bus/pci/devices//uid. This ID was designed to serve as the PCI device's primary index and to match the device within Linux to the device configured in the hypervisor. To serve as a primary identifier the UID must be unique within the Linux instance, this is guaranteed by the platform if and only if the UID Uniqueness Checking flag is set within the CLP List PCI Functions response. While the UID has been exposed to userspace since commit ac4995b9d570 ("s390/pci: add some new arch specific pci attributes") whether or not the platform guarantees its uniqueness for the lifetime of the Linux instance while defined is not visible from userspace. Remedy this by exposing this as a per device attribute at /sys/bus/pci/devices//uid_is_unique Keeping this a per device attribute allows for maximum flexibility if we ever end up with some devices not having a UID or not enjoying the guaranteed uniqueness. Signed-off-by: Niklas Schnelle Reviewed-by: Viktor Mihajlovski Signed-off-by: Heiko Carstens --- Documentation/s390/pci.rst | 14 +++++++++++--- arch/s390/pci/pci_sysfs.c | 9 +++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/Documentation/s390/pci.rst b/Documentation/s390/pci.rst index 492850bff316..8157f0cddbc2 100644 --- a/Documentation/s390/pci.rst +++ b/Documentation/s390/pci.rst @@ -50,7 +50,8 @@ Entries specific to zPCI functions and entries that hold zPCI information. * /sys/bus/pci/slots/XXXXXXXX The slot entries are set up using the function identifier (FID) of the - PCI function. + PCI function. The format depicted as XXXXXXXX above is 8 hexadecimal digits + with 0 padding and lower case hexadecimal digitis. - /sys/bus/pci/slots/XXXXXXXX/power @@ -88,8 +89,15 @@ Entries specific to zPCI functions and entries that hold zPCI information. is attached to. - uid - The unique identifier (UID) is defined when configuring an LPAR and is - unique in the LPAR. + The user identifier (UID) may be defined as part of the machine + configuration or the z/VM or KVM guest configuration. If the accompanying + uid_is_unique attribute is 1 the platform guarantees that the UID is unique + within that instance and no devices with the same UID can be attached + during the lifetime of the system. + + - uid_is_unique + Indicates whether the user identifier (UID) is guaranteed to be and remain + unique within this Linux instance. - pfip/segmentX The segments determine the isolation of a function. diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 5c028bee91b9..e14d346dafd6 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -131,6 +131,13 @@ static ssize_t report_error_write(struct file *filp, struct kobject *kobj, } static BIN_ATTR(report_error, S_IWUSR, NULL, report_error_write, PAGE_SIZE); +static ssize_t uid_is_unique_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", zpci_unique_uid ? 1 : 0); +} +static DEVICE_ATTR_RO(uid_is_unique); + static struct bin_attribute *zpci_bin_attrs[] = { &bin_attr_util_string, &bin_attr_report_error, @@ -148,8 +155,10 @@ static struct attribute *zpci_dev_attrs[] = { &dev_attr_uid.attr, &dev_attr_recover.attr, &dev_attr_mio_enabled.attr, + &dev_attr_uid_is_unique.attr, NULL, }; + static struct attribute_group zpci_attr_group = { .attrs = zpci_dev_attrs, .bin_attrs = zpci_bin_attrs, -- cgit v1.2.3 From f38033c8dbc3365da163fece752e903fab7fced3 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Tue, 30 Mar 2021 18:56:25 +0800 Subject: s390/cio: remove duplicate struct ccw1 declaration struct ccw1 is declared twice. One has been declared at 21st line. Remove the duplicate. Signed-off-by: Wan Jiabing Acked-by: Vineeth Vijayan Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ccwdev.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index 778247bb1d61..d4e90f2ba77e 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -152,9 +152,6 @@ extern struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv, * when new devices for its type pop up */ extern int ccw_driver_register (struct ccw_driver *driver); extern void ccw_driver_unregister (struct ccw_driver *driver); - -struct ccw1; - extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long); extern int ccw_device_set_options(struct ccw_device *, unsigned long); extern void ccw_device_clear_options(struct ccw_device *, unsigned long); -- cgit v1.2.3 From 3784231b1e091857bd129fd9658a8b3cedbdcd58 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 29 Mar 2021 18:32:55 +0200 Subject: s390/mm: fix phys vs virt confusion in mark_kernel_pXd() functions family Due to historical reasons mark_kernel_pXd() functions misuse the notion of physical vs virtual addresses difference. Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/page-states.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/s390/mm/page-states.c b/arch/s390/mm/page-states.c index 567c69f3069e..7f0e154a470a 100644 --- a/arch/s390/mm/page-states.c +++ b/arch/s390/mm/page-states.c @@ -112,7 +112,7 @@ static void mark_kernel_pmd(pud_t *pud, unsigned long addr, unsigned long end) next = pmd_addr_end(addr, end); if (pmd_none(*pmd) || pmd_large(*pmd)) continue; - page = virt_to_page(pmd_val(*pmd)); + page = phys_to_page(pmd_val(*pmd)); set_bit(PG_arch_1, &page->flags); } while (pmd++, addr = next, addr != end); } @@ -130,7 +130,7 @@ static void mark_kernel_pud(p4d_t *p4d, unsigned long addr, unsigned long end) if (pud_none(*pud) || pud_large(*pud)) continue; if (!pud_folded(*pud)) { - page = virt_to_page(pud_val(*pud)); + page = phys_to_page(pud_val(*pud)); for (i = 0; i < 3; i++) set_bit(PG_arch_1, &page[i].flags); } @@ -151,7 +151,7 @@ static void mark_kernel_p4d(pgd_t *pgd, unsigned long addr, unsigned long end) if (p4d_none(*p4d)) continue; if (!p4d_folded(*p4d)) { - page = virt_to_page(p4d_val(*p4d)); + page = phys_to_page(p4d_val(*p4d)); for (i = 0; i < 3; i++) set_bit(PG_arch_1, &page[i].flags); } @@ -173,7 +173,7 @@ static void mark_kernel_pgd(void) if (pgd_none(*pgd)) continue; if (!pgd_folded(*pgd)) { - page = virt_to_page(pgd_val(*pgd)); + page = phys_to_page(pgd_val(*pgd)); for (i = 0; i < 3; i++) set_bit(PG_arch_1, &page[i].flags); } -- cgit v1.2.3 From 644975179c00802936c5afc732d9df7f63f735a0 Mon Sep 17 00:00:00 2001 From: zhongbaisong Date: Wed, 7 Apr 2021 20:38:55 +0800 Subject: s390/protvirt: fix error return code in uv_info_init() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Reported-by: Hulk Robot Signed-off-by: Baisong Zhong Fixes: 37564ed834ac ("s390/uv: add prot virt guest/host indication files") Link: https://lore.kernel.org/r/2f7d62a4-3e75-b2b4-951b-75ef8ef59d16@huawei.com Signed-off-by: Heiko Carstens --- arch/s390/kernel/uv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index cbfbeab57c3b..370f664580af 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -460,8 +460,10 @@ static int __init uv_info_init(void) goto out_kobj; uv_query_kset = kset_create_and_add("query", NULL, uv_kobj); - if (!uv_query_kset) + if (!uv_query_kset) { + rc = -ENOMEM; goto out_ind_files; + } rc = sysfs_create_group(&uv_query_kset->kobj, &uv_query_attr_group); if (!rc) -- cgit v1.2.3 From 6f8daa2953ecd1e8e853939f2007b4160591b8a6 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 7 Apr 2021 21:06:41 +0200 Subject: s390/traps: convert pgm_check.S to C Convert the program check table to C. Which allows to get rid of yet another assembler file, and also enables proper type checking for the table. Reviewed-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/include/asm/entry-common.h | 4 - arch/s390/kernel/Makefile | 2 +- arch/s390/kernel/entry.h | 22 ------ arch/s390/kernel/pgm_check.S | 147 ----------------------------------- arch/s390/kernel/traps.c | 65 ++++++++++++++-- 5 files changed, 59 insertions(+), 181 deletions(-) delete mode 100644 arch/s390/kernel/pgm_check.S (limited to 'arch') diff --git a/arch/s390/include/asm/entry-common.h b/arch/s390/include/asm/entry-common.h index 75cebc80474e..9cceb26ed63f 100644 --- a/arch/s390/include/asm/entry-common.h +++ b/arch/s390/include/asm/entry-common.h @@ -14,10 +14,6 @@ void do_per_trap(struct pt_regs *regs); void do_syscall(struct pt_regs *regs); -typedef void (*pgm_check_func)(struct pt_regs *regs); - -extern pgm_check_func pgm_check_table[128]; - #ifdef CONFIG_DEBUG_ENTRY static __always_inline void arch_check_user_regs(struct pt_regs *regs) { diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index c97818a382f3..68ca1834316f 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -36,7 +36,7 @@ CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o obj-y += processor.o syscall.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o -obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o +obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 3d0c0ac5c20e..c7969d67f317 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -26,29 +26,7 @@ void do_dat_exception(struct pt_regs *regs); void do_secure_storage_access(struct pt_regs *regs); void do_non_secure_storage_access(struct pt_regs *regs); void do_secure_storage_violation(struct pt_regs *regs); - -void addressing_exception(struct pt_regs *regs); -void data_exception(struct pt_regs *regs); void default_trap_handler(struct pt_regs *regs); -void divide_exception(struct pt_regs *regs); -void execute_exception(struct pt_regs *regs); -void hfp_divide_exception(struct pt_regs *regs); -void hfp_overflow_exception(struct pt_regs *regs); -void hfp_significance_exception(struct pt_regs *regs); -void hfp_sqrt_exception(struct pt_regs *regs); -void hfp_underflow_exception(struct pt_regs *regs); -void illegal_op(struct pt_regs *regs); -void operand_exception(struct pt_regs *regs); -void overflow_exception(struct pt_regs *regs); -void privileged_op(struct pt_regs *regs); -void space_switch_exception(struct pt_regs *regs); -void special_op_exception(struct pt_regs *regs); -void specification_exception(struct pt_regs *regs); -void transaction_exception(struct pt_regs *regs); -void translation_exception(struct pt_regs *regs); -void vector_exception(struct pt_regs *regs); -void monitor_event_exception(struct pt_regs *regs); - void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); void kernel_stack_overflow(struct pt_regs * regs); void do_signal(struct pt_regs *regs); diff --git a/arch/s390/kernel/pgm_check.S b/arch/s390/kernel/pgm_check.S deleted file mode 100644 index 9a92638360ee..000000000000 --- a/arch/s390/kernel/pgm_check.S +++ /dev/null @@ -1,147 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Program check table. - * - * Copyright IBM Corp. 2012 - */ - -#include - -#define PGM_CHECK(handler) .quad handler -#define PGM_CHECK_DEFAULT PGM_CHECK(default_trap_handler) - -/* - * The program check table contains exactly 128 (0x00-0x7f) entries. Each - * line defines the function to be called corresponding to the program check - * interruption code. - */ -.section .rodata, "a" -ENTRY(pgm_check_table) -PGM_CHECK_DEFAULT /* 00 */ -PGM_CHECK(illegal_op) /* 01 */ -PGM_CHECK(privileged_op) /* 02 */ -PGM_CHECK(execute_exception) /* 03 */ -PGM_CHECK(do_protection_exception) /* 04 */ -PGM_CHECK(addressing_exception) /* 05 */ -PGM_CHECK(specification_exception) /* 06 */ -PGM_CHECK(data_exception) /* 07 */ -PGM_CHECK(overflow_exception) /* 08 */ -PGM_CHECK(divide_exception) /* 09 */ -PGM_CHECK(overflow_exception) /* 0a */ -PGM_CHECK(divide_exception) /* 0b */ -PGM_CHECK(hfp_overflow_exception) /* 0c */ -PGM_CHECK(hfp_underflow_exception) /* 0d */ -PGM_CHECK(hfp_significance_exception) /* 0e */ -PGM_CHECK(hfp_divide_exception) /* 0f */ -PGM_CHECK(do_dat_exception) /* 10 */ -PGM_CHECK(do_dat_exception) /* 11 */ -PGM_CHECK(translation_exception) /* 12 */ -PGM_CHECK(special_op_exception) /* 13 */ -PGM_CHECK_DEFAULT /* 14 */ -PGM_CHECK(operand_exception) /* 15 */ -PGM_CHECK_DEFAULT /* 16 */ -PGM_CHECK_DEFAULT /* 17 */ -PGM_CHECK(transaction_exception) /* 18 */ -PGM_CHECK_DEFAULT /* 19 */ -PGM_CHECK_DEFAULT /* 1a */ -PGM_CHECK(vector_exception) /* 1b */ -PGM_CHECK(space_switch_exception) /* 1c */ -PGM_CHECK(hfp_sqrt_exception) /* 1d */ -PGM_CHECK_DEFAULT /* 1e */ -PGM_CHECK_DEFAULT /* 1f */ -PGM_CHECK_DEFAULT /* 20 */ -PGM_CHECK_DEFAULT /* 21 */ -PGM_CHECK_DEFAULT /* 22 */ -PGM_CHECK_DEFAULT /* 23 */ -PGM_CHECK_DEFAULT /* 24 */ -PGM_CHECK_DEFAULT /* 25 */ -PGM_CHECK_DEFAULT /* 26 */ -PGM_CHECK_DEFAULT /* 27 */ -PGM_CHECK_DEFAULT /* 28 */ -PGM_CHECK_DEFAULT /* 29 */ -PGM_CHECK_DEFAULT /* 2a */ -PGM_CHECK_DEFAULT /* 2b */ -PGM_CHECK_DEFAULT /* 2c */ -PGM_CHECK_DEFAULT /* 2d */ -PGM_CHECK_DEFAULT /* 2e */ -PGM_CHECK_DEFAULT /* 2f */ -PGM_CHECK_DEFAULT /* 30 */ -PGM_CHECK_DEFAULT /* 31 */ -PGM_CHECK_DEFAULT /* 32 */ -PGM_CHECK_DEFAULT /* 33 */ -PGM_CHECK_DEFAULT /* 34 */ -PGM_CHECK_DEFAULT /* 35 */ -PGM_CHECK_DEFAULT /* 36 */ -PGM_CHECK_DEFAULT /* 37 */ -PGM_CHECK(do_dat_exception) /* 38 */ -PGM_CHECK(do_dat_exception) /* 39 */ -PGM_CHECK(do_dat_exception) /* 3a */ -PGM_CHECK(do_dat_exception) /* 3b */ -PGM_CHECK_DEFAULT /* 3c */ -PGM_CHECK(do_secure_storage_access) /* 3d */ -PGM_CHECK(do_non_secure_storage_access) /* 3e */ -PGM_CHECK(do_secure_storage_violation) /* 3f */ -PGM_CHECK(monitor_event_exception) /* 40 */ -PGM_CHECK_DEFAULT /* 41 */ -PGM_CHECK_DEFAULT /* 42 */ -PGM_CHECK_DEFAULT /* 43 */ -PGM_CHECK_DEFAULT /* 44 */ -PGM_CHECK_DEFAULT /* 45 */ -PGM_CHECK_DEFAULT /* 46 */ -PGM_CHECK_DEFAULT /* 47 */ -PGM_CHECK_DEFAULT /* 48 */ -PGM_CHECK_DEFAULT /* 49 */ -PGM_CHECK_DEFAULT /* 4a */ -PGM_CHECK_DEFAULT /* 4b */ -PGM_CHECK_DEFAULT /* 4c */ -PGM_CHECK_DEFAULT /* 4d */ -PGM_CHECK_DEFAULT /* 4e */ -PGM_CHECK_DEFAULT /* 4f */ -PGM_CHECK_DEFAULT /* 50 */ -PGM_CHECK_DEFAULT /* 51 */ -PGM_CHECK_DEFAULT /* 52 */ -PGM_CHECK_DEFAULT /* 53 */ -PGM_CHECK_DEFAULT /* 54 */ -PGM_CHECK_DEFAULT /* 55 */ -PGM_CHECK_DEFAULT /* 56 */ -PGM_CHECK_DEFAULT /* 57 */ -PGM_CHECK_DEFAULT /* 58 */ -PGM_CHECK_DEFAULT /* 59 */ -PGM_CHECK_DEFAULT /* 5a */ -PGM_CHECK_DEFAULT /* 5b */ -PGM_CHECK_DEFAULT /* 5c */ -PGM_CHECK_DEFAULT /* 5d */ -PGM_CHECK_DEFAULT /* 5e */ -PGM_CHECK_DEFAULT /* 5f */ -PGM_CHECK_DEFAULT /* 60 */ -PGM_CHECK_DEFAULT /* 61 */ -PGM_CHECK_DEFAULT /* 62 */ -PGM_CHECK_DEFAULT /* 63 */ -PGM_CHECK_DEFAULT /* 64 */ -PGM_CHECK_DEFAULT /* 65 */ -PGM_CHECK_DEFAULT /* 66 */ -PGM_CHECK_DEFAULT /* 67 */ -PGM_CHECK_DEFAULT /* 68 */ -PGM_CHECK_DEFAULT /* 69 */ -PGM_CHECK_DEFAULT /* 6a */ -PGM_CHECK_DEFAULT /* 6b */ -PGM_CHECK_DEFAULT /* 6c */ -PGM_CHECK_DEFAULT /* 6d */ -PGM_CHECK_DEFAULT /* 6e */ -PGM_CHECK_DEFAULT /* 6f */ -PGM_CHECK_DEFAULT /* 70 */ -PGM_CHECK_DEFAULT /* 71 */ -PGM_CHECK_DEFAULT /* 72 */ -PGM_CHECK_DEFAULT /* 73 */ -PGM_CHECK_DEFAULT /* 74 */ -PGM_CHECK_DEFAULT /* 75 */ -PGM_CHECK_DEFAULT /* 76 */ -PGM_CHECK_DEFAULT /* 77 */ -PGM_CHECK_DEFAULT /* 78 */ -PGM_CHECK_DEFAULT /* 79 */ -PGM_CHECK_DEFAULT /* 7a */ -PGM_CHECK_DEFAULT /* 7b */ -PGM_CHECK_DEFAULT /* 7c */ -PGM_CHECK_DEFAULT /* 7d */ -PGM_CHECK_DEFAULT /* 7e */ -PGM_CHECK_DEFAULT /* 7f */ diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index db7dd59b570c..e8b894184f83 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -89,7 +89,7 @@ void default_trap_handler(struct pt_regs *regs) } #define DO_ERROR_INFO(name, signr, sicode, str) \ -void name(struct pt_regs *regs) \ +static void name(struct pt_regs *regs) \ { \ do_trap(regs, signr, sicode, str); \ } @@ -141,13 +141,13 @@ static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc) do_trap(regs, SIGFPE, si_code, "floating point exception"); } -void translation_exception(struct pt_regs *regs) +static void translation_exception(struct pt_regs *regs) { /* May never happen. */ panic("Translation exception"); } -void illegal_op(struct pt_regs *regs) +static void illegal_op(struct pt_regs *regs) { __u8 opcode[6]; __u16 __user *location; @@ -189,7 +189,7 @@ NOKPROBE_SYMBOL(illegal_op); DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, "specification exception"); -void vector_exception(struct pt_regs *regs) +static void vector_exception(struct pt_regs *regs) { int si_code, vic; @@ -223,7 +223,7 @@ void vector_exception(struct pt_regs *regs) do_trap(regs, SIGFPE, si_code, "vector exception"); } -void data_exception(struct pt_regs *regs) +static void data_exception(struct pt_regs *regs) { save_fpu_regs(); if (current->thread.fpu.fpc & FPC_DXC_MASK) @@ -232,7 +232,7 @@ void data_exception(struct pt_regs *regs) do_trap(regs, SIGILL, ILL_ILLOPN, "data exception"); } -void space_switch_exception(struct pt_regs *regs) +static void space_switch_exception(struct pt_regs *regs) { /* Set user psw back to home space mode. */ if (user_mode(regs)) @@ -241,7 +241,7 @@ void space_switch_exception(struct pt_regs *regs) do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); } -void monitor_event_exception(struct pt_regs *regs) +static void monitor_event_exception(struct pt_regs *regs) { const struct exception_table_entry *fixup; @@ -293,6 +293,8 @@ void __init trap_init(void) test_monitor_call(); } +static void (*pgm_check_table[128])(struct pt_regs *regs); + void noinstr __do_pgm_check(struct pt_regs *regs) { unsigned long last_break = S390_lowcore.breaking_event_addr; @@ -353,3 +355,52 @@ out: exit_to_user_mode(); } } + +/* + * The program check table contains exactly 128 (0x00-0x7f) entries. Each + * line defines the function to be called corresponding to the program check + * interruption code. + */ +static void (*pgm_check_table[128])(struct pt_regs *regs) = { + [0x00] = default_trap_handler, + [0x01] = illegal_op, + [0x02] = privileged_op, + [0x03] = execute_exception, + [0x04] = do_protection_exception, + [0x05] = addressing_exception, + [0x06] = specification_exception, + [0x07] = data_exception, + [0x08] = overflow_exception, + [0x09] = divide_exception, + [0x0a] = overflow_exception, + [0x0b] = divide_exception, + [0x0c] = hfp_overflow_exception, + [0x0d] = hfp_underflow_exception, + [0x0e] = hfp_significance_exception, + [0x0f] = hfp_divide_exception, + [0x10] = do_dat_exception, + [0x11] = do_dat_exception, + [0x12] = translation_exception, + [0x13] = special_op_exception, + [0x14] = default_trap_handler, + [0x15] = operand_exception, + [0x16] = default_trap_handler, + [0x17] = default_trap_handler, + [0x18] = transaction_exception, + [0x19] = default_trap_handler, + [0x1a] = default_trap_handler, + [0x1b] = vector_exception, + [0x1c] = space_switch_exception, + [0x1d] = hfp_sqrt_exception, + [0x1e ... 0x37] = default_trap_handler, + [0x38] = do_dat_exception, + [0x39] = do_dat_exception, + [0x3a] = do_dat_exception, + [0x3b] = do_dat_exception, + [0x3c] = default_trap_handler, + [0x3d] = do_secure_storage_access, + [0x3e] = do_non_secure_storage_access, + [0x3f] = do_secure_storage_violation, + [0x40] = monitor_event_exception, + [0x41 ... 0x7f] = default_trap_handler, +}; -- cgit v1.2.3 From faf29a4d93a98b4ccd8a10297353a9d0779d231f Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Thu, 11 Feb 2021 14:20:03 +0100 Subject: s390/pci: introduce zpci_bus_scan_device() To match zpci_bus_scan_device() and the PCI common code terminology and to remove some code duplication, we pull the multiple uses of pci_scan_single_device() into a function. For now this has the side effect of adding each device to the PCI bus separately and locking and unlocking the rescan/remove lock for each instead of just once per bus. This is clearly less efficient but provides a correct intermediate behavior until a follow on change does both the adding and scanning only once per bus. Reviewed-by: Matthew Rosato Acked-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci.c | 9 ++------- arch/s390/pci/pci_bus.c | 35 +++++++++++++++++++++++++---------- arch/s390/pci/pci_bus.h | 1 + 3 files changed, 28 insertions(+), 17 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index dd14641b2d20..0bce6078bfd6 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -757,7 +757,6 @@ error: */ int zpci_configure_device(struct zpci_dev *zdev, u32 fh) { - struct pci_dev *pdev; int rc; zdev->fh = fh; @@ -777,14 +776,10 @@ int zpci_configure_device(struct zpci_dev *zdev, u32 fh) if (!zdev->zbus->bus) return 0; - pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn); - if (!pdev) + rc = zpci_bus_scan_device(zdev); + if (rc) goto error_disable; - pci_bus_add_device(pdev); - pci_lock_rescan_remove(); - pci_bus_add_devices(zdev->zbus->bus); - pci_unlock_rescan_remove(); return 0; error_disable: diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index ace9dbbe3bc1..7b37c4316e35 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -30,6 +30,29 @@ static LIST_HEAD(zbus_list); static DEFINE_SPINLOCK(zbus_list_lock); static int zpci_nb_devices; +/* zpci_bus_scan_device - Scan a single device adding it to the PCI core + * @zdev: the zdev to be scanned + * + * Scans the PCI function making it available to the common PCI code. + * + * Return: 0 on success, an error value otherwise + */ +int zpci_bus_scan_device(struct zpci_dev *zdev) +{ + struct pci_dev *pdev; + + pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn); + if (!pdev) + return -ENODEV; + + pci_bus_add_device(pdev); + pci_lock_rescan_remove(); + pci_bus_add_devices(zdev->zbus->bus); + pci_unlock_rescan_remove(); + + return 0; +} + /* zpci_bus_remove_device - Removes the given zdev from the PCI core * @zdev: the zdev to be removed from the PCI core * @set_error: if true the device's error state is set to permanent failure @@ -176,10 +199,10 @@ void pcibios_bus_add_device(struct pci_dev *pdev) static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) { - struct pci_bus *bus; struct resource_entry *window, *n; struct resource *res; struct pci_dev *pdev; + struct pci_bus *bus; int rc; bus = zbus->bus; @@ -203,11 +226,7 @@ static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) pci_bus_add_resource(bus, res, 0); } - pdev = pci_scan_single_device(bus, zdev->devfn); - if (pdev) - pci_bus_add_device(pdev); - - return 0; + return zpci_bus_scan_device(zdev); } static void zpci_bus_add_devices(struct zpci_bus *zbus) @@ -217,10 +236,6 @@ static void zpci_bus_add_devices(struct zpci_bus *zbus) for (i = 1; i < ZPCI_FUNCTIONS_PER_BUS; i++) if (zbus->function[i]) zpci_bus_add_device(zbus, zbus->function[i]); - - pci_lock_rescan_remove(); - pci_bus_add_devices(zbus->bus); - pci_unlock_rescan_remove(); } int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) diff --git a/arch/s390/pci/pci_bus.h b/arch/s390/pci/pci_bus.h index e04ca06a71b6..2649238f6cde 100644 --- a/arch/s390/pci/pci_bus.h +++ b/arch/s390/pci/pci_bus.h @@ -10,6 +10,7 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops); void zpci_bus_device_unregister(struct zpci_dev *zdev); +int zpci_bus_scan_device(struct zpci_dev *zdev); void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error); void zpci_release_device(struct kref *kref); -- cgit v1.2.3 From 7dc697d6b2b5299ab7e09c592d727671a3859be2 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 12 Feb 2021 12:17:53 +0100 Subject: s390/pci: do more bus setup in zpci_bus_scan() Pull setting the maximum bus speed and multifunction attribute into zpci_bus_scan() in preparation for handling bus creation separately from scanning the bus. Reviewed-by: Matthew Rosato Acked-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci_bus.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index 7b37c4316e35..f2577fb35be2 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -86,24 +86,33 @@ void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error) } } -/* zpci_bus_scan +/* zpci_bus_scan - Scan the PCI bus associated with this zbus * @zbus: the zbus holding the zdevices + * @f0: function 0 of the bus * @ops: the pci operations * - * The domain number must be set before pci_scan_root_bus is called. - * This function can be called once the domain is known, hence - * when the function_0 is dicovered. + * Function zero is taken as a parameter as this is used to determine the + * domain, multifunction property and maximum bus speed of the entire bus. + * + * Return: 0 on success, an error code otherwise */ -static int zpci_bus_scan(struct zpci_bus *zbus, int domain, struct pci_ops *ops) +static int zpci_bus_scan(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ops *ops) { struct pci_bus *bus; - int rc; + int domain; - rc = zpci_alloc_domain(domain); - if (rc < 0) - return rc; - zbus->domain_nr = rc; + domain = zpci_alloc_domain((u16)f0->uid); + if (domain < 0) + return domain; + zbus->domain_nr = domain; + zbus->multifunction = f0->rid_available; + zbus->max_bus_speed = f0->max_bus_speed; + + /* + * Note that the zbus->resources are taken over and zbus->resources + * is empty after a successful call + */ bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, ops, zbus, &zbus->resources); if (!bus) { zpci_free_domain(zbus->domain_nr); @@ -288,7 +297,7 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) WARN_ONCE(1, "rid_available not set on function 0 for multifunction\n"); goto error_bus; } - rc = zpci_bus_scan(zbus, (u16)zdev->uid, ops); + rc = zpci_bus_scan(zbus, zdev, ops); if (rc) goto error_bus; zpci_bus_add_devices(zbus); @@ -296,8 +305,6 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) if (rc) goto error_bus; zdev->has_hp_slot = 1; - zbus->multifunction = zdev->rid_available; - zbus->max_bus_speed = zdev->max_bus_speed; } else { zbus->multifunction = 1; } -- cgit v1.2.3 From a50297cf8235b062bcdeaa8b1dad58e69d3e1b43 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 12 Feb 2021 14:19:31 +0100 Subject: s390/pci: separate zbus creation from scanning In the existing code the creation of the PCI bus and the scanning of function zero all happens in zpci_scan_bus(). This in turn requires functions to be enabled and their resources to be available before the PCI bus is even created. This not only means that functions are enabled long before they are actually made available to the common PCI subsystem. In case of functions with non-zero devfn which appeared before the function with devfn zero they can wait arbitrarily long in this enabled but not scanned state. Fix this by separating the creation of the PCI bus from scanning it and only prepare, that is enable and setup MMIO bus resources, functions just before they are scanned. As they may be scanned multiple times track if we already created resources in the zdev. Reviewed-by: Matthew Rosato Acked-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pci.h | 3 +- arch/s390/pci/pci.c | 42 +++----- arch/s390/pci/pci_bus.c | 227 +++++++++++++++++++++++++++++++------------- arch/s390/pci/pci_bus.h | 2 + 4 files changed, 177 insertions(+), 97 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 35dec33c2801..d810ea4d358f 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -130,9 +130,10 @@ struct zpci_dev { u8 port; u8 rid_available : 1; u8 has_hp_slot : 1; + u8 has_resources : 1; u8 is_physfn : 1; u8 util_str_avail : 1; - u8 reserved : 4; + u8 reserved : 3; unsigned int devfn; /* DEVFN part of the RID*/ struct mutex lock; diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 0bce6078bfd6..023c3c2ab7f1 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -538,6 +538,7 @@ int zpci_setup_bus_resources(struct zpci_dev *zdev, zdev->bars[i].res = res; pci_add_resource(resources, res); } + zdev->has_resources = 1; return 0; } @@ -554,6 +555,7 @@ static void zpci_cleanup_bus_resources(struct zpci_dev *zdev) release_resource(zdev->bars[i].res); kfree(zdev->bars[i].res); } + zdev->has_resources = 0; } int pcibios_add_device(struct pci_dev *pdev) @@ -717,15 +719,9 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) if (rc) goto error; - if (zdev->state == ZPCI_FN_STATE_CONFIGURED) { - rc = zpci_enable_device(zdev); - if (rc) - goto error_destroy_iommu; - } - rc = zpci_bus_device_register(zdev, &pci_root_ops); if (rc) - goto error_disable; + goto error_destroy_iommu; spin_lock(&zpci_list_lock); list_add_tail(&zdev->entry, &zpci_list); @@ -733,9 +729,6 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) return 0; -error_disable: - if (zdev_enabled(zdev)) - zpci_disable_device(zdev); error_destroy_iommu: zpci_destroy_iommu(zdev); error: @@ -751,7 +744,8 @@ error: * * Configuring a device includes the configuration itself, if not done by the * platform, enabling, scanning and adding it to the common code PCI subsystem. - * If any failure occurs, the zpci_dev is left in Standby. + * If any failure occurs, the zpci_dev is left disabled either in Standby if + * the configuration failed or Configured if enabling or scanning failed. * * Return: 0 on success, or an error code otherwise */ @@ -768,29 +762,19 @@ int zpci_configure_device(struct zpci_dev *zdev, u32 fh) zdev->state = ZPCI_FN_STATE_CONFIGURED; } - rc = zpci_enable_device(zdev); - if (rc) - goto error; - /* the PCI function will be scanned once function 0 appears */ if (!zdev->zbus->bus) return 0; - rc = zpci_bus_scan_device(zdev); - if (rc) - goto error_disable; - - return 0; + /* For function 0 on a multi-function bus scan whole bus as we might + * have to pick up existing functions waiting for it to allow creating + * the PCI bus + */ + if (zdev->devfn == 0 && zdev->zbus->multifunction) + rc = zpci_bus_scan_bus(zdev->zbus); + else + rc = zpci_bus_scan_device(zdev); -error_disable: - zpci_disable_device(zdev); -error: - if (zdev->state == ZPCI_FN_STATE_CONFIGURED) { - rc = sclp_pci_deconfigure(zdev->fid); - zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, rc); - if (!rc) - zdev->state = ZPCI_FN_STATE_STANDBY; - } return rc; } diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index f2577fb35be2..d200e7559725 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -30,6 +30,38 @@ static LIST_HEAD(zbus_list); static DEFINE_SPINLOCK(zbus_list_lock); static int zpci_nb_devices; +/* zpci_bus_prepare_device - Prepare a zPCI function for scanning + * @zdev: the zPCI function to be prepared + * + * The PCI resources for the function are set up and added to its zbus and the + * function is enabled. The function must be added to a zbus which must have + * a PCI bus created. If an error occurs the zPCI function is not enabled. + * + * Return: 0 on success, an error code otherwise + */ +static int zpci_bus_prepare_device(struct zpci_dev *zdev) +{ + struct resource_entry *window, *n; + struct resource *res; + int rc; + + if (!zdev_enabled(zdev)) { + rc = zpci_enable_device(zdev); + if (rc) + return rc; + } + + if (!zdev->has_resources) { + zpci_setup_bus_resources(zdev, &zdev->zbus->resources); + resource_list_for_each_entry_safe(window, n, &zdev->zbus->resources) { + res = window->res; + pci_bus_add_resource(zdev->zbus->bus, res, 0); + } + } + + return 0; +} + /* zpci_bus_scan_device - Scan a single device adding it to the PCI core * @zdev: the zdev to be scanned * @@ -40,6 +72,11 @@ static int zpci_nb_devices; int zpci_bus_scan_device(struct zpci_dev *zdev) { struct pci_dev *pdev; + int rc; + + rc = zpci_bus_prepare_device(zdev); + if (rc) + return rc; pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn); if (!pdev) @@ -86,7 +123,44 @@ void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error) } } -/* zpci_bus_scan - Scan the PCI bus associated with this zbus +/* zpci_bus_scan_bus - Scan all configured zPCI functions on the bus + * @zbus: the zbus to be scanned + * + * Enables and scans all PCI functions on the bus making them available to the + * common PCI code. If there is no function 0 on the zbus nothing is scanned. If + * a function does not have a slot yet because it was added to the zbus before + * function 0 the slot is created. If a PCI function fails to be initialized + * an error will be returned but attempts will still be made for all other + * functions on the bus. + * + * Return: 0 on success, an error value otherwise + */ +int zpci_bus_scan_bus(struct zpci_bus *zbus) +{ + struct zpci_dev *zdev; + int devfn, rc, ret = 0; + + if (!zbus->function[0]) + return 0; + + for (devfn = 0; devfn < ZPCI_FUNCTIONS_PER_BUS; devfn++) { + zdev = zbus->function[devfn]; + if (zdev && zdev->state == ZPCI_FN_STATE_CONFIGURED) { + rc = zpci_bus_prepare_device(zdev); + if (rc) + ret = -EIO; + } + } + + pci_lock_rescan_remove(); + pci_scan_child_bus(zbus->bus); + pci_bus_add_devices(zbus->bus); + pci_unlock_rescan_remove(); + + return ret; +} + +/* zpci_bus_create_pci_bus - Create the PCI bus associated with this zbus * @zbus: the zbus holding the zdevices * @f0: function 0 of the bus * @ops: the pci operations @@ -96,7 +170,7 @@ void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error) * * Return: 0 on success, an error code otherwise */ -static int zpci_bus_scan(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ops *ops) +static int zpci_bus_create_pci_bus(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ops *ops) { struct pci_bus *bus; int domain; @@ -113,7 +187,7 @@ static int zpci_bus_scan(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ * Note that the zbus->resources are taken over and zbus->resources * is empty after a successful call */ - bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, ops, zbus, &zbus->resources); + bus = pci_create_root_bus(NULL, ZPCI_BUS_NR, ops, zbus, &zbus->resources); if (!bus) { zpci_free_domain(zbus->domain_nr); return -EFAULT; @@ -121,6 +195,7 @@ static int zpci_bus_scan(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ zbus->bus = bus; pci_bus_add_devices(bus); + return 0; } @@ -206,45 +281,77 @@ void pcibios_bus_add_device(struct pci_dev *pdev) } } -static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) +/* zpci_bus_create_hotplug_slots - Add hotplug slot(s) for device added to bus + * @zdev: the zPCI device that was newly added + * + * Add the hotplug slot(s) for the newly added PCI function. Normally this is + * simply the slot for the function itself. If however we are adding the + * function 0 on a zbus, it might be that we already registered functions on + * that zbus but could not create their hotplug slots yet so add those now too. + * + * Return: 0 on success, an error code otherwise + */ +static int zpci_bus_create_hotplug_slots(struct zpci_dev *zdev) { - struct resource_entry *window, *n; - struct resource *res; - struct pci_dev *pdev; - struct pci_bus *bus; - int rc; - - bus = zbus->bus; - if (!bus) - return -EINVAL; - - pdev = pci_get_slot(bus, zdev->devfn); - if (pdev) { - /* Device is already known. */ - pci_dev_put(pdev); - return 0; - } + struct zpci_bus *zbus = zdev->zbus; + int devfn, rc = 0; rc = zpci_init_slot(zdev); if (rc) return rc; zdev->has_hp_slot = 1; - resource_list_for_each_entry_safe(window, n, &zbus->resources) { - res = window->res; - pci_bus_add_resource(bus, res, 0); + if (zdev->devfn == 0 && zbus->multifunction) { + /* Now that function 0 is there we can finally create the + * hotplug slots for those functions with devfn != 0 that have + * been parked in zbus->function[] waiting for us to be able to + * create the PCI bus. + */ + for (devfn = 1; devfn < ZPCI_FUNCTIONS_PER_BUS; devfn++) { + zdev = zbus->function[devfn]; + if (zdev && !zdev->has_hp_slot) { + rc = zpci_init_slot(zdev); + if (rc) + return rc; + zdev->has_hp_slot = 1; + } + } + } - return zpci_bus_scan_device(zdev); + return rc; } -static void zpci_bus_add_devices(struct zpci_bus *zbus) +static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) { - int i; + int rc = -EINVAL; + + zdev->zbus = zbus; + if (zbus->function[zdev->devfn]) { + pr_err("devfn %04x is already assigned\n", zdev->devfn); + return rc; + } + zbus->function[zdev->devfn] = zdev; + zpci_nb_devices++; + + if (zbus->bus) { + if (zbus->multifunction && !zdev->rid_available) { + WARN_ONCE(1, "rid_available not set for multifunction\n"); + goto error; + } + + zpci_bus_create_hotplug_slots(zdev); + } else { + /* Hotplug slot will be created once function 0 appears */ + zbus->multifunction = 1; + } - for (i = 1; i < ZPCI_FUNCTIONS_PER_BUS; i++) - if (zbus->function[i]) - zpci_bus_add_device(zbus, zbus->function[i]); + return 0; + +error: + zbus->function[zdev->devfn] = NULL; + zpci_nb_devices--; + return rc; } int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) @@ -257,7 +364,6 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) zdev->fid, ZPCI_NR_DEVICES); return -ENOSPC; } - zpci_nb_devices++; if (zdev->devfn >= ZPCI_FUNCTIONS_PER_BUS) return -EINVAL; @@ -271,49 +377,36 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) return -ENOMEM; } - zdev->zbus = zbus; - if (zbus->function[zdev->devfn]) { - pr_err("devfn %04x is already assigned\n", zdev->devfn); - goto error; /* rc already set */ + if (zdev->devfn == 0) { + rc = zpci_bus_create_pci_bus(zbus, zdev, ops); + if (rc) + goto error; } - zbus->function[zdev->devfn] = zdev; - zpci_setup_bus_resources(zdev, &zbus->resources); + rc = zpci_bus_add_device(zbus, zdev); + if (rc) + goto error; - if (zbus->bus) { - if (!zbus->multifunction) { - WARN_ONCE(1, "zbus is not multifunction\n"); - goto error_bus; - } - if (!zdev->rid_available) { - WARN_ONCE(1, "rid_available not set for multifunction\n"); - goto error_bus; - } - rc = zpci_bus_add_device(zbus, zdev); - if (rc) - goto error_bus; - } else if (zdev->devfn == 0) { - if (zbus->multifunction && !zdev->rid_available) { - WARN_ONCE(1, "rid_available not set on function 0 for multifunction\n"); - goto error_bus; - } - rc = zpci_bus_scan(zbus, zdev, ops); - if (rc) - goto error_bus; - zpci_bus_add_devices(zbus); - rc = zpci_init_slot(zdev); - if (rc) - goto error_bus; - zdev->has_hp_slot = 1; - } else { - zbus->multifunction = 1; - } + if (zdev->state != ZPCI_FN_STATE_CONFIGURED) + return 0; + + /* the PCI function will be scanned once function 0 appears */ + if (!zdev->zbus->bus) + return 0; + + /* For function 0 scan whole bus as we might have to pick up existing + * functions waiting for it to allow creating the PCI bus + */ + if (zdev->devfn == 0 && zdev->zbus->multifunction) + rc = zpci_bus_scan_bus(zdev->zbus); + else + rc = zpci_bus_scan_device(zdev); + + if (rc) + goto error; return 0; -error_bus: - zpci_nb_devices--; - zbus->function[zdev->devfn] = NULL; error: pr_err("Adding PCI function %08x failed\n", zdev->fid); zpci_bus_put(zbus); diff --git a/arch/s390/pci/pci_bus.h b/arch/s390/pci/pci_bus.h index 2649238f6cde..981876ae3bd7 100644 --- a/arch/s390/pci/pci_bus.h +++ b/arch/s390/pci/pci_bus.h @@ -10,6 +10,8 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops); void zpci_bus_device_unregister(struct zpci_dev *zdev); +int zpci_bus_scan_bus(struct zpci_bus *zbus); + int zpci_bus_scan_device(struct zpci_dev *zdev); void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error); -- cgit v1.2.3 From 0350276168942a9fb7540c03995229e3502976a2 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 12 Feb 2021 10:16:46 +0100 Subject: s390/pci: use mutex not spinlock for zbus list In a later change we will first collect all PCI functions from the CLP List PCI functions call, then register them to/creating the relevant zbus. Then only after we've created our virtual bus structure will we scan all zbusses iterating over the zbus list. Since scanning is relatively slow a spinlock is a bad fit for protecting the loop over the devices on the zbus. Furthermore doing the probing on the bus we need to use pci_lock_rescan_remove() as devices are added to the PCI subsystem and that is a mutex which can't be locked nested inside a spinlock section. Note that the contention of this lock should be very low either way as zbusses are only added/removed concurrently on hotplug events. Reviewed-by: Matthew Rosato Reviewed-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci_bus.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index d200e7559725..9bc869afe011 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -27,7 +27,7 @@ #include "pci_iov.h" static LIST_HEAD(zbus_list); -static DEFINE_SPINLOCK(zbus_list_lock); +static DEFINE_MUTEX(zbus_list_lock); static int zpci_nb_devices; /* zpci_bus_prepare_device - Prepare a zPCI function for scanning @@ -214,9 +214,9 @@ static void zpci_bus_release(struct kref *kref) pci_unlock_rescan_remove(); } - spin_lock(&zbus_list_lock); + mutex_lock(&zbus_list_lock); list_del(&zbus->bus_next); - spin_unlock(&zbus_list_lock); + mutex_unlock(&zbus_list_lock); kfree(zbus); } @@ -229,7 +229,7 @@ static struct zpci_bus *zpci_bus_get(int pchid) { struct zpci_bus *zbus; - spin_lock(&zbus_list_lock); + mutex_lock(&zbus_list_lock); list_for_each_entry(zbus, &zbus_list, bus_next) { if (pchid == zbus->pchid) { kref_get(&zbus->kref); @@ -238,7 +238,7 @@ static struct zpci_bus *zpci_bus_get(int pchid) } zbus = NULL; out_unlock: - spin_unlock(&zbus_list_lock); + mutex_unlock(&zbus_list_lock); return zbus; } @@ -252,9 +252,9 @@ static struct zpci_bus *zpci_bus_alloc(int pchid) zbus->pchid = pchid; INIT_LIST_HEAD(&zbus->bus_next); - spin_lock(&zbus_list_lock); + mutex_lock(&zbus_list_lock); list_add_tail(&zbus->bus_next, &zbus_list); - spin_unlock(&zbus_list_lock); + mutex_unlock(&zbus_list_lock); kref_init(&zbus->kref); INIT_LIST_HEAD(&zbus->resources); -- cgit v1.2.3 From 14c87ba8123abe6b707d04e1711eef90653567f2 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 12 Feb 2021 11:57:58 +0100 Subject: s390/pci: separate zbus registration from scanning Now that the zbus can be created without being scanned we can go one step further and make registering a device to a zbus independent from scanning it. This way the zbus handling becomes much more natural in that functions can be registered on the zbus to be scanned later more closely resembling the handling of both real PCI hardware and other virtual PCI busses like Hyper-V's virtual PCI bus (see for example drivers/pci/controller/pci-hyperv.c:create_root_hv_pci_bus()). Having zbus registration separate from scanning allows us to return fully initialized but still disabled zdevs from zpci_create_device() which can then be configured just as we would configure a zdev from standby (minus the SCLP Configure already done by the platform). There is still the exception that a PCI function with non-zero devfn can be plugged before its PCI bus, which depends on the function with zero devfn, is created. In this case the zdev returend from zpci_create_device() is still missing its bus, hotplug slot, and resources which need to be created later but at least it doesn't wait in the enabled state and can otherwise be treated as initialized. With this we also separate the initial PCI scan using CLP List PCI Functions into two phases. In the CLP loop's callback we only register each function with a virtual zbus creating the latter as needed. Then, after we have built this virtual PCI topology based on our list of zbusses, we can make use of the common code functionality to scan each complete zbus as a separate child bus. Reviewed-by: Matthew Rosato Acked-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pci.h | 2 +- arch/s390/pci/pci.c | 11 ++++++----- arch/s390/pci/pci_bus.c | 35 +++++++++++++++++------------------ arch/s390/pci/pci_bus.h | 1 + arch/s390/pci/pci_event.c | 14 ++++++++------ 5 files changed, 33 insertions(+), 30 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index d810ea4d358f..35c2af9371a9 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -201,7 +201,7 @@ extern unsigned int s390_pci_no_rid; Prototypes ----------------------------------------------------------------------------- */ /* Base stuff */ -int zpci_create_device(u32 fid, u32 fh, enum zpci_state state); +struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state); int zpci_enable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *); int zpci_configure_device(struct zpci_dev *zdev, u32 fh); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 023c3c2ab7f1..d6c6b5119a14 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -690,9 +690,9 @@ int zpci_disable_device(struct zpci_dev *zdev) * Creates a new zpci device and adds it to its, possibly newly created, zbus * as well as zpci_list. * - * Returns: 0 on success, an error value otherwise + * Returns: the zdev on success or an error pointer otherwise */ -int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) +struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state) { struct zpci_dev *zdev; int rc; @@ -700,7 +700,7 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, state); zdev = kzalloc(sizeof(*zdev), GFP_KERNEL); if (!zdev) - return -ENOMEM; + return ERR_PTR(-ENOMEM); /* FID and Function Handle are the static/dynamic identifiers */ zdev->fid = fid; @@ -727,14 +727,14 @@ int zpci_create_device(u32 fid, u32 fh, enum zpci_state state) list_add_tail(&zdev->entry, &zpci_list); spin_unlock(&zpci_list_lock); - return 0; + return zdev; error_destroy_iommu: zpci_destroy_iommu(zdev); error: zpci_dbg(0, "add fid:%x, rc:%d\n", fid, rc); kfree(zdev); - return rc; + return ERR_PTR(rc); } /** @@ -959,6 +959,7 @@ static int __init pci_base_init(void) rc = clp_scan_pci_devices(); if (rc) goto out_find; + zpci_bus_scan_busses(); s390_pci_initialized = 1; return 0; diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c index 9bc869afe011..9629f9779c79 100644 --- a/arch/s390/pci/pci_bus.c +++ b/arch/s390/pci/pci_bus.c @@ -160,6 +160,23 @@ int zpci_bus_scan_bus(struct zpci_bus *zbus) return ret; } +/* zpci_bus_scan_busses - Scan all registered busses + * + * Scan all available zbusses + * + */ +void zpci_bus_scan_busses(void) +{ + struct zpci_bus *zbus = NULL; + + mutex_lock(&zbus_list_lock); + list_for_each_entry(zbus, &zbus_list, bus_next) { + zpci_bus_scan_bus(zbus); + cond_resched(); + } + mutex_unlock(&zbus_list_lock); +} + /* zpci_bus_create_pci_bus - Create the PCI bus associated with this zbus * @zbus: the zbus holding the zdevices * @f0: function 0 of the bus @@ -387,24 +404,6 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) if (rc) goto error; - if (zdev->state != ZPCI_FN_STATE_CONFIGURED) - return 0; - - /* the PCI function will be scanned once function 0 appears */ - if (!zdev->zbus->bus) - return 0; - - /* For function 0 scan whole bus as we might have to pick up existing - * functions waiting for it to allow creating the PCI bus - */ - if (zdev->devfn == 0 && zdev->zbus->multifunction) - rc = zpci_bus_scan_bus(zdev->zbus); - else - rc = zpci_bus_scan_device(zdev); - - if (rc) - goto error; - return 0; error: diff --git a/arch/s390/pci/pci_bus.h b/arch/s390/pci/pci_bus.h index 981876ae3bd7..b877a97e6745 100644 --- a/arch/s390/pci/pci_bus.h +++ b/arch/s390/pci/pci_bus.h @@ -11,6 +11,7 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops); void zpci_bus_device_unregister(struct zpci_dev *zdev); int zpci_bus_scan_bus(struct zpci_bus *zbus); +void zpci_bus_scan_busses(void); int zpci_bus_scan_device(struct zpci_dev *zdev); void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error); diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index ae3054d85491..1178b48a66df 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -104,13 +104,15 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf) switch (ccdf->pec) { case 0x0301: /* Reserved|Standby -> Configured */ if (!zdev) { - zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED); - break; + zdev = zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED); + if (IS_ERR(zdev)) + break; + } else { + /* the configuration request may be stale */ + if (zdev->state != ZPCI_FN_STATE_STANDBY) + break; + zdev->state = ZPCI_FN_STATE_CONFIGURED; } - /* the configuration request may be stale */ - if (zdev->state != ZPCI_FN_STATE_STANDBY) - break; - zdev->state = ZPCI_FN_STATE_CONFIGURED; zpci_configure_device(zdev, ccdf->fh); break; case 0x0302: /* Reserved -> Standby */ -- cgit v1.2.3 From 61311e32892b008886478bdba4ce2a34f4d938f8 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 26 Mar 2021 13:58:48 +0100 Subject: s390/pci: narrow scope of zpci_configure_device() Currently zpci_configure_device() can be called on a zPCI function in two completely different states. Either the underlying zPCI function has already been configured by the platform and we are only doing the scanning to get it usable by Linux drivers. Or the underlying function is in Standby and we first do an SCLP to get it configured. This makes zpci_configure_device() harder to reason about. Since calling zpci_configure_device() on a function in Standby only happens in enable_slot() simply pull out the SCLP call and setting of zdev->state and thus call zpci_configure_device() under the same circumstances as in the event handling code. Reviewed-by: Matthew Rosato Reviewed-by: Pierre Morel Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/pci/pci.c | 15 +++------------ drivers/pci/hotplug/s390_pci_hpc.c | 7 +++++++ 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index d6c6b5119a14..c01b6dbac7cf 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -742,10 +742,9 @@ error: * @zdev: The zpci_dev to be configured * @fh: The general function handle supplied by the platform * - * Configuring a device includes the configuration itself, if not done by the - * platform, enabling, scanning and adding it to the common code PCI subsystem. - * If any failure occurs, the zpci_dev is left disabled either in Standby if - * the configuration failed or Configured if enabling or scanning failed. + * Given a device in the configuration state Configured, enables, scans and + * adds it to the common code PCI subsystem. If any failure occurs, the + * zpci_dev is left disabled. * * Return: 0 on success, or an error code otherwise */ @@ -754,14 +753,6 @@ int zpci_configure_device(struct zpci_dev *zdev, u32 fh) int rc; zdev->fh = fh; - if (zdev->state != ZPCI_FN_STATE_CONFIGURED) { - rc = sclp_pci_configure(zdev->fid); - zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc); - if (rc) - return rc; - zdev->state = ZPCI_FN_STATE_CONFIGURED; - } - /* the PCI function will be scanned once function 0 appears */ if (!zdev->zbus->bus) return 0; diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 154532663a70..f8f056be71b7 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -24,10 +24,17 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) { struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, hotplug_slot); + int rc; if (zdev->state != ZPCI_FN_STATE_STANDBY) return -EIO; + rc = sclp_pci_configure(zdev->fid); + zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc); + if (rc) + return rc; + zdev->state = ZPCI_FN_STATE_CONFIGURED; + return zpci_configure_device(zdev, zdev->fh); } -- cgit v1.2.3 From 17a363dcd2f7455d8661a7b2f9ba7cfb85bbc7e4 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 9 Apr 2021 10:34:43 +0200 Subject: s390/traps,mm: add conditional trap handlers Add conditional trap handlers similar to conditional system calls (COND_SYSCALL), to reduce the number of ifdefs. Trap handlers which may or may not exist depending on config options are supposed to have a COND_TRAP entry, which redirects to default_trap_handler() for non-existent trap handlers during link time. This allows to get rid of the secure execution trap handlers for the !PGSTE case. Reviewed-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/kernel/entry.h | 1 - arch/s390/kernel/traps.c | 11 ++++++++++- arch/s390/mm/fault.c | 18 ++---------------- 3 files changed, 12 insertions(+), 18 deletions(-) (limited to 'arch') diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index c7969d67f317..09abb11bc660 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -26,7 +26,6 @@ void do_dat_exception(struct pt_regs *regs); void do_secure_storage_access(struct pt_regs *regs); void do_non_secure_storage_access(struct pt_regs *regs); void do_secure_storage_violation(struct pt_regs *regs); -void default_trap_handler(struct pt_regs *regs); void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); void kernel_stack_overflow(struct pt_regs * regs); void do_signal(struct pt_regs *regs); diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index e8b894184f83..63021d484626 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -79,7 +79,7 @@ void do_per_trap(struct pt_regs *regs) } NOKPROBE_SYMBOL(do_per_trap); -void default_trap_handler(struct pt_regs *regs) +static void default_trap_handler(struct pt_regs *regs) { if (user_mode(regs)) { report_user_fault(regs, SIGSEGV, 0); @@ -404,3 +404,12 @@ static void (*pgm_check_table[128])(struct pt_regs *regs) = { [0x40] = monitor_event_exception, [0x41 ... 0x7f] = default_trap_handler, }; + +#define COND_TRAP(x) asm( \ + ".weak " __stringify(x) "\n\t" \ + ".set " __stringify(x) "," \ + __stringify(default_trap_handler)) + +COND_TRAP(do_secure_storage_access); +COND_TRAP(do_non_secure_storage_access); +COND_TRAP(do_secure_storage_violation); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index e30c7c781172..826d01777361 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -783,6 +783,7 @@ early_initcall(pfault_irq_init); #endif /* CONFIG_PFAULT */ #if IS_ENABLED(CONFIG_PGSTE) + void do_secure_storage_access(struct pt_regs *regs) { unsigned long addr = regs->int_parm_long & __FAIL_ADDR_MASK; @@ -859,19 +860,4 @@ void do_secure_storage_violation(struct pt_regs *regs) send_sig(SIGSEGV, current, 0); } -#else -void do_secure_storage_access(struct pt_regs *regs) -{ - default_trap_handler(regs); -} - -void do_non_secure_storage_access(struct pt_regs *regs) -{ - default_trap_handler(regs); -} - -void do_secure_storage_violation(struct pt_regs *regs) -{ - default_trap_handler(regs); -} -#endif +#endif /* CONFIG_PGSTE */ -- cgit v1.2.3 From 6000b5f4032e9be5413dcfcdd9e39eb1c9cc2453 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Apr 2021 20:12:16 +0200 Subject: s390/bitops: make bitops only work on longs The bitops code was optimized to generate test under mask instructions with the __bitops_byte() helper. However that was many years ago and in the meantime a lot of new instructions were introduced. Changing the code so that it always operates on longs nowadays even generates shorter code (~ -20kb, defconfig, gcc 10, march=zE12). Signed-off-by: Heiko Carstens --- arch/s390/include/asm/bitops.h | 93 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 46 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/bitops.h b/arch/s390/include/asm/bitops.h index 31121d32f81d..68da67d2c4c9 100644 --- a/arch/s390/include/asm/bitops.h +++ b/arch/s390/include/asm/bitops.h @@ -42,7 +42,7 @@ #define __BITOPS_WORDS(bits) (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG) static inline unsigned long * -__bitops_word(unsigned long nr, volatile unsigned long *ptr) +__bitops_word(unsigned long nr, const volatile unsigned long *ptr) { unsigned long addr; @@ -50,37 +50,33 @@ __bitops_word(unsigned long nr, volatile unsigned long *ptr) return (unsigned long *)addr; } -static inline unsigned char * -__bitops_byte(unsigned long nr, volatile unsigned long *ptr) +static inline unsigned long __bitops_mask(unsigned long nr) { - return ((unsigned char *)ptr) + ((nr ^ (BITS_PER_LONG - 8)) >> 3); + return 1UL << (nr & (BITS_PER_LONG - 1)); } static __always_inline void arch_set_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long mask; + unsigned long mask = __bitops_mask(nr); - mask = 1UL << (nr & (BITS_PER_LONG - 1)); __atomic64_or(mask, (long *)addr); } static __always_inline void arch_clear_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long mask; + unsigned long mask = __bitops_mask(nr); - mask = ~(1UL << (nr & (BITS_PER_LONG - 1))); - __atomic64_and(mask, (long *)addr); + __atomic64_and(~mask, (long *)addr); } static __always_inline void arch_change_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long mask; + unsigned long mask = __bitops_mask(nr); - mask = 1UL << (nr & (BITS_PER_LONG - 1)); __atomic64_xor(mask, (long *)addr); } @@ -88,99 +84,104 @@ static inline bool arch_test_and_set_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long old, mask; + unsigned long mask = __bitops_mask(nr); + unsigned long old; - mask = 1UL << (nr & (BITS_PER_LONG - 1)); old = __atomic64_or_barrier(mask, (long *)addr); - return (old & mask) != 0; + return old & mask; } static inline bool arch_test_and_clear_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long old, mask; + unsigned long mask = __bitops_mask(nr); + unsigned long old; - mask = ~(1UL << (nr & (BITS_PER_LONG - 1))); - old = __atomic64_and_barrier(mask, (long *)addr); - return (old & ~mask) != 0; + old = __atomic64_and_barrier(~mask, (long *)addr); + return old & mask; } static inline bool arch_test_and_change_bit(unsigned long nr, volatile unsigned long *ptr) { unsigned long *addr = __bitops_word(nr, ptr); - unsigned long old, mask; + unsigned long mask = __bitops_mask(nr); + unsigned long old; - mask = 1UL << (nr & (BITS_PER_LONG - 1)); old = __atomic64_xor_barrier(mask, (long *)addr); - return (old & mask) != 0; + return old & mask; } static inline void arch___set_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); - *addr |= 1 << (nr & 7); + *addr |= mask; } static inline void arch___clear_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); - *addr &= ~(1 << (nr & 7)); + *addr &= ~mask; } static inline void arch___change_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); - *addr ^= 1 << (nr & 7); + *addr ^= mask; } static inline bool arch___test_and_set_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); - unsigned char ch; + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); + unsigned long old; - ch = *addr; - *addr |= 1 << (nr & 7); - return (ch >> (nr & 7)) & 1; + old = *addr; + *addr |= mask; + return old & mask; } static inline bool arch___test_and_clear_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); - unsigned char ch; + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); + unsigned long old; - ch = *addr; - *addr &= ~(1 << (nr & 7)); - return (ch >> (nr & 7)) & 1; + old = *addr; + *addr &= ~mask; + return old & mask; } static inline bool arch___test_and_change_bit(unsigned long nr, volatile unsigned long *ptr) { - unsigned char *addr = __bitops_byte(nr, ptr); - unsigned char ch; + unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); + unsigned long old; - ch = *addr; - *addr ^= 1 << (nr & 7); - return (ch >> (nr & 7)) & 1; + old = *addr; + *addr ^= mask; + return old & mask; } static inline bool arch_test_bit(unsigned long nr, const volatile unsigned long *ptr) { - const volatile unsigned char *addr; + const volatile unsigned long *addr = __bitops_word(nr, ptr); + unsigned long mask = __bitops_mask(nr); - addr = ((const volatile unsigned char *)ptr); - addr += (nr ^ (BITS_PER_LONG - 8)) >> 3; - return (*addr >> (nr & 7)) & 1; + return *addr & mask; } static inline bool arch_test_and_set_bit_lock(unsigned long nr, -- cgit v1.2.3 From c8a91c285d8c3449b32021b28bcb7fb5662403a8 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Mar 2021 14:02:55 +0100 Subject: s390/atomic: move remaining inline assemblies to atomic_ops.h Move all remaining inline assemblies from atomic.h to atomic_ops.h. That way all atomic inline assemblies are contained within only a single header file. Signed-off-by: Heiko Carstens --- arch/s390/include/asm/atomic.h | 22 ++++------------------ arch/s390/include/asm/atomic_ops.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h index 5860ae790f2d..5d8bd0de6b2a 100644 --- a/arch/s390/include/asm/atomic.h +++ b/arch/s390/include/asm/atomic.h @@ -17,19 +17,12 @@ static inline int atomic_read(const atomic_t *v) { - int c; - - asm volatile( - " l %0,%1\n" - : "=d" (c) : "Q" (v->counter)); - return c; + return __atomic_read(v); } static inline void atomic_set(atomic_t *v, int i) { - asm volatile( - " st %1,%0\n" - : "=Q" (v->counter) : "d" (i)); + __atomic_set(v, i); } static inline int atomic_add_return(int i, atomic_t *v) @@ -78,19 +71,12 @@ static inline int atomic_cmpxchg(atomic_t *v, int old, int new) static inline s64 atomic64_read(const atomic64_t *v) { - s64 c; - - asm volatile( - " lg %0,%1\n" - : "=d" (c) : "Q" (v->counter)); - return c; + return __atomic64_read(v); } static inline void atomic64_set(atomic64_t *v, s64 i) { - asm volatile( - " stg %1,%0\n" - : "=Q" (v->counter) : "d" (i)); + __atomic64_set(v, i); } static inline s64 atomic64_add_return(s64 i, atomic64_t *v) diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h index 61467b9eecc7..82ed50885558 100644 --- a/arch/s390/include/asm/atomic_ops.h +++ b/arch/s390/include/asm/atomic_ops.h @@ -8,6 +8,40 @@ #ifndef __ARCH_S390_ATOMIC_OPS__ #define __ARCH_S390_ATOMIC_OPS__ +static inline int __atomic_read(const atomic_t *v) +{ + int c; + + asm volatile( + " l %0,%1\n" + : "=d" (c) : "Q" (v->counter)); + return c; +} + +static inline void __atomic_set(atomic_t *v, int i) +{ + asm volatile( + " st %1,%0\n" + : "=Q" (v->counter) : "d" (i)); +} + +static inline s64 __atomic64_read(const atomic64_t *v) +{ + s64 c; + + asm volatile( + " lg %0,%1\n" + : "=d" (c) : "Q" (v->counter)); + return c; +} + +static inline void __atomic64_set(atomic64_t *v, s64 i) +{ + asm volatile( + " stg %1,%0\n" + : "=Q" (v->counter) : "d" (i)); +} + #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES #define __ATOMIC_OP(op_name, op_type, op_string, op_barrier) \ -- cgit v1.2.3 From ca897bb1814fc77ce2ded7b31350ff2b25ccb0a4 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Mar 2021 14:08:39 +0100 Subject: s390/atomic: use proper constraints Use the R,T, and S constraints instead of the Q constraint in atomic inline assemblies wherever possible. This allows the compiler to generate better code. (~ -2kb code size). Signed-off-by: Heiko Carstens --- arch/s390/include/asm/atomic_ops.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h index 82ed50885558..92ea9d9a2b93 100644 --- a/arch/s390/include/asm/atomic_ops.h +++ b/arch/s390/include/asm/atomic_ops.h @@ -14,7 +14,7 @@ static inline int __atomic_read(const atomic_t *v) asm volatile( " l %0,%1\n" - : "=d" (c) : "Q" (v->counter)); + : "=d" (c) : "R" (v->counter)); return c; } @@ -22,7 +22,7 @@ static inline void __atomic_set(atomic_t *v, int i) { asm volatile( " st %1,%0\n" - : "=Q" (v->counter) : "d" (i)); + : "=R" (v->counter) : "d" (i)); } static inline s64 __atomic64_read(const atomic64_t *v) @@ -31,7 +31,7 @@ static inline s64 __atomic64_read(const atomic64_t *v) asm volatile( " lg %0,%1\n" - : "=d" (c) : "Q" (v->counter)); + : "=d" (c) : "T" (v->counter)); return c; } @@ -39,7 +39,7 @@ static inline void __atomic64_set(atomic64_t *v, s64 i) { asm volatile( " stg %1,%0\n" - : "=Q" (v->counter) : "d" (i)); + : "=T" (v->counter) : "d" (i)); } #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES @@ -52,7 +52,7 @@ static inline op_type op_name(op_type val, op_type *ptr) \ asm volatile( \ op_string " %[old],%[val],%[ptr]\n" \ op_barrier \ - : [old] "=d" (old), [ptr] "+Q" (*ptr) \ + : [old] "=d" (old), [ptr] "+S" (*ptr) \ : [val] "d" (val) : "cc", "memory"); \ return old; \ } \ @@ -80,7 +80,7 @@ static __always_inline void op_name(op_type val, op_type *ptr) \ asm volatile( \ op_string " %[ptr],%[val]\n" \ op_barrier \ - : [ptr] "+Q" (*ptr) : [val] "i" (val) : "cc", "memory");\ + : [ptr] "+S" (*ptr) : [val] "i" (val) : "cc", "memory");\ } #define __ATOMIC_CONST_OPS(op_name, op_type, op_string) \ @@ -131,7 +131,7 @@ static inline long op_name(long val, long *ptr) \ op_string " %[new],%[val]\n" \ " csg %[old],%[new],%[ptr]\n" \ " jl 0b" \ - : [old] "=d" (old), [new] "=&d" (new), [ptr] "+Q" (*ptr)\ + : [old] "=d" (old), [new] "=&d" (new), [ptr] "+S" (*ptr)\ : [val] "d" (val), "0" (*ptr) : "cc", "memory"); \ return old; \ } -- cgit v1.2.3 From b23eb636d7f9f3d7c3ae0dd443cf26c4cc1e18f7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Mar 2021 16:36:27 +0100 Subject: s390/atomic: get rid of gcc atomic builtins s390 is the only architecture in the kernel which makes use of gcc's atomic builtin functions. Even though I don't see any technical problem with that right now, remove this code and open-code compare-and-swap loops again, like every other architecture is doing it also. We can switch to a generic implementation when other architectures are doing that also. See also https://lwn.net/Articles/586838/ for forther details. Signed-off-by: Heiko Carstens --- arch/s390/include/asm/atomic_ops.h | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h index 92ea9d9a2b93..2e818f2709bf 100644 --- a/arch/s390/include/asm/atomic_ops.h +++ b/arch/s390/include/asm/atomic_ops.h @@ -156,22 +156,46 @@ __ATOMIC64_OPS(__atomic64_xor, "xgr") static inline int __atomic_cmpxchg(int *ptr, int old, int new) { - return __sync_val_compare_and_swap(ptr, old, new); + asm volatile( + " cs %[old],%[new],%[ptr]" + : [old] "+d" (old), [ptr] "+Q" (*ptr) + : [new] "d" (new) + : "cc", "memory"); + return old; } -static inline int __atomic_cmpxchg_bool(int *ptr, int old, int new) +static inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new) { - return __sync_bool_compare_and_swap(ptr, old, new); + int old_expected = old; + + asm volatile( + " cs %[old],%[new],%[ptr]" + : [old] "+d" (old), [ptr] "+Q" (*ptr) + : [new] "d" (new) + : "cc", "memory"); + return old == old_expected; } static inline long __atomic64_cmpxchg(long *ptr, long old, long new) { - return __sync_val_compare_and_swap(ptr, old, new); + asm volatile( + " csg %[old],%[new],%[ptr]" + : [old] "+d" (old), [ptr] "+S" (*ptr) + : [new] "d" (new) + : "cc", "memory"); + return old; } -static inline long __atomic64_cmpxchg_bool(long *ptr, long old, long new) +static inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new) { - return __sync_bool_compare_and_swap(ptr, old, new); + long old_expected = old; + + asm volatile( + " csg %[old],%[new],%[ptr]" + : [old] "+d" (old), [ptr] "+S" (*ptr) + : [new] "d" (new) + : "cc", "memory"); + return old == old_expected; } #endif /* __ARCH_S390_ATOMIC_OPS__ */ -- cgit v1.2.3 From d2b1f6d2d35043d2c9d079c1595f10c93bfca7d2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Apr 2021 14:51:48 +0200 Subject: s390/cmpxchg: get rid of gcc atomic builtins s390 is the only architecture in the kernel which makes use of gcc's atomic builtin functions. Even though I don't see any technical problem with that right now, remove this code and open-code compare-and-swap loops again, like every other architecture is doing it also. We can switch to a generic implementation when other architectures are doing that also. See also https://lwn.net/Articles/586838/ for forther details. This basically reverts commit f318a1229bd8 ("s390/cmpxchg: use compiler builtins"). Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 165 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index af99c1f66f12..263d3f87edc8 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -12,26 +12,163 @@ #include #include +void __xchg_called_with_bad_pointer(void); + +static inline unsigned long __xchg(unsigned long x, void *ptr, int size) +{ + unsigned long addr, old; + int shift; + + switch (size) { + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,%1\n" + "0: lr 0,%0\n" + " nr 0,%3\n" + " or 0,%2\n" + " cs %0,0,%1\n" + " jl 0b\n" + : "=&d" (old), "+Q" (*(int *) addr) + : "d" ((x & 0xff) << shift), "d" (~(0xff << shift)) + : "memory", "cc", "0"); + return old >> shift; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,%1\n" + "0: lr 0,%0\n" + " nr 0,%3\n" + " or 0,%2\n" + " cs %0,0,%1\n" + " jl 0b\n" + : "=&d" (old), "+Q" (*(int *) addr) + : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)) + : "memory", "cc", "0"); + return old >> shift; + case 4: + asm volatile( + " l %0,%1\n" + "0: cs %0,%2,%1\n" + " jl 0b\n" + : "=&d" (old), "+Q" (*(int *) ptr) + : "d" (x) + : "memory", "cc"); + return old; + case 8: + asm volatile( + " lg %0,%1\n" + "0: csg %0,%2,%1\n" + " jl 0b\n" + : "=&d" (old), "+S" (*(long *) ptr) + : "d" (x) + : "memory", "cc"); + return old; + } + __xchg_called_with_bad_pointer(); + return x; +} + +#define xchg(ptr, x) \ +({ \ + __typeof__(*(ptr)) __ret; \ + \ + __ret = (__typeof__(*(ptr))) \ + __xchg((unsigned long)(x), (void *)(ptr), sizeof(*(ptr))); \ + __ret; \ +}) + +void __cmpxchg_called_with_bad_pointer(void); + +static inline unsigned long __cmpxchg(void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long addr, prev, tmp; + int shift; + + switch (size) { + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,%2\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%3\n" + " or %1,%4\n" + " cs %0,%1,%2\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr) + : "d" ((old & 0xff) << shift), + "d" ((new & 0xff) << shift), + "d" (~(0xff << shift)) + : "memory", "cc"); + return prev >> shift; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,%2\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%3\n" + " or %1,%4\n" + " cs %0,%1,%2\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr) + : "d" ((old & 0xffff) << shift), + "d" ((new & 0xffff) << shift), + "d" (~(0xffff << shift)) + : "memory", "cc"); + return prev >> shift; + case 4: + asm volatile( + " cs %0,%3,%1\n" + : "=&d" (prev), "+Q" (*(int *) ptr) + : "0" (old), "d" (new) + : "memory", "cc"); + return prev; + case 8: + asm volatile( + " csg %0,%3,%1\n" + : "=&d" (prev), "+S" (*(long *) ptr) + : "0" (old), "d" (new) + : "memory", "cc"); + return prev; + } + __cmpxchg_called_with_bad_pointer(); + return old; +} + #define cmpxchg(ptr, o, n) \ ({ \ - __typeof__(*(ptr)) __o = (o); \ - __typeof__(*(ptr)) __n = (n); \ - (__typeof__(*(ptr))) __sync_val_compare_and_swap((ptr),__o,__n);\ + __typeof__(*(ptr)) __ret; \ + \ + __ret = (__typeof__(*(ptr))) \ + __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + __ret; \ }) #define cmpxchg64 cmpxchg #define cmpxchg_local cmpxchg -#define cmpxchg64_local cmpxchg +#define cmpxchg64_local cmpxchg -#define xchg(ptr, x) \ -({ \ - __typeof__(ptr) __ptr = (ptr); \ - __typeof__(*(ptr)) __old; \ - do { \ - __old = *__ptr; \ - } while (!__sync_bool_compare_and_swap(__ptr, __old, x)); \ - __old; \ -}) +#define system_has_cmpxchg_double() 1 #define __cmpxchg_double(p1, p2, o1, o2, n1, n2) \ ({ \ @@ -61,6 +198,4 @@ __cmpxchg_double(__p1, __p2, o1, o2, n1, n2); \ }) -#define system_has_cmpxchg_double() 1 - #endif /* __ASM_CMPXCHG_H */ -- cgit v1.2.3 From 000174233b91340ca52a9eca905d029a9a2aefd9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Apr 2021 21:33:47 +0200 Subject: s390/atomic,cmpxchg: switch to use atomic-instrumented.h Add arch_ prefix to all atomic operations, and define ARCH_ATOMIC. This enables KASAN instrumentation for all atomic operations on s390. This is the s390 variant of commit 8bf705d13039 ("locking/atomic/x86: Switch atomic.h to use atomic-instrumented.h"). Signed-off-by: Heiko Carstens --- arch/s390/include/asm/atomic.h | 76 ++++++++++++++++++++++++++++------------- arch/s390/include/asm/cmpxchg.h | 12 +++---- 2 files changed, 58 insertions(+), 30 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h index 5d8bd0de6b2a..7c93c6573524 100644 --- a/arch/s390/include/asm/atomic.h +++ b/arch/s390/include/asm/atomic.h @@ -15,41 +15,46 @@ #include #include -static inline int atomic_read(const atomic_t *v) +static inline int arch_atomic_read(const atomic_t *v) { return __atomic_read(v); } +#define arch_atomic_read arch_atomic_read -static inline void atomic_set(atomic_t *v, int i) +static inline void arch_atomic_set(atomic_t *v, int i) { __atomic_set(v, i); } +#define arch_atomic_set arch_atomic_set -static inline int atomic_add_return(int i, atomic_t *v) +static inline int arch_atomic_add_return(int i, atomic_t *v) { return __atomic_add_barrier(i, &v->counter) + i; } +#define arch_atomic_add_return arch_atomic_add_return -static inline int atomic_fetch_add(int i, atomic_t *v) +static inline int arch_atomic_fetch_add(int i, atomic_t *v) { return __atomic_add_barrier(i, &v->counter); } +#define arch_atomic_fetch_add arch_atomic_fetch_add -static inline void atomic_add(int i, atomic_t *v) +static inline void arch_atomic_add(int i, atomic_t *v) { __atomic_add(i, &v->counter); } +#define arch_atomic_add arch_atomic_add -#define atomic_sub(_i, _v) atomic_add(-(int)(_i), _v) -#define atomic_sub_return(_i, _v) atomic_add_return(-(int)(_i), _v) -#define atomic_fetch_sub(_i, _v) atomic_fetch_add(-(int)(_i), _v) +#define arch_atomic_sub(_i, _v) arch_atomic_add(-(int)(_i), _v) +#define arch_atomic_sub_return(_i, _v) arch_atomic_add_return(-(int)(_i), _v) +#define arch_atomic_fetch_sub(_i, _v) arch_atomic_fetch_add(-(int)(_i), _v) #define ATOMIC_OPS(op) \ -static inline void atomic_##op(int i, atomic_t *v) \ +static inline void arch_atomic_##op(int i, atomic_t *v) \ { \ __atomic_##op(i, &v->counter); \ } \ -static inline int atomic_fetch_##op(int i, atomic_t *v) \ +static inline int arch_atomic_fetch_##op(int i, atomic_t *v) \ { \ return __atomic_##op##_barrier(i, &v->counter); \ } @@ -60,53 +65,67 @@ ATOMIC_OPS(xor) #undef ATOMIC_OPS -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) +#define arch_atomic_and arch_atomic_and +#define arch_atomic_or arch_atomic_or +#define arch_atomic_xor arch_atomic_xor +#define arch_atomic_fetch_and arch_atomic_fetch_and +#define arch_atomic_fetch_or arch_atomic_fetch_or +#define arch_atomic_fetch_xor arch_atomic_fetch_xor -static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +#define arch_atomic_xchg(v, new) (arch_xchg(&((v)->counter), new)) + +static inline int arch_atomic_cmpxchg(atomic_t *v, int old, int new) { return __atomic_cmpxchg(&v->counter, old, new); } +#define arch_atomic_cmpxchg arch_atomic_cmpxchg #define ATOMIC64_INIT(i) { (i) } -static inline s64 atomic64_read(const atomic64_t *v) +static inline s64 arch_atomic64_read(const atomic64_t *v) { return __atomic64_read(v); } +#define arch_atomic64_read arch_atomic64_read -static inline void atomic64_set(atomic64_t *v, s64 i) +static inline void arch_atomic64_set(atomic64_t *v, s64 i) { __atomic64_set(v, i); } +#define arch_atomic64_set arch_atomic64_set -static inline s64 atomic64_add_return(s64 i, atomic64_t *v) +static inline s64 arch_atomic64_add_return(s64 i, atomic64_t *v) { return __atomic64_add_barrier(i, (long *)&v->counter) + i; } +#define arch_atomic64_add_return arch_atomic64_add_return -static inline s64 atomic64_fetch_add(s64 i, atomic64_t *v) +static inline s64 arch_atomic64_fetch_add(s64 i, atomic64_t *v) { return __atomic64_add_barrier(i, (long *)&v->counter); } +#define arch_atomic64_fetch_add arch_atomic64_fetch_add -static inline void atomic64_add(s64 i, atomic64_t *v) +static inline void arch_atomic64_add(s64 i, atomic64_t *v) { __atomic64_add(i, (long *)&v->counter); } +#define arch_atomic64_add arch_atomic64_add -#define atomic64_xchg(v, new) (xchg(&((v)->counter), new)) +#define arch_atomic64_xchg(v, new) (arch_xchg(&((v)->counter), new)) -static inline s64 atomic64_cmpxchg(atomic64_t *v, s64 old, s64 new) +static inline s64 arch_atomic64_cmpxchg(atomic64_t *v, s64 old, s64 new) { return __atomic64_cmpxchg((long *)&v->counter, old, new); } +#define arch_atomic64_cmpxchg arch_atomic64_cmpxchg #define ATOMIC64_OPS(op) \ -static inline void atomic64_##op(s64 i, atomic64_t *v) \ +static inline void arch_atomic64_##op(s64 i, atomic64_t *v) \ { \ __atomic64_##op(i, (long *)&v->counter); \ } \ -static inline long atomic64_fetch_##op(s64 i, atomic64_t *v) \ +static inline long arch_atomic64_fetch_##op(s64 i, atomic64_t *v) \ { \ return __atomic64_##op##_barrier(i, (long *)&v->counter); \ } @@ -117,8 +136,17 @@ ATOMIC64_OPS(xor) #undef ATOMIC64_OPS -#define atomic64_sub_return(_i, _v) atomic64_add_return(-(s64)(_i), _v) -#define atomic64_fetch_sub(_i, _v) atomic64_fetch_add(-(s64)(_i), _v) -#define atomic64_sub(_i, _v) atomic64_add(-(s64)(_i), _v) +#define arch_atomic64_and arch_atomic64_and +#define arch_atomic64_or arch_atomic64_or +#define arch_atomic64_xor arch_atomic64_xor +#define arch_atomic64_fetch_and arch_atomic64_fetch_and +#define arch_atomic64_fetch_or arch_atomic64_fetch_or +#define arch_atomic64_fetch_xor arch_atomic64_fetch_xor + +#define arch_atomic64_sub_return(_i, _v) arch_atomic64_add_return(-(s64)(_i), _v) +#define arch_atomic64_fetch_sub(_i, _v) arch_atomic64_fetch_add(-(s64)(_i), _v) +#define arch_atomic64_sub(_i, _v) arch_atomic64_add(-(s64)(_i), _v) + +#define ARCH_ATOMIC #endif /* __ARCH_S390_ATOMIC__ */ diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 263d3f87edc8..6def9722124e 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -73,7 +73,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) return x; } -#define xchg(ptr, x) \ +#define arch_xchg(ptr, x) \ ({ \ __typeof__(*(ptr)) __ret; \ \ @@ -154,7 +154,7 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, return old; } -#define cmpxchg(ptr, o, n) \ +#define arch_cmpxchg(ptr, o, n) \ ({ \ __typeof__(*(ptr)) __ret; \ \ @@ -164,9 +164,9 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, __ret; \ }) -#define cmpxchg64 cmpxchg -#define cmpxchg_local cmpxchg -#define cmpxchg64_local cmpxchg +#define arch_cmpxchg64 arch_cmpxchg +#define arch_cmpxchg_local arch_cmpxchg +#define arch_cmpxchg64_local arch_cmpxchg #define system_has_cmpxchg_double() 1 @@ -188,7 +188,7 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, !cc; \ }) -#define cmpxchg_double(p1, p2, o1, o2, n1, n2) \ +#define arch_cmpxchg_double(p1, p2, o1, o2, n1, n2) \ ({ \ __typeof__(p1) __p1 = (p1); \ __typeof__(p2) __p2 = (p2); \ -- cgit v1.2.3 From 4f9abb7e70f2f4808f0fce36b66232890201c6a3 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Mar 2021 16:12:56 +0100 Subject: s390/spinlock: use R constraint in inline assembly Allow the compiler to generate slightly better code by using the R constraint. Signed-off-by: Heiko Carstens --- arch/s390/include/asm/spinlock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/include/asm/spinlock.h b/arch/s390/include/asm/spinlock.h index 3a37172d5398..ef59588a3042 100644 --- a/arch/s390/include/asm/spinlock.h +++ b/arch/s390/include/asm/spinlock.h @@ -88,7 +88,7 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) asm_inline volatile( ALTERNATIVE("", ".long 0xb2fa0070", 49) /* NIAI 7 */ " sth %1,%0\n" - : "=Q" (((unsigned short *) &lp->lock)[1]) + : "=R" (((unsigned short *) &lp->lock)[1]) : "d" (0) : "cc", "memory"); } -- cgit v1.2.3 From 4dd4269ea51eeb68a073b900df6b5f5b78159633 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 10 Apr 2021 19:18:25 +0200 Subject: s390: update defconfigs Set CONFIG_FRAME_WARN to 2048, which is the default for 64 bit architectures. Signed-off-by: Heiko Carstens --- arch/s390/configs/debug_defconfig | 1 - arch/s390/configs/defconfig | 1 - 2 files changed, 2 deletions(-) (limited to 'arch') diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index b2d2db1b3410..6422618a4f75 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -771,7 +771,6 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y -CONFIG_FRAME_WARN=1024 CONFIG_HEADERS_INSTALL=y CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_MAGIC_SYSRQ=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 320379da96d9..371a529546aa 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -756,7 +756,6 @@ CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y -CONFIG_FRAME_WARN=1024 CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_WX=y -- cgit v1.2.3 From f169f42130653bd4da24ed0d1b2cc91af5977928 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 9 Apr 2021 00:21:14 +0200 Subject: s390/test_unwind: unify error handling paths Handle the case of "unwind state reliable but addr is 0" like other error cases in this function and trigger output of failing stacktrace to aid debugging. Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/lib/test_unwind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index dcd8946255be..54b36e71ddf3 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -64,8 +64,8 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, break; if (state.reliable && !addr) { pr_err("unwind state reliable but addr is 0\n"); - kfree(bt); - return -EINVAL; + ret = -EINVAL; + break; } sprint_symbol(sym, addr); if (bt_pos < BT_BUF_SIZE) { -- cgit v1.2.3 From 9d42a4d3e27db3cabad82483ed876d4c8b8bed65 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 9 Apr 2021 00:31:47 +0200 Subject: s390/test_unwind: add WARN if tests failed Trigger a warning if any of unwinder tests fail. This should help to prevent quiet ignoring of test results when panic_on_warn is enabled. Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/lib/test_unwind.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index 54b36e71ddf3..eda72bc49c79 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -296,16 +296,18 @@ static int test_unwind_flags(int flags) static int test_unwind_init(void) { - int ret = 0; + int failed = 0; + int total = 0; #define TEST(flags) \ do { \ pr_info("[ RUN ] " #flags "\n"); \ + total++; \ if (!test_unwind_flags((flags))) { \ pr_info("[ OK ] " #flags "\n"); \ } else { \ pr_err("[ FAILED ] " #flags "\n"); \ - ret = -EINVAL; \ + failed++; \ } \ } while (0) @@ -336,7 +338,8 @@ do { \ #endif #undef TEST - return ret; + WARN(failed, "%d of %d unwinder tests failed", failed, total); + return failed ? -EINVAL : 0; } static void test_unwind_exit(void) -- cgit v1.2.3 From 13525f0a62cc258b2b2266478cc5fec0a45d1e71 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Apr 2021 11:23:30 +0200 Subject: s390/cmpxchg: use unsigned long values instead of void pointers gcc and clang warn about incompatible pointer types due to the recent cmpxchg changes: drivers/gpu/drm/drm_lock.c:75:10: error: passing 'typeof (lock)' (aka 'volatile unsigned int *') to parameter of type 'void *' discards qualifiers [-Werror,-Wincompatible-pointer-types-discards-qualifiers] prev = cmpxchg(lock, old, new); ^~~~~~~~~~~~~~~~~~~~~~~ include/asm-generic/atomic-instrumented.h:1685:2: note: expanded from macro 'cmpxchg' arch_cmpxchg(__ai_ptr, __VA_ARGS__); \ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To avoid this simply cast pointers to unsigned long and use them instead of void pointers. This allows to stay with functions, instead of using complex defines and having to deal with all their potential side effects. Reported-by: kernel test robot Fixes: d2b1f6d2d350 ("s390/cmpxchg: get rid of gcc atomic builtins") Link: https://lore.kernel.org/linux-s390/202104130131.sMmSqpb5-lkp@intel.com/ Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 49 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 26 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 6def9722124e..6ae4e8a288a2 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -14,16 +14,15 @@ void __xchg_called_with_bad_pointer(void); -static inline unsigned long __xchg(unsigned long x, void *ptr, int size) +static inline unsigned long __xchg(unsigned long x, unsigned long address, int size) { - unsigned long addr, old; + unsigned long old; int shift; switch (size) { case 1: - addr = (unsigned long) ptr; - shift = (3 ^ (addr & 3)) << 3; - addr ^= addr & 3; + shift = (3 ^ (address & 3)) << 3; + address ^= address & 3; asm volatile( " l %0,%1\n" "0: lr 0,%0\n" @@ -31,14 +30,13 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) " or 0,%2\n" " cs %0,0,%1\n" " jl 0b\n" - : "=&d" (old), "+Q" (*(int *) addr) + : "=&d" (old), "+Q" (*(int *) address) : "d" ((x & 0xff) << shift), "d" (~(0xff << shift)) : "memory", "cc", "0"); return old >> shift; case 2: - addr = (unsigned long) ptr; - shift = (2 ^ (addr & 2)) << 3; - addr ^= addr & 2; + shift = (2 ^ (address & 2)) << 3; + address ^= address & 2; asm volatile( " l %0,%1\n" "0: lr 0,%0\n" @@ -46,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) " or 0,%2\n" " cs %0,0,%1\n" " jl 0b\n" - : "=&d" (old), "+Q" (*(int *) addr) + : "=&d" (old), "+Q" (*(int *) address) : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)) : "memory", "cc", "0"); return old >> shift; @@ -55,7 +53,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) " l %0,%1\n" "0: cs %0,%2,%1\n" " jl 0b\n" - : "=&d" (old), "+Q" (*(int *) ptr) + : "=&d" (old), "+Q" (*(int *) address) : "d" (x) : "memory", "cc"); return old; @@ -64,7 +62,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) " lg %0,%1\n" "0: csg %0,%2,%1\n" " jl 0b\n" - : "=&d" (old), "+S" (*(long *) ptr) + : "=&d" (old), "+S" (*(long *) address) : "d" (x) : "memory", "cc"); return old; @@ -78,23 +76,23 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size) __typeof__(*(ptr)) __ret; \ \ __ret = (__typeof__(*(ptr))) \ - __xchg((unsigned long)(x), (void *)(ptr), sizeof(*(ptr))); \ + __xchg((unsigned long)(x), (unsigned long)(ptr), \ + sizeof(*(ptr))); \ __ret; \ }) void __cmpxchg_called_with_bad_pointer(void); -static inline unsigned long __cmpxchg(void *ptr, unsigned long old, +static inline unsigned long __cmpxchg(unsigned long address, unsigned long old, unsigned long new, int size) { - unsigned long addr, prev, tmp; + unsigned long prev, tmp; int shift; switch (size) { case 1: - addr = (unsigned long) ptr; - shift = (3 ^ (addr & 3)) << 3; - addr ^= addr & 3; + shift = (3 ^ (address & 3)) << 3; + address ^= address & 3; asm volatile( " l %0,%2\n" "0: nr %0,%5\n" @@ -107,16 +105,15 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, " nr %1,%5\n" " jnz 0b\n" "1:" - : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr) + : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) address) : "d" ((old & 0xff) << shift), "d" ((new & 0xff) << shift), "d" (~(0xff << shift)) : "memory", "cc"); return prev >> shift; case 2: - addr = (unsigned long) ptr; - shift = (2 ^ (addr & 2)) << 3; - addr ^= addr & 2; + shift = (2 ^ (address & 2)) << 3; + address ^= address & 2; asm volatile( " l %0,%2\n" "0: nr %0,%5\n" @@ -129,7 +126,7 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, " nr %1,%5\n" " jnz 0b\n" "1:" - : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr) + : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) address) : "d" ((old & 0xffff) << shift), "d" ((new & 0xffff) << shift), "d" (~(0xffff << shift)) @@ -138,14 +135,14 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, case 4: asm volatile( " cs %0,%3,%1\n" - : "=&d" (prev), "+Q" (*(int *) ptr) + : "=&d" (prev), "+Q" (*(int *) address) : "0" (old), "d" (new) : "memory", "cc"); return prev; case 8: asm volatile( " csg %0,%3,%1\n" - : "=&d" (prev), "+S" (*(long *) ptr) + : "=&d" (prev), "+S" (*(long *) address) : "0" (old), "d" (new) : "memory", "cc"); return prev; @@ -159,7 +156,7 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, __typeof__(*(ptr)) __ret; \ \ __ret = (__typeof__(*(ptr))) \ - __cmpxchg((ptr), (unsigned long)(o), \ + __cmpxchg((unsigned long)(ptr), (unsigned long)(o), \ (unsigned long)(n), sizeof(*(ptr))); \ __ret; \ }) -- cgit v1.2.3 From 5d8da6951ee2b2f7785ead62f785f3b3dd254104 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 13 Apr 2021 13:28:50 +0200 Subject: s390/test_unwind: print test suite start/end info Add couple of additional info lines to make it easier to match test suite output and results. Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/lib/test_unwind.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index eda72bc49c79..2f32802f79ce 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -311,6 +311,7 @@ do { \ } \ } while (0) + pr_info("running stack unwinder tests"); TEST(UWM_DEFAULT); TEST(UWM_SP); TEST(UWM_REGS); @@ -337,8 +338,13 @@ do { \ TEST(UWM_PGM | UWM_SP | UWM_REGS); #endif #undef TEST + if (failed) { + pr_err("%d of %d stack unwinder tests failed", failed, total); + WARN(1, "%d of %d stack unwinder tests failed", failed, total); + } else { + pr_info("all %d stack unwinder tests passed", total); + } - WARN(failed, "%d of %d unwinder tests failed", failed, total); return failed ? -EINVAL : 0; } -- cgit v1.2.3 From 3e5ee32392efd00399d038cdad07478237d9a9f1 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 13 Apr 2021 15:30:32 +0200 Subject: s390/atomic,cmpxchg: make constraints work with old compilers Old gcc versions may fail with an internal compiler error if only the T or S constraint is specified for an operand, and no displacement is needed at all. To fix this use RT and QS as constraints, which reflects the union of both. Later gcc versions do the right thing and always accept single T and S constraints. See gcc commit 3e4be43f69da ("S/390: Memory constraint cleanup"). Fixes: ca897bb1814f ("s390/atomic: use proper constraints") Fixes: b23eb636d7f9 ("s390/atomic: get rid of gcc atomic builtins") Fixes: d2b1f6d2d350 ("s390/cmpxchg: get rid of gcc atomic builtins") Signed-off-by: Heiko Carstens --- arch/s390/include/asm/atomic_ops.h | 14 +++++++------- arch/s390/include/asm/cmpxchg.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h index 2e818f2709bf..50510e08b893 100644 --- a/arch/s390/include/asm/atomic_ops.h +++ b/arch/s390/include/asm/atomic_ops.h @@ -31,7 +31,7 @@ static inline s64 __atomic64_read(const atomic64_t *v) asm volatile( " lg %0,%1\n" - : "=d" (c) : "T" (v->counter)); + : "=d" (c) : "RT" (v->counter)); return c; } @@ -39,7 +39,7 @@ static inline void __atomic64_set(atomic64_t *v, s64 i) { asm volatile( " stg %1,%0\n" - : "=T" (v->counter) : "d" (i)); + : "=RT" (v->counter) : "d" (i)); } #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES @@ -52,7 +52,7 @@ static inline op_type op_name(op_type val, op_type *ptr) \ asm volatile( \ op_string " %[old],%[val],%[ptr]\n" \ op_barrier \ - : [old] "=d" (old), [ptr] "+S" (*ptr) \ + : [old] "=d" (old), [ptr] "+QS" (*ptr) \ : [val] "d" (val) : "cc", "memory"); \ return old; \ } \ @@ -80,7 +80,7 @@ static __always_inline void op_name(op_type val, op_type *ptr) \ asm volatile( \ op_string " %[ptr],%[val]\n" \ op_barrier \ - : [ptr] "+S" (*ptr) : [val] "i" (val) : "cc", "memory");\ + : [ptr] "+QS" (*ptr) : [val] "i" (val) : "cc", "memory");\ } #define __ATOMIC_CONST_OPS(op_name, op_type, op_string) \ @@ -131,7 +131,7 @@ static inline long op_name(long val, long *ptr) \ op_string " %[new],%[val]\n" \ " csg %[old],%[new],%[ptr]\n" \ " jl 0b" \ - : [old] "=d" (old), [new] "=&d" (new), [ptr] "+S" (*ptr)\ + : [old] "=d" (old), [new] "=&d" (new), [ptr] "+QS" (*ptr)\ : [val] "d" (val), "0" (*ptr) : "cc", "memory"); \ return old; \ } @@ -180,7 +180,7 @@ static inline long __atomic64_cmpxchg(long *ptr, long old, long new) { asm volatile( " csg %[old],%[new],%[ptr]" - : [old] "+d" (old), [ptr] "+S" (*ptr) + : [old] "+d" (old), [ptr] "+QS" (*ptr) : [new] "d" (new) : "cc", "memory"); return old; @@ -192,7 +192,7 @@ static inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new) asm volatile( " csg %[old],%[new],%[ptr]" - : [old] "+d" (old), [ptr] "+S" (*ptr) + : [old] "+d" (old), [ptr] "+QS" (*ptr) : [new] "d" (new) : "cc", "memory"); return old == old_expected; diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 6ae4e8a288a2..e1eb65fceef2 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -62,7 +62,7 @@ static inline unsigned long __xchg(unsigned long x, unsigned long address, int s " lg %0,%1\n" "0: csg %0,%2,%1\n" " jl 0b\n" - : "=&d" (old), "+S" (*(long *) address) + : "=&d" (old), "+QS" (*(long *) address) : "d" (x) : "memory", "cc"); return old; @@ -142,7 +142,7 @@ static inline unsigned long __cmpxchg(unsigned long address, unsigned long old, case 8: asm volatile( " csg %0,%3,%1\n" - : "=&d" (prev), "+S" (*(long *) address) + : "=&d" (prev), "+QS" (*(long *) address) : "0" (old), "d" (new) : "memory", "cc"); return prev; -- cgit v1.2.3 From ff23f8c970ab79238d9777f3d0d886eff13f7c06 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 14 Apr 2021 14:44:07 +0100 Subject: s390: get rid of oprofile leftovers perf_pmu_name() and perf_num_counters() are unused. Drop them. Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20210414134409.1266357-4-maz@kernel.org Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_event.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'arch') diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index 1e75cc983546..ea7729bebaa0 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c @@ -23,27 +23,6 @@ #include #include -const char *perf_pmu_name(void) -{ - if (cpum_cf_avail() || cpum_sf_avail()) - return "CPU-Measurement Facilities (CPU-MF)"; - return "pmu"; -} -EXPORT_SYMBOL(perf_pmu_name); - -int perf_num_counters(void) -{ - int num = 0; - - if (cpum_cf_avail()) - num += PERF_CPUM_CF_MAX_CTR; - if (cpum_sf_avail()) - num += PERF_CPUM_SF_MAX_CTR; - - return num; -} -EXPORT_SYMBOL(perf_num_counters); - static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs) { struct stack_frame *stack = (struct stack_frame *) regs->gprs[15]; -- cgit v1.2.3 From b44913fceb1324be8eaefa8a96c9ae5d368b39c5 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Tue, 6 Apr 2021 17:38:11 +0200 Subject: s390/smp: fix do_restart() prototype Funciton do_restart() is a callback invoked from the restart CPU routine and passed a single parameter. Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/kernel/entry.h | 2 +- arch/s390/kernel/ipl.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 09abb11bc660..1ab33465382f 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -36,7 +36,7 @@ void do_notify_resume(struct pt_regs *regs); void __init init_IRQ(void); void do_io_irq(struct pt_regs *regs); void do_ext_irq(struct pt_regs *regs); -void do_restart(void); +void do_restart(void *arg); void __init startup_init(void); void die(struct pt_regs *regs, const char *str); int setup_profiling_timer(unsigned int multiplier); diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 7a21eca498aa..dba04fbc37a2 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1849,12 +1849,12 @@ static void __do_restart(void *ignore) stop_run(&on_restart_trigger); } -void do_restart(void) +void do_restart(void *arg) { tracing_off(); debug_locks_off(); lgr_info_log(); - smp_call_online_cpu(__do_restart, NULL); + smp_call_online_cpu(__do_restart, arg); } /* on halt */ -- cgit v1.2.3 From a637b3bfa43aead7221b79cd92e092ef911c8253 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 17 Apr 2021 14:48:32 +0200 Subject: s390/atomic,cmpxchg: always inline __xchg/__cmpxchg Make sure to always inline __xchg() and __cmpxchg() otherwise the compiler might decide to generate out-of-line versions which will fail at link time: s390-linux-ld: lib/atomic64_test.o: in function `__xchg': >> atomic64_test.c:(.text.unlikely+0xa4): undefined reference to `__xchg_called_with_bad_pointer' Reported-by: kernel test robot Link: https://lore.kernel.org/linux-mm/202104170449.SIIFKVjT-lkp@intel.com/ Fixes: d2b1f6d2d350 ("s390/cmpxchg: get rid of gcc atomic builtins") Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index e1eb65fceef2..1960a7295ae5 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -14,7 +14,8 @@ void __xchg_called_with_bad_pointer(void); -static inline unsigned long __xchg(unsigned long x, unsigned long address, int size) +static __always_inline unsigned long __xchg(unsigned long x, + unsigned long address, int size) { unsigned long old; int shift; @@ -83,8 +84,9 @@ static inline unsigned long __xchg(unsigned long x, unsigned long address, int s void __cmpxchg_called_with_bad_pointer(void); -static inline unsigned long __cmpxchg(unsigned long address, unsigned long old, - unsigned long new, int size) +static __always_inline unsigned long __cmpxchg(unsigned long address, + unsigned long old, + unsigned long new, int size) { unsigned long prev, tmp; int shift; -- cgit v1.2.3 From 81bbf03905aae47a80fd05604cc9b0d1ca20e30a Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 24 Feb 2021 11:29:36 +0100 Subject: s390/pci: expose a PCI device's UID as its index On s390 each PCI device has a user-defined ID (UID) exposed under /sys/bus/pci/devices//uid. This ID was designed to serve as the PCI device's primary index and to match the device within Linux to the device configured in the hypervisor. To serve as a primary identifier the UID must be unique within the Linux instance, this is guaranteed by the platform if and only if the UID Uniqueness Checking flag is set within the CLP List PCI Functions response. In this sense the UID serves an analogous function as the SMBIOS instance number or ACPI index exposed as the "index" respectively "acpi_index" device attributes and used by e.g. systemd to set interface names. As s390 does not use and will likely never use ACPI nor SMBIOS there is no conflict and we can just expose the UID under the "index" attribute whenever UID Uniqueness Checking is active and get systemd's interface naming support for free. Link: https://lore.kernel.org/lkml/20210412135905.1434249-1-schnelle@linux.ibm.com/ Acked-by: Viktor Mihajlovski Acked-by: Bjorn Helgaas Acked-by: Narendra K Signed-off-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- Documentation/ABI/testing/sysfs-bus-pci | 11 +++++++---- arch/s390/pci/pci_sysfs.c | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 25c9c39770c6..1241b6d11a52 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -195,10 +195,13 @@ What: /sys/bus/pci/devices/.../index Date: July 2010 Contact: Narendra K , linux-bugs@dell.com Description: - Reading this attribute will provide the firmware - given instance (SMBIOS type 41 device type instance) of the - PCI device. The attribute will be created only if the firmware - has given an instance number to the PCI device. + Reading this attribute will provide the firmware given instance + number of the PCI device. Depending on the platform this can + be for example the SMBIOS type 41 device type instance or the + user-defined ID (UID) on s390. The attribute will be created + only if the firmware has given an instance number to the PCI + device and that number is guaranteed to uniquely identify the + device in the system. Users: Userspace applications interested in knowing the firmware assigned device type instance of the PCI diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index e14d346dafd6..6e2450c2b9c1 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -138,6 +138,38 @@ static ssize_t uid_is_unique_show(struct device *dev, } static DEVICE_ATTR_RO(uid_is_unique); +#ifndef CONFIG_DMI +/* analogous to smbios index */ +static ssize_t index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zpci_dev *zdev = to_zpci(to_pci_dev(dev)); + u32 index = ~0; + + if (zpci_unique_uid) + index = zdev->uid; + + return sysfs_emit(buf, "%u\n", index); +} +static DEVICE_ATTR_RO(index); + +static umode_t zpci_index_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return zpci_unique_uid ? attr->mode : 0; +} + +static struct attribute *zpci_ident_attrs[] = { + &dev_attr_index.attr, + NULL, +}; + +static struct attribute_group zpci_ident_attr_group = { + .attrs = zpci_ident_attrs, + .is_visible = zpci_index_is_visible, +}; +#endif + static struct bin_attribute *zpci_bin_attrs[] = { &bin_attr_util_string, &bin_attr_report_error, @@ -179,5 +211,8 @@ static struct attribute_group pfip_attr_group = { const struct attribute_group *zpci_attr_groups[] = { &zpci_attr_group, &pfip_attr_group, +#ifndef CONFIG_DMI + &zpci_ident_attr_group, +#endif NULL, }; -- cgit v1.2.3 From 28096067686c5a5cbd4c35b079749bd805df5010 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Tue, 20 Apr 2021 08:23:12 +0200 Subject: s390/archrandom: add parameter check for s390_arch_random_generate A review of the code showed, that this function which is exposed within the whole kernel should do a parameter check for the amount of bytes requested. If this requested bytes is too high an unsigned int overflow could happen causing this function to try to memcpy a really big memory chunk. This is not a security issue as there are only two invocations of this function from arch/s390/include/asm/archrandom.h and both are not exposed to userland. Reported-by: Sven Schnelle Signed-off-by: Harald Freudenberger Signed-off-by: Heiko Carstens --- arch/s390/crypto/arch_random.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch') diff --git a/arch/s390/crypto/arch_random.c b/arch/s390/crypto/arch_random.c index 7b947728d57e..56007c763902 100644 --- a/arch/s390/crypto/arch_random.c +++ b/arch/s390/crypto/arch_random.c @@ -54,6 +54,10 @@ static DECLARE_DELAYED_WORK(arch_rng_work, arch_rng_refill_buffer); bool s390_arch_random_generate(u8 *buf, unsigned int nbytes) { + /* max hunk is ARCH_RNG_BUF_SIZE */ + if (nbytes > ARCH_RNG_BUF_SIZE) + return false; + /* lock rng buffer */ if (!spin_trylock(&arch_rng_lock)) return false; -- cgit v1.2.3 From 6f3353c2d2b3eb4de52e9704cb962712033db181 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 20 Apr 2021 11:04:10 +0200 Subject: s390/disassembler: increase ebpf disasm buffer size Current ebpf disassembly buffer size of 64 is too small. E.g. this line takes 65 bytes: 01fffff8005822e: ec8100ed8065\tclgrj\t%r8,%r1,8,001fffff80058408\n\0 Double the buffer size like it is done for the kernel disassembly buffer. Fixes the following KASAN finding: UG: KASAN: stack-out-of-bounds in print_fn_code+0x34c/0x380 Write of size 1 at addr 001fff800ad5f970 by task test_progs/853 CPU: 53 PID: 853 Comm: test_progs Not tainted 5.12.0-rc7-23786-g23457d86b1f0-dirty #19 Hardware name: IBM 3906 M04 704 (LPAR) Call Trace: [<0000000cd8e0538a>] show_stack+0x17a/0x1668 [<0000000cd8e2a5d8>] dump_stack+0x140/0x1b8 [<0000000cd8e16e74>] print_address_description.constprop.0+0x54/0x260 [<0000000cd75a8698>] kasan_report+0xc8/0x130 [<0000000cd6e26da4>] print_fn_code+0x34c/0x380 [<0000000cd6ea0f4e>] bpf_int_jit_compile+0xe3e/0xe58 [<0000000cd72c4c88>] bpf_prog_select_runtime+0x5b8/0x9c0 [<0000000cd72d1bf8>] bpf_prog_load+0xa78/0x19c0 [<0000000cd72d7ad6>] __do_sys_bpf.part.0+0x18e/0x768 [<0000000cd6e0f392>] do_syscall+0x12a/0x220 [<0000000cd8e333f8>] __do_syscall+0x98/0xc8 [<0000000cd8e54834>] system_call+0x6c/0x94 1 lock held by test_progs/853: #0: 0000000cd9bf7460 (report_lock){....}-{2:2}, at: kasan_report+0x96/0x130 addr 001fff800ad5f970 is located in stack of task test_progs/853 at offset 96 in frame: print_fn_code+0x0/0x380 this frame has 1 object: [32, 96) 'buffer' Memory state around the buggy address: 001fff800ad5f800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 001fff800ad5f880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >001fff800ad5f900: 00 00 f1 f1 f1 f1 00 00 00 00 00 00 00 00 f3 f3 ^ 001fff800ad5f980: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 001fff800ad5fa00: 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 Cc: Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/kernel/dis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index a7eab7be4db0..5412efe328f8 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -563,7 +563,7 @@ void show_code(struct pt_regs *regs) void print_fn_code(unsigned char *code, unsigned long len) { - char buffer[64], *ptr; + char buffer[128], *ptr; int opsize, i; while (len) { -- cgit v1.2.3