]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 15 May 2023 12:48:36 +0000 (14:48 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 15 May 2023 12:48:36 +0000 (14:48 +0200)
added patches:
drbd-correctly-submit-flush-bio-on-barrier.patch
pci-pciehp-fix-ab-ba-deadlock-between-reset_lock-and-device_lock.patch
pci-pciehp-use-down_read-write_nested-reset_lock-to-fix-lockdep-errors.patch
serial-8250-fix-serial8250_tx_empty-race-with-dma-tx.patch
tty-prevent-writing-chars-during-tcsetattr-tcsadrain-flush.patch

queue-5.4/drbd-correctly-submit-flush-bio-on-barrier.patch [new file with mode: 0644]
queue-5.4/pci-pciehp-fix-ab-ba-deadlock-between-reset_lock-and-device_lock.patch [new file with mode: 0644]
queue-5.4/pci-pciehp-use-down_read-write_nested-reset_lock-to-fix-lockdep-errors.patch [new file with mode: 0644]
queue-5.4/serial-8250-fix-serial8250_tx_empty-race-with-dma-tx.patch [new file with mode: 0644]
queue-5.4/series
queue-5.4/tty-prevent-writing-chars-during-tcsetattr-tcsadrain-flush.patch [new file with mode: 0644]

diff --git a/queue-5.4/drbd-correctly-submit-flush-bio-on-barrier.patch b/queue-5.4/drbd-correctly-submit-flush-bio-on-barrier.patch
new file mode 100644 (file)
index 0000000..81b1a3a
--- /dev/null
@@ -0,0 +1,49 @@
+From 3899d94e3831ee07ea6821c032dc297aec80586a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Christoph=20B=C3=B6hmwalder?=
+ <christoph.boehmwalder@linbit.com>
+Date: Wed, 3 May 2023 14:19:37 +0200
+Subject: drbd: correctly submit flush bio on barrier
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
+
+commit 3899d94e3831ee07ea6821c032dc297aec80586a upstream.
+
+When we receive a flush command (or "barrier" in DRBD), we currently use
+a REQ_OP_FLUSH with the REQ_PREFLUSH flag set.
+
+The correct way to submit a flush bio is by using a REQ_OP_WRITE without
+any data, and set the REQ_PREFLUSH flag.
+
+Since commit b4a6bb3a67aa ("block: add a sanity check for non-write
+flush/fua bios"), this triggers a warning in the block layer, but this
+has been broken for quite some time before that.
+
+So use the correct set of flags to actually make the flush happen.
+
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: stable@vger.kernel.org
+Fixes: f9ff0da56437 ("drbd: allow parallel flushes for multi-volume resources")
+Reported-by: Thomas Voegtle <tv@lio96.de>
+Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20230503121937.17232-1-christoph.boehmwalder@linbit.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/block/drbd/drbd_receiver.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/block/drbd/drbd_receiver.c
++++ b/drivers/block/drbd/drbd_receiver.c
+@@ -1298,7 +1298,7 @@ static void submit_one_flush(struct drbd
+       bio_set_dev(bio, device->ldev->backing_bdev);
+       bio->bi_private = octx;
+       bio->bi_end_io = one_flush_endio;
+-      bio->bi_opf = REQ_OP_FLUSH | REQ_PREFLUSH;
++      bio->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
+       device->flush_jif = jiffies;
+       set_bit(FLUSH_PENDING, &device->flags);
diff --git a/queue-5.4/pci-pciehp-fix-ab-ba-deadlock-between-reset_lock-and-device_lock.patch b/queue-5.4/pci-pciehp-fix-ab-ba-deadlock-between-reset_lock-and-device_lock.patch
new file mode 100644 (file)
index 0000000..e677860
--- /dev/null
@@ -0,0 +1,181 @@
+From stable-owner@vger.kernel.org Tue May  9 12:51:50 2023
+From: Lukas Wunner <lukas@wunner.de>
+Date: Tue, 9 May 2023 12:41:10 +0200
+Subject: PCI: pciehp: Fix AB-BA deadlock between reset_lock and device_lock
+To: stable@vger.kernel.org
+Cc: Anatoli.Antonovitch@amd.com, alex.williamson@redhat.com, amichon@kalrayinc.com, andrey2805@gmail.com, ashok.raj@intel.com, bhelgaas@google.com, dstein@hpe.com, ian.may@canonical.com, michael.haeuptle@hpe.com, mika.westerberg@linux.intel.com, rahul.kumar1@amd.com, sathyanarayanan.kuppuswamy@linux.intel.com, wangxiongfeng2@huawei.com, zhangjialin11@huawei.com
+Message-ID: <8811bdb978f819b519ef33e9f536422453311bea.1683628574.git.lukas@wunner.de>
+
+From: Lukas Wunner <lukas@wunner.de>
+
+commit f5eff5591b8f9c5effd25c92c758a127765f74c1 upstream.
+
+In 2013, commits
+
+  2e35afaefe64 ("PCI: pciehp: Add reset_slot() method")
+  608c388122c7 ("PCI: Add slot reset option to pci_dev_reset()")
+
+amended PCIe hotplug to mask Presence Detect Changed events during a
+Secondary Bus Reset.  The reset thus no longer causes gratuitous slot
+bringdown and bringup.
+
+However the commits neglected to serialize reset with code paths reading
+slot registers.  For instance, a slot bringup due to an earlier hotplug
+event may see the Presence Detect State bit cleared during a concurrent
+Secondary Bus Reset.
+
+In 2018, commit
+
+  5b3f7b7d062b ("PCI: pciehp: Avoid slot access during reset")
+
+retrofitted the missing locking.  It introduced a reset_lock which
+serializes a Secondary Bus Reset with other parts of pciehp.
+
+Unfortunately the locking turns out to be overzealous:  reset_lock is
+held for the entire enumeration and de-enumeration of hotplugged devices,
+including driver binding and unbinding.
+
+Driver binding and unbinding acquires device_lock while the reset_lock
+of the ancestral hotplug port is held.  A concurrent Secondary Bus Reset
+acquires the ancestral reset_lock while already holding the device_lock.
+The asymmetric locking order in the two code paths can lead to AB-BA
+deadlocks.
+
+Michael Haeuptle reports such deadlocks on simultaneous hot-removal and
+vfio release (the latter implies a Secondary Bus Reset):
+
+  pciehp_ist()                                    # down_read(reset_lock)
+    pciehp_handle_presence_or_link_change()
+      pciehp_disable_slot()
+        __pciehp_disable_slot()
+          remove_board()
+            pciehp_unconfigure_device()
+              pci_stop_and_remove_bus_device()
+                pci_stop_bus_device()
+                  pci_stop_dev()
+                    device_release_driver()
+                      device_release_driver_internal()
+                        __device_driver_lock()    # device_lock()
+
+  SYS_munmap()
+    vfio_device_fops_release()
+      vfio_device_group_close()
+        vfio_device_close()
+          vfio_device_last_close()
+            vfio_pci_core_close_device()
+              vfio_pci_core_disable()             # device_lock()
+                __pci_reset_function_locked()
+                  pci_reset_bus_function()
+                    pci_dev_reset_slot_function()
+                      pci_reset_hotplug_slot()
+                        pciehp_reset_slot()       # down_write(reset_lock)
+
+Ian May reports the same deadlock on simultaneous hot-removal and an
+AER-induced Secondary Bus Reset:
+
+  aer_recover_work_func()
+    pcie_do_recovery()
+      aer_root_reset()
+        pci_bus_error_reset()
+          pci_slot_reset()
+            pci_slot_lock()                       # device_lock()
+            pci_reset_hotplug_slot()
+              pciehp_reset_slot()                 # down_write(reset_lock)
+
+Fix by releasing the reset_lock during driver binding and unbinding,
+thereby splitting and shrinking the critical section.
+
+Driver binding and unbinding is protected by the device_lock() and thus
+serialized with a Secondary Bus Reset.  There's no need to additionally
+protect it with the reset_lock.  However, pciehp does not bind and
+unbind devices directly, but rather invokes PCI core functions which
+also perform certain enumeration and de-enumeration steps.
+
+The reset_lock's purpose is to protect slot registers, not enumeration
+and de-enumeration of hotplugged devices.  That would arguably be the
+job of the PCI core, not the PCIe hotplug driver.  After all, an
+AER-induced Secondary Bus Reset may as well happen during boot-time
+enumeration of the PCI hierarchy and there's no locking to prevent that
+either.
+
+Exempting *de-enumeration* from the reset_lock is relatively harmless:
+A concurrent Secondary Bus Reset may foil config space accesses such as
+PME interrupt disablement.  But if the device is physically gone, those
+accesses are pointless anyway.  If the device is physically present and
+only logically removed through an Attention Button press or the sysfs
+"power" attribute, PME interrupts as well as DMA cannot come through
+because pciehp_unconfigure_device() disables INTx and Bus Master bits.
+That's still protected by the reset_lock in the present commit.
+
+Exempting *enumeration* from the reset_lock also has limited impact:
+The exempted call to pci_bus_add_device() may perform device accesses
+through pcibios_bus_add_device() and pci_fixup_device() which are now
+no longer protected from a concurrent Secondary Bus Reset.  Otherwise
+there should be no impact.
+
+In essence, the present commit seeks to fix the AB-BA deadlocks while
+still retaining a best-effort reset protection for enumeration and
+de-enumeration of hotplugged devices -- until a general solution is
+implemented in the PCI core.
+
+Link: https://lore.kernel.org/linux-pci/CS1PR8401MB0728FC6FDAB8A35C22BD90EC95F10@CS1PR8401MB0728.NAMPRD84.PROD.OUTLOOK.COM
+Link: https://lore.kernel.org/linux-pci/20200615143250.438252-1-ian.may@canonical.com
+Link: https://lore.kernel.org/linux-pci/ce878dab-c0c4-5bd0-a725-9805a075682d@amd.com
+Link: https://lore.kernel.org/linux-pci/ed831249-384a-6d35-0831-70af191e9bce@huawei.com
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=215590
+Fixes: 5b3f7b7d062b ("PCI: pciehp: Avoid slot access during reset")
+Link: https://lore.kernel.org/r/fef2b2e9edf245c049a8c5b94743c0f74ff5008a.1681191902.git.lukas@wunner.de
+Reported-by: Michael Haeuptle <michael.haeuptle@hpe.com>
+Reported-by: Ian May <ian.may@canonical.com>
+Reported-by: Andrey Grodzovsky <andrey2805@gmail.com>
+Reported-by: Rahul Kumar <rahul.kumar1@amd.com>
+Reported-by: Jialin Zhang <zhangjialin11@huawei.com>
+Tested-by: Anatoli Antonovitch <Anatoli.Antonovitch@amd.com>
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Cc: stable@vger.kernel.org # v4.19+
+Cc: Dan Stein <dstein@hpe.com>
+Cc: Ashok Raj <ashok.raj@intel.com>
+Cc: Alex Michon <amichon@kalrayinc.com>
+Cc: Xiongfeng Wang <wangxiongfeng2@huawei.com>
+Cc: Alex Williamson <alex.williamson@redhat.com>
+Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
+Cc: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/hotplug/pciehp_pci.c |   15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/pci/hotplug/pciehp_pci.c
++++ b/drivers/pci/hotplug/pciehp_pci.c
+@@ -63,7 +63,14 @@ int pciehp_configure_device(struct contr
+       pci_assign_unassigned_bridge_resources(bridge);
+       pcie_bus_configure_settings(parent);
++
++      /*
++       * Release reset_lock during driver binding
++       * to avoid AB-BA deadlock with device_lock.
++       */
++      up_read(&ctrl->reset_lock);
+       pci_bus_add_devices(parent);
++      down_read_nested(&ctrl->reset_lock, ctrl->depth);
+  out:
+       pci_unlock_rescan_remove();
+@@ -104,7 +111,15 @@ void pciehp_unconfigure_device(struct co
+       list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
+                                        bus_list) {
+               pci_dev_get(dev);
++
++              /*
++               * Release reset_lock during driver unbinding
++               * to avoid AB-BA deadlock with device_lock.
++               */
++              up_read(&ctrl->reset_lock);
+               pci_stop_and_remove_bus_device(dev);
++              down_read_nested(&ctrl->reset_lock, ctrl->depth);
++
+               /*
+                * Ensure that no new Requests will be generated from
+                * the device.
diff --git a/queue-5.4/pci-pciehp-use-down_read-write_nested-reset_lock-to-fix-lockdep-errors.patch b/queue-5.4/pci-pciehp-use-down_read-write_nested-reset_lock-to-fix-lockdep-errors.patch
new file mode 100644 (file)
index 0000000..04763a6
--- /dev/null
@@ -0,0 +1,186 @@
+From stable-owner@vger.kernel.org Tue May  9 12:50:59 2023
+From: Lukas Wunner <lukas@wunner.de>
+Date: Tue, 9 May 2023 12:41:09 +0200
+Subject: PCI: pciehp: Use down_read/write_nested(reset_lock) to fix lockdep errors
+To: stable@vger.kernel.org
+Cc: Anatoli.Antonovitch@amd.com, alex.williamson@redhat.com, amichon@kalrayinc.com, andrey2805@gmail.com, ashok.raj@intel.com, bhelgaas@google.com, dstein@hpe.com, ian.may@canonical.com, michael.haeuptle@hpe.com, mika.westerberg@linux.intel.com, rahul.kumar1@amd.com, sathyanarayanan.kuppuswamy@linux.intel.com, wangxiongfeng2@huawei.com, zhangjialin11@huawei.com
+Message-ID: <0296a234ed04adfec0f256b24fae929f6a268509.1683628574.git.lukas@wunner.de>
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+commit 085a9f43433f30cbe8a1ade62d9d7827c3217f4d upstream.
+
+Use down_read_nested() and down_write_nested() when taking the
+ctrl->reset_lock rw-sem, passing the number of PCIe hotplug controllers in
+the path to the PCI root bus as lock subclass parameter.
+
+This fixes the following false-positive lockdep report when unplugging a
+Lenovo X1C8 from a Lenovo 2nd gen TB3 dock:
+
+  pcieport 0000:06:01.0: pciehp: Slot(1): Link Down
+  pcieport 0000:06:01.0: pciehp: Slot(1): Card not present
+  ============================================
+  WARNING: possible recursive locking detected
+  5.16.0-rc2+ #621 Not tainted
+  --------------------------------------------
+  irq/124-pciehp/86 is trying to acquire lock:
+  ffff8e5ac4299ef8 (&ctrl->reset_lock){.+.+}-{3:3}, at: pciehp_check_presence+0x23/0x80
+
+  but task is already holding lock:
+  ffff8e5ac4298af8 (&ctrl->reset_lock){.+.+}-{3:3}, at: pciehp_ist+0xf3/0x180
+
+   other info that might help us debug this:
+   Possible unsafe locking scenario:
+
+        CPU0
+        ----
+    lock(&ctrl->reset_lock);
+    lock(&ctrl->reset_lock);
+
+   *** DEADLOCK ***
+
+   May be due to missing lock nesting notation
+
+  3 locks held by irq/124-pciehp/86:
+   #0: ffff8e5ac4298af8 (&ctrl->reset_lock){.+.+}-{3:3}, at: pciehp_ist+0xf3/0x180
+   #1: ffffffffa3b024e8 (pci_rescan_remove_lock){+.+.}-{3:3}, at: pciehp_unconfigure_device+0x31/0x110
+   #2: ffff8e5ac1ee2248 (&dev->mutex){....}-{3:3}, at: device_release_driver+0x1c/0x40
+
+  stack backtrace:
+  CPU: 4 PID: 86 Comm: irq/124-pciehp Not tainted 5.16.0-rc2+ #621
+  Hardware name: LENOVO 20U90SIT19/20U90SIT19, BIOS N2WET30W (1.20 ) 08/26/2021
+  Call Trace:
+   <TASK>
+   dump_stack_lvl+0x59/0x73
+   __lock_acquire.cold+0xc5/0x2c6
+   lock_acquire+0xb5/0x2b0
+   down_read+0x3e/0x50
+   pciehp_check_presence+0x23/0x80
+   pciehp_runtime_resume+0x5c/0xa0
+   device_for_each_child+0x45/0x70
+   pcie_port_device_runtime_resume+0x20/0x30
+   pci_pm_runtime_resume+0xa7/0xc0
+   __rpm_callback+0x41/0x110
+   rpm_callback+0x59/0x70
+   rpm_resume+0x512/0x7b0
+   __pm_runtime_resume+0x4a/0x90
+   __device_release_driver+0x28/0x240
+   device_release_driver+0x26/0x40
+   pci_stop_bus_device+0x68/0x90
+   pci_stop_bus_device+0x2c/0x90
+   pci_stop_and_remove_bus_device+0xe/0x20
+   pciehp_unconfigure_device+0x6c/0x110
+   pciehp_disable_slot+0x5b/0xe0
+   pciehp_handle_presence_or_link_change+0xc3/0x2f0
+   pciehp_ist+0x179/0x180
+
+This lockdep warning is triggered because with Thunderbolt, hotplug ports
+are nested. When removing multiple devices in a daisy-chain, each hotplug
+port's reset_lock may be acquired recursively. It's never the same lock, so
+the lockdep splat is a false positive.
+
+Because locks at the same hierarchy level are never acquired recursively, a
+per-level lockdep class is sufficient to fix the lockdep warning.
+
+The choice to use one lockdep subclass per pcie-hotplug controller in the
+path to the root-bus was made to conserve class keys because their number
+is limited and the complexity grows quadratically with number of keys
+according to Documentation/locking/lockdep-design.rst.
+
+Link: https://lore.kernel.org/linux-pci/20190402021933.GA2966@mit.edu/
+Link: https://lore.kernel.org/linux-pci/de684a28-9038-8fc6-27ca-3f6f2f6400d7@redhat.com/
+Link: https://lore.kernel.org/r/20211217141709.379663-1-hdegoede@redhat.com
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=208855
+Reported-by: "Theodore Ts'o" <tytso@mit.edu>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Lukas Wunner <lukas@wunner.de>
+Cc: stable@vger.kernel.org
+[lukas: backport to v5.4-stable]
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/hotplug/pciehp.h      |    3 +++
+ drivers/pci/hotplug/pciehp_core.c |    2 +-
+ drivers/pci/hotplug/pciehp_hpc.c  |   19 +++++++++++++++++--
+ 3 files changed, 21 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/hotplug/pciehp.h
++++ b/drivers/pci/hotplug/pciehp.h
+@@ -72,6 +72,8 @@ extern int pciehp_poll_time;
+  * @reset_lock: prevents access to the Data Link Layer Link Active bit in the
+  *    Link Status register and to the Presence Detect State bit in the Slot
+  *    Status register during a slot reset which may cause them to flap
++ * @depth: Number of additional hotplug ports in the path to the root bus,
++ *    used as lock subclass for @reset_lock
+  * @ist_running: flag to keep user request waiting while IRQ thread is running
+  * @request_result: result of last user request submitted to the IRQ thread
+  * @requester: wait queue to wake up on completion of user request,
+@@ -102,6 +104,7 @@ struct controller {
+       struct hotplug_slot hotplug_slot;       /* hotplug core interface */
+       struct rw_semaphore reset_lock;
++      unsigned int depth;
+       unsigned int ist_running;
+       int request_result;
+       wait_queue_head_t requester;
+--- a/drivers/pci/hotplug/pciehp_core.c
++++ b/drivers/pci/hotplug/pciehp_core.c
+@@ -165,7 +165,7 @@ static void pciehp_check_presence(struct
+ {
+       int occupied;
+-      down_read(&ctrl->reset_lock);
++      down_read_nested(&ctrl->reset_lock, ctrl->depth);
+       mutex_lock(&ctrl->state_lock);
+       occupied = pciehp_card_present_or_link_active(ctrl);
+--- a/drivers/pci/hotplug/pciehp_hpc.c
++++ b/drivers/pci/hotplug/pciehp_hpc.c
+@@ -674,7 +674,7 @@ static irqreturn_t pciehp_ist(int irq, v
+        * Disable requests have higher priority than Presence Detect Changed
+        * or Data Link Layer State Changed events.
+        */
+-      down_read(&ctrl->reset_lock);
++      down_read_nested(&ctrl->reset_lock, ctrl->depth);
+       if (events & DISABLE_SLOT)
+               pciehp_handle_disable_request(ctrl);
+       else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
+@@ -808,7 +808,7 @@ int pciehp_reset_slot(struct hotplug_slo
+       if (probe)
+               return 0;
+-      down_write(&ctrl->reset_lock);
++      down_write_nested(&ctrl->reset_lock, ctrl->depth);
+       if (!ATTN_BUTTN(ctrl)) {
+               ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
+@@ -864,6 +864,20 @@ static inline void dbg_ctrl(struct contr
+ #define FLAG(x, y)    (((x) & (y)) ? '+' : '-')
++static inline int pcie_hotplug_depth(struct pci_dev *dev)
++{
++      struct pci_bus *bus = dev->bus;
++      int depth = 0;
++
++      while (bus->parent) {
++              bus = bus->parent;
++              if (bus->self && bus->self->is_hotplug_bridge)
++                      depth++;
++      }
++
++      return depth;
++}
++
+ struct controller *pcie_init(struct pcie_device *dev)
+ {
+       struct controller *ctrl;
+@@ -877,6 +891,7 @@ struct controller *pcie_init(struct pcie
+               return NULL;
+       ctrl->pcie = dev;
++      ctrl->depth = pcie_hotplug_depth(dev->port);
+       pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
+       if (pdev->hotplug_user_indicators)
diff --git a/queue-5.4/serial-8250-fix-serial8250_tx_empty-race-with-dma-tx.patch b/queue-5.4/serial-8250-fix-serial8250_tx_empty-race-with-dma-tx.patch
new file mode 100644 (file)
index 0000000..d6f2123
--- /dev/null
@@ -0,0 +1,98 @@
+From stable-owner@vger.kernel.org Thu May 11 14:33:08 2023
+From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
+Date: Thu, 11 May 2023 15:32:44 +0300
+Subject: serial: 8250: Fix serial8250_tx_empty() race with DMA Tx
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
+Message-ID: <20230511123244.38514-2-ilpo.jarvinen@linux.intel.com>
+
+From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
+
+There's a potential race before THRE/TEMT deasserts when DMA Tx is
+starting up (or the next batch of continuous Tx is being submitted).
+This can lead to misdetecting Tx empty condition.
+
+It is entirely normal for THRE/TEMT to be set for some time after the
+DMA Tx had been setup in serial8250_tx_dma(). As Tx side is definitely
+not empty at that point, it seems incorrect for serial8250_tx_empty()
+claim Tx is empty.
+
+Fix the race by also checking in serial8250_tx_empty() whether there's
+DMA Tx active.
+
+Note: This fix only addresses in-kernel race mainly to make using
+TCSADRAIN/FLUSH robust. Userspace can still cause other races but they
+seem userspace concurrency control problems.
+
+Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
+Cc: stable@vger.kernel.org
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20230317113318.31327-3-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+(cherry picked from commit 146a37e05d620cef4ad430e5d1c9c077fe6fa76f)
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/8250/8250.h      |   12 ++++++++++++
+ drivers/tty/serial/8250/8250_port.c |   12 +++++++++---
+ 2 files changed, 21 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250.h
++++ b/drivers/tty/serial/8250/8250.h
+@@ -305,6 +305,13 @@ extern int serial8250_rx_dma(struct uart
+ extern void serial8250_rx_dma_flush(struct uart_8250_port *);
+ extern int serial8250_request_dma(struct uart_8250_port *);
+ extern void serial8250_release_dma(struct uart_8250_port *);
++
++static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
++{
++      struct uart_8250_dma *dma = p->dma;
++
++      return dma && dma->tx_running;
++}
+ #else
+ static inline int serial8250_tx_dma(struct uart_8250_port *p)
+ {
+@@ -320,6 +327,11 @@ static inline int serial8250_request_dma
+       return -1;
+ }
+ static inline void serial8250_release_dma(struct uart_8250_port *p) { }
++
++static inline bool serial8250_tx_dma_running(struct uart_8250_port *p)
++{
++      return false;
++}
+ #endif
+ static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -1922,19 +1922,25 @@ static int serial8250_tx_threshold_handl
+ static unsigned int serial8250_tx_empty(struct uart_port *port)
+ {
+       struct uart_8250_port *up = up_to_u8250p(port);
++      unsigned int result = 0;
+       unsigned long flags;
+       unsigned int lsr;
+       serial8250_rpm_get(up);
+       spin_lock_irqsave(&port->lock, flags);
+-      lsr = serial_port_in(port, UART_LSR);
+-      up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
++      if (!serial8250_tx_dma_running(up)) {
++              lsr = serial_port_in(port, UART_LSR);
++              up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
++
++              if ((lsr & BOTH_EMPTY) == BOTH_EMPTY)
++                      result = TIOCSER_TEMT;
++      }
+       spin_unlock_irqrestore(&port->lock, flags);
+       serial8250_rpm_put(up);
+-      return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
++      return result;
+ }
+ unsigned int serial8250_do_get_mctrl(struct uart_port *port)
index a72337bc69d158a7d04eac45201cd0b74e3aa460..d3cda07113e3ad0ab90a0855f40ddfb1f4aab058 100644 (file)
@@ -266,3 +266,8 @@ ext4-add-bounds-checking-in-get_max_inline_xattr_value_size.patch
 ext4-bail-out-of-ext4_xattr_ibody_get-fails-for-any-reason.patch
 ext4-remove-a-bug_on-in-ext4_mb_release_group_pa.patch
 ext4-fix-invalid-free-tracking-in-ext4_xattr_move_to_block.patch
+tty-prevent-writing-chars-during-tcsetattr-tcsadrain-flush.patch
+serial-8250-fix-serial8250_tx_empty-race-with-dma-tx.patch
+drbd-correctly-submit-flush-bio-on-barrier.patch
+pci-pciehp-use-down_read-write_nested-reset_lock-to-fix-lockdep-errors.patch
+pci-pciehp-fix-ab-ba-deadlock-between-reset_lock-and-device_lock.patch
diff --git a/queue-5.4/tty-prevent-writing-chars-during-tcsetattr-tcsadrain-flush.patch b/queue-5.4/tty-prevent-writing-chars-during-tcsetattr-tcsadrain-flush.patch
new file mode 100644 (file)
index 0000000..3b37956
--- /dev/null
@@ -0,0 +1,127 @@
+From stable-owner@vger.kernel.org Thu May 11 14:33:03 2023
+From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
+Date: Thu, 11 May 2023 15:32:43 +0300
+Subject: tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
+Message-ID: <20230511123244.38514-1-ilpo.jarvinen@linux.intel.com>
+
+From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
+
+If userspace races tcsetattr() with a write, the drained condition
+might not be guaranteed by the kernel. There is a race window after
+checking Tx is empty before tty_set_termios() takes termios_rwsem for
+write. During that race window, more characters can be queued by a
+racing writer.
+
+Any ongoing transmission might produce garbage during HW's
+->set_termios() call. The intent of TCSADRAIN/FLUSH seems to be
+preventing such a character corruption. If those flags are set, take
+tty's write lock to stop any writer before performing the lower layer
+Tx empty check and wait for the pending characters to be sent (if any).
+
+The initial wait for all-writers-done must be placed outside of tty's
+write lock to avoid deadlock which makes it impossible to use
+tty_wait_until_sent(). The write lock is retried if a racing write is
+detected.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20230317113318.31327-2-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+(cherry picked from commit 094fb49a2d0d6827c86d2e0840873e6db0c491d2)
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/tty_io.c    |    4 ++--
+ drivers/tty/tty_ioctl.c |   45 +++++++++++++++++++++++++++++++++------------
+ include/linux/tty.h     |    2 ++
+ 3 files changed, 37 insertions(+), 14 deletions(-)
+
+--- a/drivers/tty/tty_io.c
++++ b/drivers/tty/tty_io.c
+@@ -876,13 +876,13 @@ static ssize_t tty_read(struct file *fil
+       return i;
+ }
+-static void tty_write_unlock(struct tty_struct *tty)
++void tty_write_unlock(struct tty_struct *tty)
+ {
+       mutex_unlock(&tty->atomic_write_lock);
+       wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
+ }
+-static int tty_write_lock(struct tty_struct *tty, int ndelay)
++int tty_write_lock(struct tty_struct *tty, int ndelay)
+ {
+       if (!mutex_trylock(&tty->atomic_write_lock)) {
+               if (ndelay)
+--- a/drivers/tty/tty_ioctl.c
++++ b/drivers/tty/tty_ioctl.c
+@@ -397,21 +397,42 @@ static int set_termios(struct tty_struct
+       tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
+       tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
+-      ld = tty_ldisc_ref(tty);
++      if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
++retry_write_wait:
++              retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));
++              if (retval < 0)
++                      return retval;
+-      if (ld != NULL) {
+-              if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
+-                      ld->ops->flush_buffer(tty);
+-              tty_ldisc_deref(ld);
+-      }
++              if (tty_write_lock(tty, 0) < 0)
++                      goto retry_write_wait;
+-      if (opt & TERMIOS_WAIT) {
+-              tty_wait_until_sent(tty, 0);
+-              if (signal_pending(current))
+-                      return -ERESTARTSYS;
+-      }
++              /* Racing writer? */
++              if (tty_chars_in_buffer(tty)) {
++                      tty_write_unlock(tty);
++                      goto retry_write_wait;
++              }
++
++              ld = tty_ldisc_ref(tty);
++              if (ld != NULL) {
++                      if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
++                              ld->ops->flush_buffer(tty);
++                      tty_ldisc_deref(ld);
++              }
+-      tty_set_termios(tty, &tmp_termios);
++              if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
++                      tty->ops->wait_until_sent(tty, 0);
++                      if (signal_pending(current)) {
++                              tty_write_unlock(tty);
++                              return -ERESTARTSYS;
++                      }
++              }
++
++              tty_set_termios(tty, &tmp_termios);
++
++              tty_write_unlock(tty);
++      } else {
++              tty_set_termios(tty, &tmp_termios);
++      }
+       /* FIXME: Arguably if tmp_termios == tty->termios AND the
+          actual requested termios was not tmp_termios then we may
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -480,6 +480,8 @@ extern void __stop_tty(struct tty_struct
+ extern void stop_tty(struct tty_struct *tty);
+ extern void __start_tty(struct tty_struct *tty);
+ extern void start_tty(struct tty_struct *tty);
++void tty_write_unlock(struct tty_struct *tty);
++int tty_write_lock(struct tty_struct *tty, int ndelay);
+ extern int tty_register_driver(struct tty_driver *driver);
+ extern int tty_unregister_driver(struct tty_driver *driver);
+ extern struct device *tty_register_device(struct tty_driver *driver,