mirror of
https://github.com/openwrt/openwrt.git
synced 2025-10-30 07:49:23 +08:00
generic: 6.12: add pending patch to address PCI sysfs creation entry race
Add pending patch to address PCI sysfs creation entry race observed on ipq806x. This is to handle a kernel warning on creating the same sysfs entry multiple times. All affected patch automatically refreshed. Link: https://github.com/openwrt/openwrt/pull/18989 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
@ -0,0 +1,142 @@
|
||||
From d33941523a8379e30070374b133b28a2077dcef8 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 13 Oct 2025 20:45:25 +0200
|
||||
Subject: [PATCH] PCI/sysfs: enforce single creation of sysfs entry for pdev
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
In some specific scenario it's possible that the
|
||||
pci_create_resource_files() gets called multiple times and the created
|
||||
entry actually gets wrongly deleted with extreme case of having a NULL
|
||||
pointer dereference when the PCI is removed.
|
||||
|
||||
This mainly happen due to bad timing where the PCI bus is adding PCI
|
||||
devices and at the same time the sysfs code is adding the entry causing
|
||||
double execution of the pci_create_resource_files function and kernel
|
||||
WARNING.
|
||||
|
||||
To be more precise there is a race between the late_initcall of
|
||||
pci-sysfs with pci_sysfs_init and PCI bus.c pci_bus_add_device that also
|
||||
call pci_create_sysfs_dev_files.
|
||||
|
||||
With correct amount of ""luck"" (or better say bad luck)
|
||||
pci_create_sysfs_dev_files in bus.c might be called with pci_sysfs_init
|
||||
is executing the loop.
|
||||
|
||||
This has been reported multiple times and on multiple system, like imx6
|
||||
system, ipq806x systems...
|
||||
|
||||
To address this, imlement multiple improvement to the implementation:
|
||||
1. Add a bool to pci_dev to flag when sysfs entry are created
|
||||
(sysfs_init)
|
||||
2. Implement a simple completion to wait pci_sysfs_init execution.
|
||||
3. Permit additional call of pci_create_sysfs_dev_files only after
|
||||
pci_sysfs_init has finished.
|
||||
|
||||
With such logic in place, we address al kind of timing problem with
|
||||
minimal change to any driver.
|
||||
|
||||
A notice worth to mention is that the remove function are not affected
|
||||
by this as the pci_remove_resource_files have enough check in place to
|
||||
always work and it's always called by pci_stop_dev.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Reported-by: Krzysztof Hałasa <khalasa@piap.pl>
|
||||
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215515
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/pci/pci-sysfs.c | 34 +++++++++++++++++++++++++++++-----
|
||||
include/linux/pci.h | 1 +
|
||||
2 files changed, 30 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/pci/pci-sysfs.c
|
||||
+++ b/drivers/pci/pci-sysfs.c
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
+#include <linux/completion.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pci.h>
|
||||
@@ -36,6 +37,7 @@
|
||||
#endif
|
||||
|
||||
static int sysfs_initialized; /* = 0 */
|
||||
+static DECLARE_COMPLETION(sysfs_init_completion);
|
||||
|
||||
/* show configuration fields */
|
||||
#define pci_config_attr(field, format_string) \
|
||||
@@ -1533,12 +1535,32 @@ static const struct attribute_group pci_
|
||||
.is_visible = resource_resize_is_visible,
|
||||
};
|
||||
|
||||
+static int __pci_create_sysfs_dev_files(struct pci_dev *pdev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = pci_create_resource_files(pdev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* on success set sysfs correctly created */
|
||||
+ pdev->sysfs_init = true;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
|
||||
{
|
||||
if (!sysfs_initialized)
|
||||
return -EACCES;
|
||||
|
||||
- return pci_create_resource_files(pdev);
|
||||
+ /* sysfs entry already created */
|
||||
+ if (pdev->sysfs_init)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* wait for pci_sysfs_init */
|
||||
+ wait_for_completion(&sysfs_init_completion);
|
||||
+
|
||||
+ return __pci_create_sysfs_dev_files(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1559,21 +1581,23 @@ static int __init pci_sysfs_init(void)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
struct pci_bus *pbus = NULL;
|
||||
- int retval;
|
||||
+ int retval = 0;
|
||||
|
||||
sysfs_initialized = 1;
|
||||
for_each_pci_dev(pdev) {
|
||||
- retval = pci_create_sysfs_dev_files(pdev);
|
||||
+ retval = __pci_create_sysfs_dev_files(pdev);
|
||||
if (retval) {
|
||||
pci_dev_put(pdev);
|
||||
- return retval;
|
||||
+ goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
while ((pbus = pci_find_next_bus(pbus)))
|
||||
pci_create_legacy_files(pbus);
|
||||
|
||||
- return 0;
|
||||
+exit:
|
||||
+ complete_all(&sysfs_init_completion);
|
||||
+ return retval;
|
||||
}
|
||||
late_initcall(pci_sysfs_init);
|
||||
|
||||
--- a/include/linux/pci.h
|
||||
+++ b/include/linux/pci.h
|
||||
@@ -484,6 +484,7 @@ struct pci_dev {
|
||||
unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */
|
||||
pci_dev_flags_t dev_flags;
|
||||
atomic_t enable_cnt; /* pci_enable_device has been called */
|
||||
+ bool sysfs_init; /* sysfs entry has been created */
|
||||
|
||||
spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */
|
||||
u32 saved_config_space[16]; /* Config space saved at suspend time */
|
||||
@ -5518,7 +5518,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
(transaction layer end-to-end CRC checking).
|
||||
--- a/include/linux/pci.h
|
||||
+++ b/include/linux/pci.h
|
||||
@@ -1643,6 +1643,8 @@ void pci_walk_bus_locked(struct pci_bus
|
||||
@@ -1644,6 +1644,8 @@ void pci_walk_bus_locked(struct pci_bus
|
||||
void *userdata);
|
||||
int pci_cfg_space_size(struct pci_dev *dev);
|
||||
unsigned char pci_bus_max_busnr(struct pci_bus *bus);
|
||||
|
||||
Reference in New Issue
Block a user