Skip to content

Commit

Permalink
x86/PCI: use RCU list to protect mmconfig list
Browse files Browse the repository at this point in the history
Use RCU list to protect mmconfig list from dynamic change
when supporting PCI host bridge hotplug.

Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jiang Liu <liuj97@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Jiang Liu authored and Bjorn Helgaas committed Jun 22, 2012
1 parent 846e402 commit 376f70a
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 10 deletions.
18 changes: 12 additions & 6 deletions arch/x86/pci/mmconfig-shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <linux/bitmap.h>
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/rculist.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <asm/acpi.h>
Expand All @@ -25,6 +27,7 @@

/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted;
static DEFINE_MUTEX(pci_mmcfg_lock);

LIST_HEAD(pci_mmcfg_list);

Expand All @@ -45,20 +48,20 @@ static __init void free_all_mmcfg(void)
pci_mmconfig_remove(cfg);
}

static __init void list_add_sorted(struct pci_mmcfg_region *new)
static __devinit void list_add_sorted(struct pci_mmcfg_region *new)
{
struct pci_mmcfg_region *cfg;

/* keep list sorted by segment and starting bus number */
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
if (cfg->segment > new->segment ||
(cfg->segment == new->segment &&
cfg->start_bus >= new->start_bus)) {
list_add_tail(&new->list, &cfg->list);
list_add_tail_rcu(&new->list, &cfg->list);
return;
}
}
list_add_tail(&new->list, &pci_mmcfg_list);
list_add_tail_rcu(&new->list, &pci_mmcfg_list);
}

static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment,
Expand Down Expand Up @@ -101,8 +104,11 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
struct pci_mmcfg_region *new;

new = pci_mmconfig_alloc(segment, start, end, addr);
if (new)
if (new) {
mutex_lock(&pci_mmcfg_lock);
list_add_sorted(new);
mutex_unlock(&pci_mmcfg_lock);
}

return new;
}
Expand All @@ -111,7 +117,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
{
struct pci_mmcfg_region *cfg;

list_for_each_entry(cfg, &pci_mmcfg_list, list)
list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
if (cfg->segment == segment &&
cfg->start_bus <= bus && bus <= cfg->end_bus)
return cfg;
Expand Down
13 changes: 11 additions & 2 deletions arch/x86/pci/mmconfig_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <linux/pci.h>
#include <linux/init.h>
#include <linux/rcupdate.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <acpi/acpi.h>
Expand Down Expand Up @@ -60,9 +61,12 @@ err: *value = -1;
return -EINVAL;
}

rcu_read_lock();
base = get_base_addr(seg, bus, devfn);
if (!base)
if (!base) {
rcu_read_unlock();
goto err;
}

raw_spin_lock_irqsave(&pci_config_lock, flags);

Expand All @@ -80,6 +84,7 @@ err: *value = -1;
break;
}
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();

return 0;
}
Expand All @@ -93,9 +98,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;

rcu_read_lock();
base = get_base_addr(seg, bus, devfn);
if (!base)
if (!base) {
rcu_read_unlock();
return -EINVAL;
}

raw_spin_lock_irqsave(&pci_config_lock, flags);

Expand All @@ -113,6 +121,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
break;
}
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();

return 0;
}
Expand Down
13 changes: 11 additions & 2 deletions arch/x86/pci/mmconfig_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>

Expand All @@ -34,9 +35,12 @@ err: *value = -1;
return -EINVAL;
}

rcu_read_lock();
addr = pci_dev_base(seg, bus, devfn);
if (!addr)
if (!addr) {
rcu_read_unlock();
goto err;
}

switch (len) {
case 1:
Expand All @@ -49,6 +53,7 @@ err: *value = -1;
*value = mmio_config_readl(addr + reg);
break;
}
rcu_read_unlock();

return 0;
}
Expand All @@ -62,9 +67,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
return -EINVAL;

rcu_read_lock();
addr = pci_dev_base(seg, bus, devfn);
if (!addr)
if (!addr) {
rcu_read_unlock();
return -EINVAL;
}

switch (len) {
case 1:
Expand All @@ -77,6 +85,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
mmio_config_writel(addr + reg, value);
break;
}
rcu_read_unlock();

return 0;
}
Expand Down

0 comments on commit 376f70a

Please sign in to comment.