From 610929e119b2166167f4f8fce85408472e77a16a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 01/11] PCI: move host bridge-related code to host-bridge.c Move host bridge-related code from probe.c to a new host-bridge.c. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 2 +- drivers/pci/host-bridge.c | 93 +++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 2 + drivers/pci/probe.c | 81 +--------------------------------- 4 files changed, 97 insertions(+), 81 deletions(-) create mode 100644 drivers/pci/host-bridge.c diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 083a49fee56a..2c224edae1ac 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -2,7 +2,7 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o remove.o pci.o \ +obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ irq.o vpd.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c new file mode 100644 index 000000000000..28b24a5375d4 --- /dev/null +++ b/drivers/pci/host-bridge.c @@ -0,0 +1,93 @@ +/* + * host bridge related code + */ + +#include +#include +#include +#include + +#include "pci.h" + +static LIST_HEAD(pci_host_bridges); + +void add_to_pci_host_bridges(struct pci_host_bridge *bridge) +{ + list_add_tail(&bridge->list, &pci_host_bridges); +} + +static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus; + struct pci_host_bridge *bridge; + + bus = dev->bus; + while (bus->parent) + bus = bus->parent; + + list_for_each_entry(bridge, &pci_host_bridges, list) { + if (bridge->bus == bus) + return bridge; + } + + return NULL; +} + +static bool resource_contains(struct resource *res1, struct resource *res2) +{ + return res1->start <= res2->start && res1->end >= res2->end; +} + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + if (resource_contains(window->res, res)) { + offset = window->offset; + break; + } + } + + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +static bool region_contains(struct pci_bus_region *region1, + struct pci_bus_region *region2) +{ + return region1->start <= region2->start && region1->end >= region2->end; +} + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + struct pci_bus_region bus_region; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + bus_region.start = window->res->start - window->offset; + bus_region.end = window->res->end - window->offset; + + if (region_contains(&bus_region, region)) { + offset = window->offset; + break; + } + } + + res->start = region->start + offset; + res->end = region->end + offset; +} +EXPORT_SYMBOL(pcibios_bus_to_resource); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..c695a92cca13 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -231,6 +231,8 @@ static inline int pci_ari_enabled(struct pci_bus *bus) void pci_reassigndev_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); +void add_to_pci_host_bridges(struct pci_host_bridge *bridge); + /* Single Root I/O Virtualization */ struct pci_sriov { int pos; /* capability position */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5e1ca3c58a7d..bcea52b90e0d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,13 +15,10 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 -static LIST_HEAD(pci_host_bridges); - /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); - static int find_anything(struct device *dev, void *data) { return 1; @@ -44,82 +41,6 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); -static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) -{ - struct pci_bus *bus; - struct pci_host_bridge *bridge; - - bus = dev->bus; - while (bus->parent) - bus = bus->parent; - - list_for_each_entry(bridge, &pci_host_bridges, list) { - if (bridge->bus == bus) - return bridge; - } - - return NULL; -} - -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - struct pci_host_bridge *bridge = pci_host_bridge(dev); - struct pci_host_bridge_window *window; - resource_size_t offset = 0; - - list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - - if (resource_contains(window->res, res)) { - offset = window->offset; - break; - } - } - - region->start = res->start - offset; - region->end = res->end - offset; -} -EXPORT_SYMBOL(pcibios_resource_to_bus); - -static bool region_contains(struct pci_bus_region *region1, - struct pci_bus_region *region2) -{ - return region1->start <= region2->start && region1->end >= region2->end; -} - -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - struct pci_host_bridge *bridge = pci_host_bridge(dev); - struct pci_host_bridge_window *window; - struct pci_bus_region bus_region; - resource_size_t offset = 0; - - list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - - bus_region.start = window->res->start - window->offset; - bus_region.end = window->res->end - window->offset; - - if (region_contains(&bus_region, region)) { - offset = window->offset; - break; - } - } - - res->start = region->start + offset; - res->end = region->end + offset; -} -EXPORT_SYMBOL(pcibios_bus_to_resource); - /* * PCI Bus Class */ @@ -1732,7 +1653,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, } down_write(&pci_bus_sem); - list_add_tail(&bridge->list, &pci_host_bridges); + add_to_pci_host_bridges(bridge); list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); From baa495d9de2af97310128bfc0e365a813b63d5bb Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 02/11] x86/PCI: fix memleak with get_current_resources() In pci_scan_acpi_root(), when pci_use_crs is set, get_current_resources() is used to get pci_root_info, and it will allocate name and resource array. Later if pci_create_root_bus() can not create bus (could be already there...) it will only free bus res list, but the name and res array is not freed. Let get_current_resource() take info pointer instead of using local info. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 49 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index ed2835e148b5..a99b7d75f5ca 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -315,49 +315,55 @@ static void add_resources(struct pci_root_info *info) } } +static void free_pci_root_info(struct pci_root_info *info) +{ + kfree(info->name); + kfree(info->res); + memset(info, 0, sizeof(struct pci_root_info)); +} + static void -get_current_resources(struct acpi_device *device, int busnum, +get_current_resources(struct pci_root_info *info, + struct acpi_device *device, int busnum, int domain, struct list_head *resources) { - struct pci_root_info info; size_t size; - info.bridge = device; - info.res_num = 0; - info.resources = resources; + info->bridge = device; + info->res_num = 0; + info->resources = resources; acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, - &info); - if (!info.res_num) + info); + if (!info->res_num) return; - size = sizeof(*info.res) * info.res_num; - info.res = kmalloc(size, GFP_KERNEL); - if (!info.res) + size = sizeof(*info->res) * info->res_num; + info->res = kmalloc(size, GFP_KERNEL); + if (!info->res) return; - info.name = kasprintf(GFP_KERNEL, "PCI Bus %04x:%02x", domain, busnum); - if (!info.name) + info->name = kasprintf(GFP_KERNEL, "PCI Bus %04x:%02x", domain, busnum); + if (!info->name) goto name_alloc_fail; - info.res_num = 0; + info->res_num = 0; acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, - &info); + info); if (pci_use_crs) { - add_resources(&info); + add_resources(info); return; } - kfree(info.name); - name_alloc_fail: - kfree(info.res); + free_pci_root_info(info); } struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; + struct pci_root_info info; int domain = root->segment; int busnum = root->secondary.start; LIST_HEAD(resources); @@ -402,6 +408,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) sd->domain = domain; sd->node = node; + memset(&info, 0, sizeof(struct pci_root_info)); /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -415,7 +422,8 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) memcpy(bus->sysdata, sd, sizeof(*sd)); kfree(sd); } else { - get_current_resources(device, busnum, domain, &resources); + get_current_resources(&info, device, busnum, domain, + &resources); /* * _CRS with no apertures is normal, so only fall back to @@ -429,6 +437,9 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) bus->subordinate = pci_scan_child_bus(bus); else pci_free_resource_list(&resources); + + if (!bus && pci_use_crs) + free_pci_root_info(&info); } /* After the PCI-E bus has been walked and all devices discovered, From 459f58ce51e2e11235b7bb4b1732ebf3c17d86f7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 03/11] PCI: rename pci_host_bridge() to find_pci_root_bridge() pci_host_bridge() looks like a C++ constructor. Also separate find_pci_root_bus() out. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/host-bridge.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 28b24a5375d4..c49a1c482cfb 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -16,15 +16,22 @@ void add_to_pci_host_bridges(struct pci_host_bridge *bridge) list_add_tail(&bridge->list, &pci_host_bridges); } -static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +static struct pci_bus *find_pci_root_bus(struct pci_dev *dev) { struct pci_bus *bus; - struct pci_host_bridge *bridge; bus = dev->bus; while (bus->parent) bus = bus->parent; + return bus; +} + +static struct pci_host_bridge *find_pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus = find_pci_root_bus(dev); + struct pci_host_bridge *bridge; + list_for_each_entry(bridge, &pci_host_bridges, list) { if (bridge->bus == bus) return bridge; @@ -41,7 +48,7 @@ static bool resource_contains(struct resource *res1, struct resource *res2) void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res) { - struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(dev); struct pci_host_bridge_window *window; resource_size_t offset = 0; @@ -69,12 +76,13 @@ static bool region_contains(struct pci_bus_region *region1, void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, struct pci_bus_region *region) { - struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(dev); struct pci_host_bridge_window *window; - struct pci_bus_region bus_region; resource_size_t offset = 0; list_for_each_entry(window, &bridge->windows, list) { + struct pci_bus_region bus_region; + if (resource_type(res) != resource_type(window->res)) continue; From 7b54366358008241f88228f02cc80ab352265eac Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 04/11] PCI: add generic device into pci_host_bridge struct Use that device for pci_root_bus bridge pointer. Use pci_release_bus_bridge_dev() to release allocated pci_host_bridge in remove path. Use root bus bridge pointer to get host bridge pointer instead of searching host bridge list. That leaves the host bridge list unused, so remove it. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/host-bridge.c | 15 +-------- drivers/pci/pci.h | 2 -- drivers/pci/probe.c | 68 +++++++++++++++++++++------------------ include/linux/pci.h | 4 ++- 4 files changed, 40 insertions(+), 49 deletions(-) diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index c49a1c482cfb..122df80592c3 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -9,13 +9,6 @@ #include "pci.h" -static LIST_HEAD(pci_host_bridges); - -void add_to_pci_host_bridges(struct pci_host_bridge *bridge) -{ - list_add_tail(&bridge->list, &pci_host_bridges); -} - static struct pci_bus *find_pci_root_bus(struct pci_dev *dev) { struct pci_bus *bus; @@ -30,14 +23,8 @@ static struct pci_bus *find_pci_root_bus(struct pci_dev *dev) static struct pci_host_bridge *find_pci_host_bridge(struct pci_dev *dev) { struct pci_bus *bus = find_pci_root_bus(dev); - struct pci_host_bridge *bridge; - list_for_each_entry(bridge, &pci_host_bridges, list) { - if (bridge->bus == bus) - return bridge; - } - - return NULL; + return to_pci_host_bridge(bus->bridge); } static bool resource_contains(struct resource *res1, struct resource *res2) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index c695a92cca13..e4943479b234 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -231,8 +231,6 @@ static inline int pci_ari_enabled(struct pci_bus *bus) void pci_reassigndev_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); -void add_to_pci_host_bridges(struct pci_host_bridge *bridge); - /* Single Root I/O Virtualization */ struct pci_sriov { int pos; /* capability position */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bcea52b90e0d..8d291ee15257 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -422,6 +422,19 @@ static struct pci_bus * pci_alloc_bus(void) return b; } +static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b) +{ + struct pci_host_bridge *bridge; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (bridge) { + INIT_LIST_HEAD(&bridge->windows); + bridge->bus = b; + } + + return bridge; +} + static unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ @@ -1122,7 +1135,13 @@ int pci_cfg_space_size(struct pci_dev *dev) static void pci_release_bus_bridge_dev(struct device *dev) { - kfree(dev); + struct pci_host_bridge *bridge = to_pci_host_bridge(dev); + + /* TODO: need to free window->res */ + + pci_free_resource_list(&bridge->windows); + + kfree(bridge); } struct pci_dev *alloc_pci_dev(void) @@ -1571,28 +1590,19 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; - struct device *dev; struct pci_host_bridge_window *window, *n; struct resource *res; resource_size_t offset; char bus_addr[64]; char *fmt; - bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return NULL; b = pci_alloc_bus(); if (!b) - goto err_bus; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - goto err_dev; + return NULL; b->sysdata = sysdata; b->ops = ops; - b2 = pci_find_bus(pci_domain_nr(b), bus); if (b2) { /* If we already got to this bus through a different bridge, ignore it */ @@ -1600,13 +1610,17 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; } - dev->parent = parent; - dev->release = pci_release_bus_bridge_dev; - dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); - error = device_register(dev); + bridge = pci_alloc_host_bridge(b); + if (!bridge) + goto err_out; + + bridge->dev.parent = parent; + bridge->dev.release = pci_release_bus_bridge_dev; + dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + error = device_register(&bridge->dev); if (error) - goto dev_reg_err; - b->bridge = get_device(dev); + goto bridge_dev_reg_err; + b->bridge = get_device(&bridge->dev); device_enable_async_suspend(b->bridge); pci_set_bus_of_node(b); @@ -1625,9 +1639,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; - bridge->bus = b; - INIT_LIST_HEAD(&bridge->windows); - if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else @@ -1653,25 +1664,18 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, } down_write(&pci_bus_sem); - add_to_pci_host_bridges(bridge); list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); return b; class_dev_reg_err: - device_unregister(dev); -dev_reg_err: - down_write(&pci_bus_sem); - list_del(&bridge->list); - list_del(&b->node); - up_write(&pci_bus_sem); -err_out: - kfree(dev); -err_dev: - kfree(b); -err_bus: + put_device(&bridge->dev); + device_unregister(&bridge->dev); +bridge_dev_reg_err: kfree(bridge); +err_out: + kfree(b); return NULL; } diff --git a/include/linux/pci.h b/include/linux/pci.h index e444f5b49118..8f4f29d2b606 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -375,11 +375,13 @@ struct pci_host_bridge_window { }; struct pci_host_bridge { - struct list_head list; + struct device dev; struct pci_bus *bus; /* root bus */ struct list_head windows; /* pci_host_bridge_windows */ }; +#define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev) + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for From 4fa2649a01a4357a82dcc60ef8fb7b8c441e64ed Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 05/11] PCI: add host bridge release support We need a hook to release host bridge resources allocated when creating root bus. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/host-bridge.c | 8 ++++++++ drivers/pci/probe.c | 3 ++- include/linux/pci.h | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 122df80592c3..a68dc613a5be 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -27,6 +27,14 @@ static struct pci_host_bridge *find_pci_host_bridge(struct pci_dev *dev) return to_pci_host_bridge(bus->bridge); } +void pci_set_host_bridge_release(struct pci_host_bridge *bridge, + void (*release_fn)(struct pci_host_bridge *), + void *release_data) +{ + bridge->release_fn = release_fn; + bridge->release_data = release_data; +} + static bool resource_contains(struct resource *res1, struct resource *res2) { return res1->start <= res2->start && res1->end >= res2->end; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8d291ee15257..4c2f22668ea7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1137,7 +1137,8 @@ static void pci_release_bus_bridge_dev(struct device *dev) { struct pci_host_bridge *bridge = to_pci_host_bridge(dev); - /* TODO: need to free window->res */ + if (bridge->release_fn) + bridge->release_fn(bridge); pci_free_resource_list(&bridge->windows); diff --git a/include/linux/pci.h b/include/linux/pci.h index 8f4f29d2b606..17b7b5b01b4a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -378,9 +378,14 @@ struct pci_host_bridge { struct device dev; struct pci_bus *bus; /* root bus */ struct list_head windows; /* pci_host_bridge_windows */ + void (*release_fn)(struct pci_host_bridge *); + void *release_data; }; #define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev) +void pci_set_host_bridge_release(struct pci_host_bridge *bridge, + void (*release_fn)(struct pci_host_bridge *), + void *release_data); /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond From 9a03d28d9490b5a04f8b1d98fc08067c6f4f6189 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 06/11] x86/PCI: refactor get_current_resources() Rename get_current_resources() to probe_pci_root_info. 1. Remove resource list head from pci_root_info 2. Make get_current_resources() not pass resources 3. Rename get_current_resources() to probe_pci_root_info() Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index a99b7d75f5ca..a858c1d9af53 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -12,7 +12,6 @@ struct pci_root_info { char *name; unsigned int res_num; struct resource *res; - struct list_head *resources; int busnum; }; @@ -287,7 +286,8 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type) } } -static void add_resources(struct pci_root_info *info) +static void add_resources(struct pci_root_info *info, + struct list_head *resources) { int i; struct resource *res, *root, *conflict; @@ -311,7 +311,7 @@ static void add_resources(struct pci_root_info *info) "ignoring host bridge window %pR (conflicts with %s %pR)\n", res, conflict->name, conflict); else - pci_add_resource(info->resources, res); + pci_add_resource(resources, res); } } @@ -323,41 +323,30 @@ static void free_pci_root_info(struct pci_root_info *info) } static void -get_current_resources(struct pci_root_info *info, - struct acpi_device *device, int busnum, - int domain, struct list_head *resources) +probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, + int busnum, int domain) { size_t size; info->bridge = device; info->res_num = 0; - info->resources = resources; acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, info); if (!info->res_num) return; size = sizeof(*info->res) * info->res_num; + info->res_num = 0; info->res = kmalloc(size, GFP_KERNEL); if (!info->res) return; info->name = kasprintf(GFP_KERNEL, "PCI Bus %04x:%02x", domain, busnum); if (!info->name) - goto name_alloc_fail; + return; - info->res_num = 0; acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, info); - - if (pci_use_crs) { - add_resources(info); - - return; - } - -name_alloc_fail: - free_pci_root_info(info); } struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) @@ -422,15 +411,18 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) memcpy(bus->sysdata, sd, sizeof(*sd)); kfree(sd); } else { - get_current_resources(&info, device, busnum, domain, - &resources); + probe_pci_root_info(&info, device, busnum, domain); /* * _CRS with no apertures is normal, so only fall back to * defaults or native bridge info if we're ignoring _CRS. */ - if (!pci_use_crs) + if (pci_use_crs) + add_resources(&info, &resources); + else { + free_pci_root_info(&info); x86_pci_root_bus_resources(busnum, &resources); + } bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, &resources); if (bus) From fd3b0c1ea482e863d6a2556b6686e35bec7a4f1c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 07/11] x86/PCI: add host bridge resource release for _CRS path 1. Allocate pci_root_info instead of using stack. We need to pass around info for release function. 2. Add release_pci_root_info 3. Set x86 host bridge release function to make sure root bridge related resources get freed during root bus removal. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 63 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index a858c1d9af53..2b74a161d215 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -315,11 +315,40 @@ static void add_resources(struct pci_root_info *info, } } -static void free_pci_root_info(struct pci_root_info *info) +static void free_pci_root_info_res(struct pci_root_info *info) { kfree(info->name); kfree(info->res); - memset(info, 0, sizeof(struct pci_root_info)); + info->res = NULL; + info->res_num = 0; +} + +static void __release_pci_root_info(struct pci_root_info *info) +{ + int i; + struct resource *res; + + for (i = 0; i < info->res_num; i++) { + res = &info->res[i]; + + if (!res->parent) + continue; + + if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + continue; + + release_resource(res); + } + + free_pci_root_info_res(info); + + kfree(info); +} +static void release_pci_root_info(struct pci_host_bridge *bridge) +{ + struct pci_root_info *info = bridge->release_data; + + __release_pci_root_info(info); } static void @@ -352,7 +381,7 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; - struct pci_root_info info; + struct pci_root_info *info = NULL; int domain = root->segment; int busnum = root->secondary.start; LIST_HEAD(resources); @@ -397,7 +426,13 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) sd->domain = domain; sd->node = node; - memset(&info, 0, sizeof(struct pci_root_info)); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + kfree(sd); + printk(KERN_WARNING "pci_bus %04x:%02x: " + "ignored (out of memory)\n", domain, busnum); + return NULL; + } /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -409,29 +444,33 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) * be replaced by sd. */ memcpy(bus->sysdata, sd, sizeof(*sd)); + kfree(info); kfree(sd); } else { - probe_pci_root_info(&info, device, busnum, domain); + probe_pci_root_info(info, device, busnum, domain); /* * _CRS with no apertures is normal, so only fall back to * defaults or native bridge info if we're ignoring _CRS. */ if (pci_use_crs) - add_resources(&info, &resources); + add_resources(info, &resources); else { - free_pci_root_info(&info); + free_pci_root_info_res(info); x86_pci_root_bus_resources(busnum, &resources); } + bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, &resources); - if (bus) + if (bus) { bus->subordinate = pci_scan_child_bus(bus); - else + pci_set_host_bridge_release( + to_pci_host_bridge(bus->bridge), + release_pci_root_info, info); + } else { pci_free_resource_list(&resources); - - if (!bus && pci_use_crs) - free_pci_root_info(&info); + __release_pci_root_info(info); + } } /* After the PCI-E bus has been walked and all devices discovered, From fe05725ff97530e26109a0c3d52cef7fff326e15 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 08/11] x86/PCI: embed name into pci_root_info struct We now keep the pci_root_info struct for the entire lifetime of the host bridge, so just embed the name in the struct rather than allocating it separately. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 2b74a161d215..23e7361b1747 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -9,7 +9,7 @@ struct pci_root_info { struct acpi_device *bridge; - char *name; + char name[16]; unsigned int res_num; struct resource *res; int busnum; @@ -317,7 +317,6 @@ static void add_resources(struct pci_root_info *info, static void free_pci_root_info_res(struct pci_root_info *info) { - kfree(info->name); kfree(info->res); info->res = NULL; info->res_num = 0; @@ -370,9 +369,7 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, if (!info->res) return; - info->name = kasprintf(GFP_KERNEL, "PCI Bus %04x:%02x", domain, busnum); - if (!info->name) - return; + sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, info); From 35cb05e5bdac209cfdfafbe50d89ee7069cb6237 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: [PATCH 09/11] x86/PCI: embed pci_sysdata into pci_root_info on ACPI path Embed the x86 struct pci_sysdata in the struct pci_root_info so it will be automatically freed in the remove path. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 23e7361b1747..8a17b23f8c84 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -13,6 +13,7 @@ struct pci_root_info { unsigned int res_num; struct resource *res; int busnum; + struct pci_sysdata sd; }; static bool pci_use_crs = true; @@ -410,26 +411,16 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) if (node != -1 && !node_online(node)) node = -1; - /* Allocate per-root-bus (not per bus) arch-specific data. - * TODO: leak; this memory is never freed. - * It's arguable whether it's worth the trouble to care. - */ - sd = kzalloc(sizeof(*sd), GFP_KERNEL); - if (!sd) { + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { printk(KERN_WARNING "pci_bus %04x:%02x: " "ignored (out of memory)\n", domain, busnum); return NULL; } + sd = &info->sd; sd->domain = domain; sd->node = node; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - kfree(sd); - printk(KERN_WARNING "pci_bus %04x:%02x: " - "ignored (out of memory)\n", domain, busnum); - return NULL; - } /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -442,7 +433,6 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) */ memcpy(bus->sysdata, sd, sizeof(*sd)); kfree(info); - kfree(sd); } else { probe_pci_root_info(info, device, busnum, domain); @@ -484,9 +474,6 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) } } - if (!bus) - kfree(sd); - if (bus && node != -1) { #ifdef CONFIG_ACPI_NUMA if (pxm >= 0) From d28e5ac2a07e27638cf5ac061721b7969e17fe78 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:54 -0700 Subject: [PATCH 10/11] x86/PCI: dynamically allocate pci_root_info for native host bridge drivers This dynamically allocates struct pci_root_info instead of using a static array. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/amd_bus.c | 76 +++++++++++++------------------------ arch/x86/pci/broadcom_bus.c | 12 ++---- arch/x86/pci/bus_numa.c | 73 +++++++++++++++++++++++------------ arch/x86/pci/bus_numa.h | 18 +++++---- 4 files changed, 90 insertions(+), 89 deletions(-) diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c index 0567df3890e1..459a7316375c 100644 --- a/arch/x86/pci/amd_bus.c +++ b/arch/x86/pci/amd_bus.c @@ -32,6 +32,18 @@ static struct pci_hostbridge_probe pci_probes[] __initdata = { #define RANGE_NUM 16 +static struct pci_root_info __init *find_pci_root_info(int node, int link) +{ + struct pci_root_info *info; + + /* find the position */ + list_for_each_entry(info, &pci_root_infos, list) + if (info->node == node && info->link == link) + return info; + + return NULL; +} + /** * early_fill_mp_bus_to_node() * called before pcibios_scan_root and pci_scan_bus @@ -50,7 +62,6 @@ static int __init early_fill_mp_bus_info(void) int def_link; struct pci_root_info *info; u32 reg; - struct resource *res; u64 start; u64 end; struct range range[RANGE_NUM]; @@ -86,7 +97,6 @@ static int __init early_fill_mp_bus_info(void) if (!found) return 0; - pci_root_num = 0; for (i = 0; i < 4; i++) { int min_bus; int max_bus; @@ -105,13 +115,8 @@ static int __init early_fill_mp_bus_info(void) #endif link = (reg >> 8) & 0x03; - info = &pci_root_info[pci_root_num]; - info->bus_min = min_bus; - info->bus_max = max_bus; - info->node = node; - info->link = link; + info = alloc_pci_root_info(min_bus, max_bus, node, link); sprintf(info->name, "PCI Bus #%02x", min_bus); - pci_root_num++; } /* get the default node and link for left over res */ @@ -134,16 +139,10 @@ static int __init early_fill_mp_bus_info(void) link = (reg >> 4) & 0x03; end = (reg & 0xfff000) | 0xfff; - /* find the position */ - for (j = 0; j < pci_root_num; j++) { - info = &pci_root_info[j]; - if (info->node == node && info->link == link) - break; - } - if (j == pci_root_num) + info = find_pci_root_info(node, link); + if (!info) continue; /* not found */ - info = &pci_root_info[j]; printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", node, link, start, end); @@ -155,13 +154,8 @@ static int __init early_fill_mp_bus_info(void) } /* add left over io port range to def node/link, [0, 0xffff] */ /* find the position */ - for (j = 0; j < pci_root_num; j++) { - info = &pci_root_info[j]; - if (info->node == def_node && info->link == def_link) - break; - } - if (j < pci_root_num) { - info = &pci_root_info[j]; + info = find_pci_root_info(def_node, def_link); + if (info) { for (i = 0; i < RANGE_NUM; i++) { if (!range[i].end) continue; @@ -214,16 +208,10 @@ static int __init early_fill_mp_bus_info(void) end <<= 8; end |= 0xffff; - /* find the position */ - for (j = 0; j < pci_root_num; j++) { - info = &pci_root_info[j]; - if (info->node == node && info->link == link) - break; - } - if (j == pci_root_num) - continue; /* not found */ + info = find_pci_root_info(node, link); - info = &pci_root_info[j]; + if (!info) + continue; printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]", node, link, start, end); @@ -291,14 +279,8 @@ static int __init early_fill_mp_bus_info(void) * add left over mmio range to def node/link ? * that is tricky, just record range in from start_min to 4G */ - for (j = 0; j < pci_root_num; j++) { - info = &pci_root_info[j]; - if (info->node == def_node && info->link == def_link) - break; - } - if (j < pci_root_num) { - info = &pci_root_info[j]; - + info = find_pci_root_info(def_node, def_link); + if (info) { for (i = 0; i < RANGE_NUM; i++) { if (!range[i].end) continue; @@ -309,20 +291,16 @@ static int __init early_fill_mp_bus_info(void) } } - for (i = 0; i < pci_root_num; i++) { - int res_num; + list_for_each_entry(info, &pci_root_infos, list) { int busnum; + struct pci_root_res *root_res; - info = &pci_root_info[i]; - res_num = info->res_num; busnum = info->bus_min; printk(KERN_DEBUG "bus: [%02x, %02x] on node %x link %x\n", info->bus_min, info->bus_max, info->node, info->link); - for (j = 0; j < res_num; j++) { - res = &info->res[j]; - printk(KERN_DEBUG "bus: %02x index %x %pR\n", - busnum, j, res); - } + list_for_each_entry(root_res, &info->resources, list) + printk(KERN_DEBUG "bus: %02x %pR\n", + busnum, &root_res->res); } return 0; diff --git a/arch/x86/pci/broadcom_bus.c b/arch/x86/pci/broadcom_bus.c index f3a7c569a403..614392ced7d6 100644 --- a/arch/x86/pci/broadcom_bus.c +++ b/arch/x86/pci/broadcom_bus.c @@ -22,19 +22,15 @@ static void __init cnb20le_res(u8 bus, u8 slot, u8 func) { struct pci_root_info *info; + struct pci_root_res *root_res; struct resource res; u16 word1, word2; u8 fbus, lbus; - int i; - - info = &pci_root_info[pci_root_num]; - pci_root_num++; /* read the PCI bus numbers */ fbus = read_pci_config_byte(bus, slot, func, 0x44); lbus = read_pci_config_byte(bus, slot, func, 0x45); - info->bus_min = fbus; - info->bus_max = lbus; + info = alloc_pci_root_info(fbus, lbus, 0, 0); /* * Add the legacy IDE ports on bus 0 @@ -86,8 +82,8 @@ static void __init cnb20le_res(u8 bus, u8 slot, u8 func) res.flags = IORESOURCE_BUS; printk(KERN_INFO "CNB20LE PCI Host Bridge (domain 0000 %pR)\n", &res); - for (i = 0; i < info->res_num; i++) - printk(KERN_INFO "host bridge window %pR\n", &info->res[i]); + list_for_each_entry(root_res, &info->resources, list) + printk(KERN_INFO "host bridge window %pR\n", &root_res->res); } static int __init broadcom_postcore_init(void) diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c index fd3f65510e9d..306579f7d0fd 100644 --- a/arch/x86/pci/bus_numa.c +++ b/arch/x86/pci/bus_numa.c @@ -4,35 +4,38 @@ #include "bus_numa.h" -int pci_root_num; -struct pci_root_info pci_root_info[PCI_ROOT_NR]; +LIST_HEAD(pci_root_infos); + +static struct pci_root_info *x86_find_pci_root_info(int bus) +{ + struct pci_root_info *info; + + if (list_empty(&pci_root_infos)) + return NULL; + + list_for_each_entry(info, &pci_root_infos, list) + if (info->bus_min == bus) + return info; + + return NULL; +} void x86_pci_root_bus_resources(int bus, struct list_head *resources) { - int i; - int j; - struct pci_root_info *info; + struct pci_root_info *info = x86_find_pci_root_info(bus); + struct pci_root_res *root_res; - if (!pci_root_num) - goto default_resources; - - for (i = 0; i < pci_root_num; i++) { - if (pci_root_info[i].bus_min == bus) - break; - } - - if (i == pci_root_num) + if (!info) goto default_resources; printk(KERN_DEBUG "PCI: root bus %02x: hardware-probed resources\n", bus); - info = &pci_root_info[i]; - for (j = 0; j < info->res_num; j++) { + list_for_each_entry(root_res, &info->resources, list) { struct resource *res; struct resource *root; - res = &info->res[j]; + res = &root_res->res; pci_add_resource(resources, res); if (res->flags & IORESOURCE_IO) root = &ioport_resource; @@ -53,11 +56,32 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) pci_add_resource(resources, &iomem_resource); } +struct pci_root_info __init *alloc_pci_root_info(int bus_min, int bus_max, + int node, int link) +{ + struct pci_root_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + + if (!info) + return info; + + INIT_LIST_HEAD(&info->resources); + info->bus_min = bus_min; + info->bus_max = bus_max; + info->node = node; + info->link = link; + + list_add_tail(&info->list, &pci_root_infos); + + return info; +} + void __devinit update_res(struct pci_root_info *info, resource_size_t start, resource_size_t end, unsigned long flags, int merge) { - int i; struct resource *res; + struct pci_root_res *root_res; if (start > end) return; @@ -69,11 +93,11 @@ void __devinit update_res(struct pci_root_info *info, resource_size_t start, goto addit; /* try to merge it with old one */ - for (i = 0; i < info->res_num; i++) { + list_for_each_entry(root_res, &info->resources, list) { resource_size_t final_start, final_end; resource_size_t common_start, common_end; - res = &info->res[i]; + res = &root_res->res; if (res->flags != flags) continue; @@ -93,14 +117,15 @@ void __devinit update_res(struct pci_root_info *info, resource_size_t start, addit: /* need to add that */ - if (info->res_num >= RES_NUM) + root_res = kzalloc(sizeof(*root_res), GFP_KERNEL); + if (!root_res) return; - res = &info->res[info->res_num]; + res = &root_res->res; res->name = info->name; res->flags = flags; res->start = start; res->end = end; - res->child = NULL; - info->res_num++; + + list_add_tail(&root_res->list, &info->resources); } diff --git a/arch/x86/pci/bus_numa.h b/arch/x86/pci/bus_numa.h index 804a4b40c31a..226a466b2b2b 100644 --- a/arch/x86/pci/bus_numa.h +++ b/arch/x86/pci/bus_numa.h @@ -4,22 +4,24 @@ * sub bus (transparent) will use entres from 3 to store extra from * root, so need to make sure we have enough slot there. */ -#define RES_NUM 16 +struct pci_root_res { + struct list_head list; + struct resource res; +}; + struct pci_root_info { + struct list_head list; char name[12]; - unsigned int res_num; - struct resource res[RES_NUM]; + struct list_head resources; int bus_min; int bus_max; int node; int link; }; -/* 4 at this time, it may become to 32 */ -#define PCI_ROOT_NR 4 -extern int pci_root_num; -extern struct pci_root_info pci_root_info[PCI_ROOT_NR]; - +extern struct list_head pci_root_infos; +struct pci_root_info *alloc_pci_root_info(int bus_min, int bus_max, + int node, int link); extern void update_res(struct pci_root_info *info, resource_size_t start, resource_size_t end, unsigned long flags, int merge); #endif From c57ca65a6ea3171370cbb3010e5a3aea7399a5e1 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:54 -0700 Subject: [PATCH 11/11] x86/PCI: merge pcibios_scan_root() and pci_scan_bus_on_node() pcibios_scan_root() and pci_scan_bus_on_node() were almost identical, so this patch merges them. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/common.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 323481e06ef8..8e04ec591543 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -430,9 +430,7 @@ void __init dmi_check_pciprobe(void) struct pci_bus * __devinit pcibios_scan_root(int busnum) { - LIST_HEAD(resources); struct pci_bus *bus = NULL; - struct pci_sysdata *sd; while ((bus = pci_find_next_bus(bus)) != NULL) { if (bus->number == busnum) { @@ -441,28 +439,10 @@ struct pci_bus * __devinit pcibios_scan_root(int busnum) } } - /* Allocate per-root-bus (not per bus) arch-specific data. - * TODO: leak; this memory is never freed. - * It's arguable whether it's worth the trouble to care. - */ - sd = kzalloc(sizeof(*sd), GFP_KERNEL); - if (!sd) { - printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum); - return NULL; - } - - sd->node = get_mp_bus_to_node(busnum); - - printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum); - x86_pci_root_bus_resources(busnum, &resources); - bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, sd, &resources); - if (!bus) { - pci_free_resource_list(&resources); - kfree(sd); - } - - return bus; + return pci_scan_bus_on_node(busnum, &pci_root_ops, + get_mp_bus_to_node(busnum)); } + void __init pcibios_set_cache_line_size(void) { struct cpuinfo_x86 *c = &boot_cpu_data; @@ -656,6 +636,7 @@ struct pci_bus * __devinit pci_scan_bus_on_node(int busno, struct pci_ops *ops, } sd->node = node; x86_pci_root_bus_resources(busno, &resources); + printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busno); bus = pci_scan_root_bus(NULL, busno, ops, sd, &resources); if (!bus) { pci_free_resource_list(&resources);