x86/PCI: MMCONFIG: manage pci_mmcfg_region as a list, not a table

This changes pci_mmcfg_region from a table to a list, to make it easier
to add and remove MMCONFIG regions for PCI host bridge hotplug.

Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index 7aa2ed8..0b7c316 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -122,6 +122,7 @@
 #define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
 
 struct pci_mmcfg_region {
+	struct list_head list;
 	struct resource res;
 	u64 address;
 	char __iomem *virt;
@@ -134,8 +135,7 @@
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 
-extern struct pci_mmcfg_region *pci_mmcfg_config;
-extern int pci_mmcfg_config_num;
+extern struct list_head pci_mmcfg_list;
 
 #define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 6eeeac0..2709aa8 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -16,7 +16,6 @@
 #include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
 #include <linux/dmi.h>
-#include <linux/sort.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
@@ -26,53 +25,58 @@
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static int __initdata pci_mmcfg_resources_inserted;
 
+LIST_HEAD(pci_mmcfg_list);
+
 static __init void free_all_mmcfg(void)
 {
-	int i;
-	struct pci_mmcfg_region *cfg;
+	struct pci_mmcfg_region *cfg, *tmp;
 
 	pci_mmcfg_arch_free();
-	for (i = 0; i < pci_mmcfg_config_num; i++) {
-		cfg = &pci_mmcfg_config[i];
+	list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) {
 		if (cfg->res.parent)
 			release_resource(&cfg->res);
+		list_del(&cfg->list);
+		kfree(cfg);
 	}
-	pci_mmcfg_config_num = 0;
-	kfree(pci_mmcfg_config);
-	pci_mmcfg_config = NULL;
+}
+
+static __init 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) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail(&new->list, &pci_mmcfg_list);
 }
 
 static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
 							int end, u64 addr)
 {
 	struct pci_mmcfg_region *new;
-	int new_num = pci_mmcfg_config_num + 1;
-	int i = pci_mmcfg_config_num;
 	int num_buses;
 	struct resource *res;
 
 	if (addr == 0)
 		return NULL;
 
-	new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
 	if (!new)
 		return NULL;
 
-	if (pci_mmcfg_config) {
-		memcpy(new, pci_mmcfg_config,
-			 sizeof(pci_mmcfg_config[0]) * new_num);
-		kfree(pci_mmcfg_config);
-	}
-	pci_mmcfg_config = new;
-	pci_mmcfg_config_num++;
-
-	new = &pci_mmcfg_config[i];
-
 	new->address = addr;
 	new->segment = segment;
 	new->start_bus = start;
 	new->end_bus = end;
 
+	list_add_sorted(new);
+
 	num_buses = end - start + 1;
 	res = &new->res;
 	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
@@ -82,7 +86,7 @@
 		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
 	res->name = new->name;
 
-	return &pci_mmcfg_config[i];
+	return new;
 }
 
 static const char __init *pci_mmcfg_e7520(void)
@@ -214,7 +218,7 @@
 	/*
 	 * do check if amd fam10h already took over
 	 */
-	if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
+	if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
 		return NULL;
 
 	mcp55_checked = true;
@@ -275,44 +279,26 @@
 	  0x0369, pci_mmcfg_nvidia_mcp55 },
 };
 
-static int __init cmp_mmcfg(const void *x1, const void *x2)
-{
-	const struct pci_mmcfg_region *m1 = x1;
-	const struct pci_mmcfg_region *m2 = x2;
-	int start1, start2;
-
-	start1 = m1->start_bus;
-	start2 = m2->start_bus;
-
-	return start1 - start2;
-}
-
 static void __init pci_mmcfg_check_end_bus_number(void)
 {
-	int i;
 	struct pci_mmcfg_region *cfg, *cfgx;
 
-	/* sort them at first */
-	sort(pci_mmcfg_config, pci_mmcfg_config_num,
-		 sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
-
 	/* last one*/
-	if (pci_mmcfg_config_num > 0) {
-		i = pci_mmcfg_config_num - 1;
-		cfg = &pci_mmcfg_config[i];
+	cfg = list_entry(pci_mmcfg_list.prev, typeof(*cfg), list);
+	if (cfg)
 		if (cfg->end_bus < cfg->start_bus)
 			cfg->end_bus = 255;
-	}
+
+	if (list_is_singular(&pci_mmcfg_list))
+		return;
 
 	/* don't overlap please */
-	for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
-		cfg = &pci_mmcfg_config[i];
-		cfgx = &pci_mmcfg_config[i+1];
-
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 		if (cfg->end_bus < cfg->start_bus)
 			cfg->end_bus = 255;
 
-		if (cfg->end_bus >= cfgx->start_bus)
+		cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
+		if (cfg != cfgx && cfg->end_bus >= cfgx->start_bus)
 			cfg->end_bus = cfgx->start_bus - 1;
 	}
 }
@@ -350,18 +336,15 @@
 	/* some end_bus_number is crazy, fix it */
 	pci_mmcfg_check_end_bus_number();
 
-	return pci_mmcfg_config_num != 0;
+	return !list_empty(&pci_mmcfg_list);
 }
 
 static void __init pci_mmcfg_insert_resources(void)
 {
-	int i;
 	struct pci_mmcfg_region *cfg;
 
-	for (i = 0; i < pci_mmcfg_config_num; i++) {
-		cfg = &pci_mmcfg_config[i];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
 		insert_resource(&iomem_resource, &cfg->res);
-	}
 
 	/* Mark that the resources have been inserted. */
 	pci_mmcfg_resources_inserted = 1;
@@ -482,18 +465,15 @@
 	struct pci_mmcfg_region *cfg;
 	int i;
 
-	if (pci_mmcfg_config_num == 0)
-		return;
-
-	for (i = 0; i < pci_mmcfg_config_num; i++) {
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 		int valid = 0;
 
-		cfg = &pci_mmcfg_config[i];
 		printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx "
 		       "segment %hu buses %u - %u\n",
 		       i, (unsigned long)cfg->address, cfg->segment,
 		       (unsigned int)cfg->start_bus,
 		       (unsigned int)cfg->end_bus);
+		i++;
 
 		if (!early && !acpi_disabled)
 			valid = is_mmconf_reserved(is_acpi_reserved, i, cfg, 0);
@@ -524,10 +504,6 @@
 
 static int __initdata known_bridge;
 
-/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
-struct pci_mmcfg_region *pci_mmcfg_config;
-int pci_mmcfg_config_num;
-
 static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
 					struct acpi_mcfg_allocation *cfg)
 {
@@ -620,7 +596,7 @@
 
 	pci_mmcfg_reject_broken(early);
 
-	if (pci_mmcfg_config_num == 0)
+	if (list_empty(&pci_mmcfg_list))
 		return;
 
 	if (pci_mmcfg_arch_init())
@@ -652,7 +628,7 @@
 	 */
 	if ((pci_mmcfg_resources_inserted == 1) ||
 	    (pci_probe & PCI_PROBE_MMCONF) == 0 ||
-	    (pci_mmcfg_config_num == 0))
+	    list_empty(&pci_mmcfg_list))
 		return 1;
 
 	/*
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index a3cee53..c04523e 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -28,15 +28,12 @@
 static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
 {
 	struct pci_mmcfg_region *cfg;
-	int cfg_num;
 
-	for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
-		cfg = &pci_mmcfg_config[cfg_num];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
 		if (cfg->segment == seg &&
 		    (cfg->start_bus <= bus) &&
 		    (cfg->end_bus >= bus))
 			return cfg->address;
-	}
 
 	/* Fall back to type 0 */
 	return 0;
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index 78fa05c..ed1f479 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -14,16 +14,13 @@
 
 static char __iomem *get_virt(unsigned int seg, unsigned bus)
 {
-	int i;
 	struct pci_mmcfg_region *cfg;
 
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		cfg = &pci_mmcfg_config[i];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
 		if (cfg->segment == seg &&
 		    (cfg->start_bus <= bus) &&
 		    (cfg->end_bus >= bus))
 			return cfg->virt;
-	}
 
 	/* Fall back to type 0 */
 	return NULL;
@@ -122,11 +119,9 @@
 
 int __init pci_mmcfg_arch_init(void)
 {
-	int i;
 	struct pci_mmcfg_region *cfg;
 
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		cfg = &pci_mmcfg_config[i];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 		cfg->virt = mcfg_ioremap(cfg);
 		if (!cfg->virt) {
 			printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
@@ -142,11 +137,9 @@
 
 void __init pci_mmcfg_arch_free(void)
 {
-	int i;
 	struct pci_mmcfg_region *cfg;
 
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		cfg = &pci_mmcfg_config[i];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 		if (cfg->virt) {
 			iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
 			cfg->virt = NULL;