summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:01 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:01 -0500
commit0e0e66a31be93e4e0e9caf670220f2e562df4e64 (patch)
treea81a3d6c8d8730f9b322f63e16a4219e071ec383
parent3a81c660546352577a35280898d9aa20d8a91e3d (diff)
parent8857f6578b001bcf5f53c8c6a3936647f05291a8 (diff)
downloadlwn-0e0e66a31be93e4e0e9caf670220f2e562df4e64.tar.gz
lwn-0e0e66a31be93e4e0e9caf670220f2e562df4e64.zip
Merge branch 'pci/procfs'
- Fix race between pci_proc_init() and pci_bus_add_device() (Krzysztof Wilczyński) * pci/procfs: PCI/proc: Fix race between pci_proc_init() and pci_bus_add_device()
-rw-r--r--drivers/pci/proc.c79
1 files changed, 56 insertions, 23 deletions
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index ce36e35681e8..71ad289fcb8e 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -18,6 +18,7 @@
#include "pci.h"
static int proc_initialized; /* = 0 */
+static DEFINE_MUTEX(pci_proc_lock);
static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
@@ -416,40 +417,64 @@ static const struct seq_operations proc_bus_pci_devices_op = {
static struct proc_dir_entry *proc_bus_pci_dir;
-int pci_proc_attach_device(struct pci_dev *dev)
+static int __pci_proc_attach_bus(struct pci_bus *bus)
{
- struct pci_bus *bus = dev->bus;
- struct proc_dir_entry *e;
+ struct proc_dir_entry *dir;
char name[16];
+ lockdep_assert_held(&pci_proc_lock);
+
if (!proc_initialized)
return -EACCES;
- if (!bus->procdir) {
- if (pci_proc_domain(bus)) {
- sprintf(name, "%04x:%02x", pci_domain_nr(bus),
- bus->number);
- } else {
- sprintf(name, "%02x", bus->number);
- }
- bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
- if (!bus->procdir)
- return -ENOMEM;
- }
+ if (bus->procdir)
+ return 0;
+
+ if (pci_proc_domain(bus))
+ sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number);
+ else
+ sprintf(name, "%02x", bus->number);
+
+ dir = proc_mkdir(name, proc_bus_pci_dir);
+ if (!dir)
+ return -ENOMEM;
+
+ bus->procdir = dir;
+
+ return 0;
+}
+
+int pci_proc_attach_device(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->bus;
+ struct proc_dir_entry *entry;
+ char name[16];
+ int ret;
+
+ guard(mutex)(&pci_proc_lock);
+
+ if (dev->procent)
+ return 0;
+
+ ret = __pci_proc_attach_bus(bus);
+ if (ret)
+ return ret;
sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
- e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir,
- &proc_bus_pci_ops, dev);
- if (!e)
+ entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR,
+ bus->procdir, &proc_bus_pci_ops, dev);
+ if (!entry)
return -ENOMEM;
- proc_set_size(e, dev->cfg_size);
- dev->procent = e;
+
+ proc_set_size(entry, dev->cfg_size);
+ dev->procent = entry;
return 0;
}
int pci_proc_detach_device(struct pci_dev *dev)
{
+ guard(mutex)(&pci_proc_lock);
proc_remove(dev->procent);
dev->procent = NULL;
return 0;
@@ -457,19 +482,27 @@ int pci_proc_detach_device(struct pci_dev *dev)
int pci_proc_detach_bus(struct pci_bus *bus)
{
+ guard(mutex)(&pci_proc_lock);
proc_remove(bus->procdir);
+ bus->procdir = NULL;
return 0;
}
static int __init pci_proc_init(void)
{
struct pci_dev *dev = NULL;
- proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
- proc_create_seq("devices", 0, proc_bus_pci_dir,
- &proc_bus_pci_devices_op);
- proc_initialized = 1;
+
+ scoped_guard(mutex, &pci_proc_lock) {
+ proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
+ proc_create_seq("devices", 0, proc_bus_pci_dir,
+ &proc_bus_pci_devices_op);
+ proc_initialized = 1;
+ }
+
+ pci_lock_rescan_remove();
for_each_pci_dev(dev)
pci_proc_attach_device(dev);
+ pci_unlock_rescan_remove();
return 0;
}