]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - arch/x86/pci/mmconfig-shared.c
x86/PCI: prepare pci_mmcfg_check_reserved() to be called at runtime
[mirror_ubuntu-hirsute-kernel.git] / arch / x86 / pci / mmconfig-shared.c
index 301e325992f68451659127a7ac46ed0cf3f5c485..15a7abf5139cec3257c6b154a0734eb6fa0b1fbd 100644 (file)
@@ -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>
@@ -25,6 +27,8 @@
 
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static int __initdata pci_mmcfg_resources_inserted;
+static bool pci_mmcfg_running_state;
+static DEFINE_MUTEX(pci_mmcfg_lock);
 
 LIST_HEAD(pci_mmcfg_list);
 
@@ -45,24 +49,25 @@ 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 __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
-                                                       int end, u64 addr)
+static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment,
+                                                            int start,
+                                                            int end, u64 addr)
 {
        struct pci_mmcfg_region *new;
        struct resource *res;
@@ -79,8 +84,6 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
        new->start_bus = start;
        new->end_bus = end;
 
-       list_add_sorted(new);
-
        res = &new->res;
        res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
        res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
@@ -96,11 +99,26 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
        return new;
 }
 
+static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+                                                       int end, u64 addr)
+{
+       struct pci_mmcfg_region *new;
+
+       new = pci_mmconfig_alloc(segment, start, end, addr);
+       if (new) {
+               mutex_lock(&pci_mmcfg_lock);
+               list_add_sorted(new);
+               mutex_unlock(&pci_mmcfg_lock);
+       }
+
+       return new;
+}
+
 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;
@@ -358,14 +376,15 @@ static void __init pci_mmcfg_insert_resources(void)
        struct pci_mmcfg_region *cfg;
 
        list_for_each_entry(cfg, &pci_mmcfg_list, list)
-               insert_resource(&iomem_resource, &cfg->res);
+               if (!cfg->res.parent)
+                       insert_resource(&iomem_resource, &cfg->res);
 
        /* Mark that the resources have been inserted. */
        pci_mmcfg_resources_inserted = 1;
 }
 
-static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
-                                             void *data)
+static acpi_status __devinit check_mcfg_resource(struct acpi_resource *res,
+                                                void *data)
 {
        struct resource *mcfg_res = data;
        struct acpi_resource_address64 address;
@@ -401,8 +420,8 @@ static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
        return AE_OK;
 }
 
-static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
-               void *context, void **rv)
+static acpi_status __devinit find_mboard_resource(acpi_handle handle, u32 lvl,
+                                                 void *context, void **rv)
 {
        struct resource *mcfg_res = context;
 
@@ -415,7 +434,7 @@ static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
        return AE_OK;
 }
 
-static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used)
+static int __devinit is_acpi_reserved(u64 start, u64 end, unsigned not_used)
 {
        struct resource mcfg_res;
 
@@ -434,13 +453,15 @@ static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used)
 
 typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
 
-static int __init is_mmconf_reserved(check_reserved_t is_reserved,
-                                   struct pci_mmcfg_region *cfg, int with_e820)
+static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
+                                   struct pci_mmcfg_region *cfg,
+                                   struct device *dev, int with_e820)
 {
        u64 addr = cfg->res.start;
        u64 size = resource_size(&cfg->res);
        u64 old_size = size;
-       int valid = 0, num_buses;
+       int num_buses;
+       char *method = with_e820 ? "E820" : "ACPI motherboard resources";
 
        while (!is_reserved(addr, addr + size, E820_RESERVED)) {
                size >>= 1;
@@ -448,30 +469,77 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved,
                        break;
        }
 
-       if (size >= (16UL<<20) || size == old_size) {
-               printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
-                      &cfg->res,
-                      with_e820 ? "E820" : "ACPI motherboard resources");
-               valid = 1;
-
-               if (old_size != size) {
-                       /* update end_bus */
-                       cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
-                       num_buses = cfg->end_bus - cfg->start_bus + 1;
-                       cfg->res.end = cfg->res.start +
-                           PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
-                       snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
-                                "PCI MMCONFIG %04x [bus %02x-%02x]",
-                                cfg->segment, cfg->start_bus, cfg->end_bus);
+       if (size < (16UL<<20) && size != old_size)
+               return 0;
+
+       if (dev)
+               dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
+                        &cfg->res, method);
+       else
+               printk(KERN_INFO PREFIX
+                      "MMCONFIG at %pR reserved in %s\n",
+                      &cfg->res, method);
+
+       if (old_size != size) {
+               /* update end_bus */
+               cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
+               num_buses = cfg->end_bus - cfg->start_bus + 1;
+               cfg->res.end = cfg->res.start +
+                   PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
+               snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+                        "PCI MMCONFIG %04x [bus %02x-%02x]",
+                        cfg->segment, cfg->start_bus, cfg->end_bus);
+
+               if (dev)
+                       dev_info(dev,
+                               "MMCONFIG "
+                               "at %pR (base %#lx) (size reduced!)\n",
+                               &cfg->res, (unsigned long) cfg->address);
+               else
                        printk(KERN_INFO PREFIX
-                              "MMCONFIG for %04x [bus%02x-%02x] "
-                              "at %pR (base %#lx) (size reduced!)\n",
-                              cfg->segment, cfg->start_bus, cfg->end_bus,
-                              &cfg->res, (unsigned long) cfg->address);
-               }
+                               "MMCONFIG for %04x [bus%02x-%02x] "
+                               "at %pR (base %#lx) (size reduced!)\n",
+                               cfg->segment, cfg->start_bus, cfg->end_bus,
+                               &cfg->res, (unsigned long) cfg->address);
        }
 
-       return valid;
+       return 1;
+}
+
+static int __ref pci_mmcfg_check_reserved(struct device *dev,
+                 struct pci_mmcfg_region *cfg, int early)
+{
+       if (!early && !acpi_disabled) {
+               if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
+                       return 1;
+
+               if (dev)
+                       dev_info(dev, FW_INFO
+                                "MMCONFIG at %pR not reserved in "
+                                "ACPI motherboard resources\n",
+                                &cfg->res);
+               else
+                       printk(KERN_INFO FW_INFO PREFIX
+                              "MMCONFIG at %pR not reserved in "
+                              "ACPI motherboard resources\n",
+                              &cfg->res);
+       }
+
+       /*
+        * e820_all_mapped() is marked as __init.
+        * All entries from ACPI MCFG table have been checked at boot time.
+        * For MCFG information constructed from hotpluggable host bridge's
+        * _CBA method, just assume it's reserved.
+        */
+       if (pci_mmcfg_running_state)
+               return 1;
+
+       /* Don't try to do this check unless configuration
+          type 1 is available. how about type 2 ?*/
+       if (raw_pci_ops)
+               return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1);
+
+       return 0;
 }
 
 static void __init pci_mmcfg_reject_broken(int early)
@@ -479,34 +547,12 @@ static void __init pci_mmcfg_reject_broken(int early)
        struct pci_mmcfg_region *cfg;
 
        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
-               int valid = 0;
-
-               if (!early && !acpi_disabled) {
-                       valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
-
-                       if (valid)
-                               continue;
-                       else
-                               printk(KERN_ERR FW_BUG PREFIX
-                                      "MMCONFIG at %pR not reserved in "
-                                      "ACPI motherboard resources\n",
-                                      &cfg->res);
+               if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
+                       printk(KERN_INFO PREFIX "not using MMCONFIG\n");
+                       free_all_mmcfg();
+                       return;
                }
-
-               /* Don't try to do this check unless configuration
-                  type 1 is available. how about type 2 ?*/
-               if (raw_pci_ops)
-                       valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
-
-               if (!valid)
-                       goto reject;
        }
-
-       return;
-
-reject:
-       printk(KERN_INFO PREFIX "not using MMCONFIG\n");
-       free_all_mmcfg();
 }
 
 static int __initdata known_bridge;
@@ -640,6 +686,8 @@ void __init pci_mmcfg_late_init(void)
 
 static int __init pci_mmcfg_late_insert_resources(void)
 {
+       pci_mmcfg_running_state = true;
+
        /*
         * If resources are already inserted or we are not using MMCONFIG,
         * don't insert the resources.