From: Greg Kroah-Hartman Date: Mon, 25 Oct 2021 09:12:17 +0000 (+0200) Subject: 5.10-stable patches X-Git-Tag: v4.4.290~29 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e0284d8fd8b28bd2305e8283480a40d5792f2eaa;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: s390-pci-fix-zpci_zdev_put-on-reserve.patch --- diff --git a/queue-5.10/s390-pci-fix-zpci_zdev_put-on-reserve.patch b/queue-5.10/s390-pci-fix-zpci_zdev_put-on-reserve.patch new file mode 100644 index 00000000000..180ff6373cf --- /dev/null +++ b/queue-5.10/s390-pci-fix-zpci_zdev_put-on-reserve.patch @@ -0,0 +1,170 @@ +From a46044a92add6a400f4dada7b943b30221f7cc80 Mon Sep 17 00:00:00 2001 +From: Niklas Schnelle +Date: Wed, 22 Sep 2021 15:55:12 +0200 +Subject: s390/pci: fix zpci_zdev_put() on reserve + +From: Niklas Schnelle + +commit a46044a92add6a400f4dada7b943b30221f7cc80 upstream. + +Since commit 2a671f77ee49 ("s390/pci: fix use after free of zpci_dev") +the reference count of a zpci_dev is incremented between +pcibios_add_device() and pcibios_release_device() which was supposed to +prevent the zpci_dev from being freed while the common PCI code has +access to it. It was missed however that the handling of zPCI +availability events assumed that once zpci_zdev_put() was called no +later availability event would still see the device. With the previously +mentioned commit however this assumption no longer holds and we must +make sure that we only drop the initial long-lived reference the zPCI +subsystem holds exactly once. + +Do so by introducing a zpci_device_reserved() function that handles when +a device is reserved. Here we make sure the zpci_dev will not be +considered for further events by removing it from the zpci_list. + +This also means that the device actually stays in the +ZPCI_FN_STATE_RESERVED state between the time we know it has been +reserved and the final reference going away. We thus need to consider it +a real state instead of just a conceptual state after the removal. The +final cleanup of PCI resources, removal from zbus, and destruction of +the IOMMU stays in zpci_release_device() to make sure holders of the +reference do see valid data until the release. + +Fixes: 2a671f77ee49 ("s390/pci: fix use after free of zpci_dev") +Cc: stable@vger.kernel.org +Signed-off-by: Niklas Schnelle +Signed-off-by: Vasily Gorbik +Signed-off-by: Greg Kroah-Hartman + +--- + arch/s390/include/asm/pci.h | 3 ++ + arch/s390/pci/pci.c | 45 ++++++++++++++++++++++++++++++++----- + arch/s390/pci/pci_event.c | 4 +-- + drivers/pci/hotplug/s390_pci_hpc.c | 9 ------- + 4 files changed, 46 insertions(+), 15 deletions(-) + +--- a/arch/s390/include/asm/pci.h ++++ b/arch/s390/include/asm/pci.h +@@ -205,6 +205,9 @@ int zpci_create_device(u32 fid, u32 fh, + 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 *); ++void zpci_device_reserved(struct zpci_dev *zdev); ++bool zpci_is_device_configured(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); +--- a/arch/s390/pci/pci.c ++++ b/arch/s390/pci/pci.c +@@ -92,7 +92,7 @@ void zpci_remove_reserved_devices(void) + spin_unlock(&zpci_list_lock); + + list_for_each_entry_safe(zdev, tmp, &remove, entry) +- zpci_zdev_put(zdev); ++ zpci_device_reserved(zdev); + } + + int pci_domain_nr(struct pci_bus *bus) +@@ -787,6 +787,39 @@ error: + return rc; + } + ++bool zpci_is_device_configured(struct zpci_dev *zdev) ++{ ++ enum zpci_state state = zdev->state; ++ ++ return state != ZPCI_FN_STATE_RESERVED && ++ state != ZPCI_FN_STATE_STANDBY; ++} ++ ++/** ++ * zpci_device_reserved() - Mark device as resverved ++ * @zdev: the zpci_dev that was reserved ++ * ++ * Handle the case that a given zPCI function was reserved by another system. ++ * After a call to this function the zpci_dev can not be found via ++ * get_zdev_by_fid() anymore but may still be accessible via existing ++ * references though it will not be functional anymore. ++ */ ++void zpci_device_reserved(struct zpci_dev *zdev) ++{ ++ if (zdev->has_hp_slot) ++ zpci_exit_slot(zdev); ++ /* ++ * Remove device from zpci_list as it is going away. This also ++ * makes sure we ignore subsequent zPCI events for this device. ++ */ ++ spin_lock(&zpci_list_lock); ++ list_del(&zdev->entry); ++ spin_unlock(&zpci_list_lock); ++ zdev->state = ZPCI_FN_STATE_RESERVED; ++ zpci_dbg(3, "rsv fid:%x\n", zdev->fid); ++ zpci_zdev_put(zdev); ++} ++ + void zpci_release_device(struct kref *kref) + { + struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref); +@@ -802,6 +835,12 @@ void zpci_release_device(struct kref *kr + case ZPCI_FN_STATE_STANDBY: + if (zdev->has_hp_slot) + zpci_exit_slot(zdev); ++ spin_lock(&zpci_list_lock); ++ list_del(&zdev->entry); ++ spin_unlock(&zpci_list_lock); ++ zpci_dbg(3, "rsv fid:%x\n", zdev->fid); ++ fallthrough; ++ case ZPCI_FN_STATE_RESERVED: + if (zdev->has_resources) + zpci_cleanup_bus_resources(zdev); + zpci_bus_device_unregister(zdev); +@@ -810,10 +849,6 @@ void zpci_release_device(struct kref *kr + default: + break; + } +- +- spin_lock(&zpci_list_lock); +- list_del(&zdev->entry); +- spin_unlock(&zpci_list_lock); + zpci_dbg(3, "rem fid:%x\n", zdev->fid); + kfree(zdev); + } +--- a/arch/s390/pci/pci_event.c ++++ b/arch/s390/pci/pci_event.c +@@ -146,7 +146,7 @@ static void __zpci_event_availability(st + zdev->state = ZPCI_FN_STATE_STANDBY; + if (!clp_get_state(ccdf->fid, &state) && + state == ZPCI_FN_STATE_RESERVED) { +- zpci_zdev_put(zdev); ++ zpci_device_reserved(zdev); + } + break; + case 0x0306: /* 0x308 or 0x302 for multiple devices */ +@@ -156,7 +156,7 @@ static void __zpci_event_availability(st + case 0x0308: /* Standby -> Reserved */ + if (!zdev) + break; +- zpci_zdev_put(zdev); ++ zpci_device_reserved(zdev); + break; + default: + break; +--- a/drivers/pci/hotplug/s390_pci_hpc.c ++++ b/drivers/pci/hotplug/s390_pci_hpc.c +@@ -109,14 +109,7 @@ static int get_power_status(struct hotpl + struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev, + hotplug_slot); + +- switch (zdev->state) { +- case ZPCI_FN_STATE_STANDBY: +- *value = 0; +- break; +- default: +- *value = 1; +- break; +- } ++ *value = zpci_is_device_configured(zdev) ? 1 : 0; + return 0; + } + diff --git a/queue-5.10/series b/queue-5.10/series index c9403eacfcf..4cf32d4e66f 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -85,3 +85,4 @@ net-hns3-fix-for-miscalculation-of-rx-unused-desc.patch scsi-core-fix-shost-cmd_per_lun-calculation-in-scsi_add_host_with_dma.patch can-isotp-isotp_sendmsg-fix-tx-buffer-concurrent-access-in-isotp_sendmsg.patch s390-pci-cleanup-resources-only-if-necessary.patch +s390-pci-fix-zpci_zdev_put-on-reserve.patch