From 610929e119b2166167f4f8fce85408472e77a16a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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); -- cgit v1.2.3 From baa495d9de2af97310128bfc0e365a813b63d5bb Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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, -- cgit v1.2.3 From 459f58ce51e2e11235b7bb4b1732ebf3c17d86f7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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; -- cgit v1.2.3 From 7b54366358008241f88228f02cc80ab352265eac Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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 | 66 +++++++++++++++++++++++++---------------------- include/linux/pci.h | 4 ++- 4 files changed, 39 insertions(+), 48 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); + put_device(&bridge->dev); + device_unregister(&bridge->dev); +bridge_dev_reg_err: + kfree(bridge); err_out: - kfree(dev); -err_dev: kfree(b); -err_bus: - kfree(bridge); 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 -- cgit v1.2.3 From 4fa2649a01a4357a82dcc60ef8fb7b8c441e64ed Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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 -- cgit v1.2.3 From 9a03d28d9490b5a04f8b1d98fc08067c6f4f6189 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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) -- cgit v1.2.3 From fd3b0c1ea482e863d6a2556b6686e35bec7a4f1c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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, -- cgit v1.2.3 From fe05725ff97530e26109a0c3d52cef7fff326e15 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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); -- cgit v1.2.3 From 35cb05e5bdac209cfdfafbe50d89ee7069cb6237 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:53 -0700 Subject: 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) -- cgit v1.2.3 From d28e5ac2a07e27638cf5ac061721b7969e17fe78 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:54 -0700 Subject: 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 | 69 +++++++++++++++++++++++++++------------- arch/x86/pci/bus_numa.h | 18 ++++++----- 4 files changed, 88 insertions(+), 87 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); -void x86_pci_root_bus_resources(int bus, struct list_head *resources) +static struct pci_root_info *x86_find_pci_root_info(int bus) { - int i; - int j; struct pci_root_info *info; - if (!pci_root_num) - goto default_resources; + if (list_empty(&pci_root_infos)) + return NULL; - for (i = 0; i < pci_root_num; i++) { - if (pci_root_info[i].bus_min == bus) - break; - } + list_for_each_entry(info, &pci_root_infos, list) + if (info->bus_min == bus) + return info; + + return NULL; +} - if (i == pci_root_num) +void x86_pci_root_bus_resources(int bus, struct list_head *resources) +{ + struct pci_root_info *info = x86_find_pci_root_info(bus); + struct pci_root_res *root_res; + + 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 @@ default_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 -- cgit v1.2.3 From c57ca65a6ea3171370cbb3010e5a3aea7399a5e1 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 2 Apr 2012 18:31:54 -0700 Subject: 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); -- cgit v1.2.3 From 284f5f9dbac170b054c1e386ef92cbf654e91bba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 30 Apr 2012 15:21:02 -0600 Subject: PCI: work around Stratus ftServer broken PCIe hierarchy A PCIe downstream port is a P2P bridge. Its secondary interface is a link that should lead only to device 0 (unless ARI is enabled)[1], so we don't probe for non-zero device numbers. Some Stratus ftServer systems have a PCIe downstream port (02:00.0) that leads to both an upstream port (03:00.0) and a downstream port (03:01.0), and 03:01.0 has important devices below it: [0000:02]-+-00.0-[03-3c]--+-00.0-[04-09]--... \-01.0-[0a-0d]--+-[USB] +-[NIC] +-... Previously, we didn't enumerate device 03:01.0, so USB and the network didn't work. This patch adds a DMI quirk to scan all device numbers, not just 0, below a downstream port. Based on a patch by Prarit Bhargava. [1] PCIe spec r3.0, sec 7.3.1 CC: Myron Stowe CC: Don Dutile CC: James Paradis CC: Matthew Wilcox CC: Jesse Barnes CC: Prarit Bhargava Signed-off-by: Bjorn Helgaas --- Documentation/kernel-parameters.txt | 3 +++ arch/x86/pci/common.c | 16 ++++++++++++++++ drivers/pci/pci.c | 3 +++ drivers/pci/probe.c | 8 ++++++-- include/asm-generic/pci-bridge.h | 6 ++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c1601e5a8b71..f995195409fd 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2161,6 +2161,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. on: Turn realloc on realloc same as realloc=on noari do not use PCIe ARI. + pcie_scan_all Scan all possible PCIe devices. Otherwise we + only look for one device below a PCIe downstream + port. pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power Management. diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 323481e06ef8..16c5d7835295 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -229,6 +230,14 @@ static int __devinit assign_all_busses(const struct dmi_system_id *d) } #endif +static int __devinit set_scan_all(const struct dmi_system_id *d) +{ + printk(KERN_INFO "PCI: %s detected, enabling pci=pcie_scan_all\n", + d->ident); + pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS); + return 0; +} + static const struct dmi_system_id __devinitconst pciprobe_dmi_table[] = { #ifdef __i386__ /* @@ -420,6 +429,13 @@ static const struct dmi_system_id __devinitconst pciprobe_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL585 G2"), }, }, + { + .callback = set_scan_all, + .ident = "Stratus/NEC ftServer", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ftServer"), + }, + }, {} }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 111569ccab43..8e6c38817036 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "pci.h" @@ -3893,6 +3894,8 @@ static int __init pci_setup(char *str) pcie_bus_config = PCIE_BUS_PERFORMANCE; } else if (!strncmp(str, "pcie_bus_peer2peer", 18)) { pcie_bus_config = PCIE_BUS_PEER2PEER; + } else if (!strncmp(str, "pcie_scan_all", 13)) { + pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5e1ca3c58a7d..2dc8675eea1a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "pci.h" #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ @@ -1395,10 +1396,13 @@ static unsigned no_next_fn(struct pci_dev *dev, unsigned fn) static int only_one_child(struct pci_bus *bus) { struct pci_dev *parent = bus->self; + if (!parent || !pci_is_pcie(parent)) return 0; - if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT || - parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) + if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT) + return 1; + if (parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && + !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS)) return 1; return 0; } diff --git a/include/asm-generic/pci-bridge.h b/include/asm-generic/pci-bridge.h index a5b5d5a89a4f..20db2e5a0a69 100644 --- a/include/asm-generic/pci-bridge.h +++ b/include/asm-generic/pci-bridge.h @@ -30,6 +30,12 @@ enum { PCI_ENABLE_PROC_DOMAINS = 0x00000010, /* ... except for domain 0 */ PCI_COMPAT_DOMAIN_0 = 0x00000020, + + /* PCIe downstream ports are bridges that normally lead to only a + * device 0, but if this is set, we scan all possible devices, not + * just device 0. + */ + PCI_SCAN_ALL_PCIE_DEVS = 0x00000040, }; #ifdef CONFIG_PCI -- cgit v1.2.3 From 977f857ca566a1e68045fcbb7cfc9c4acb077cf0 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 24 Apr 2012 13:15:18 -0600 Subject: PCI: move mutex locking out of pci_dev_reset function The intent of git commit 6fbf9e7a90862988c278462d85ce9684605a52b2 "PCI: Introduce __pci_reset_function_locked to be used when holding device_lock." was to have a non-locking function that would call pci_dev_reset function. But it fell short of that by just probing and not actually reseting the device. To make that work we need a way to move the lock around device_lock to not be in pci_dev_reset (as the caller of __pci_reset_function_locked already holds said lock). We do this by renaming pci_dev_reset to __pci_dev_reset and bubbling said mutex out of __pci_dev_reset to pci_dev_reset (a wrapper around __pci_dev_reset). The __pci_reset_function_locked can now call __pci_dev_reset without having to worry about the dead-lock. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 111569ccab43..9e31c0ab650e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3164,18 +3164,12 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe) return 0; } -static int pci_dev_reset(struct pci_dev *dev, int probe) +static int __pci_dev_reset(struct pci_dev *dev, int probe) { int rc; might_sleep(); - if (!probe) { - pci_cfg_access_lock(dev); - /* block PM suspend, driver probe, etc. */ - device_lock(&dev->dev); - } - rc = pci_dev_specific_reset(dev, probe); if (rc != -ENOTTY) goto done; @@ -3194,14 +3188,27 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) rc = pci_parent_bus_reset(dev, probe); done: + return rc; +} + +static int pci_dev_reset(struct pci_dev *dev, int probe) +{ + int rc; + + if (!probe) { + pci_cfg_access_lock(dev); + /* block PM suspend, driver probe, etc. */ + device_lock(&dev->dev); + } + + rc = __pci_dev_reset(dev, probe); + if (!probe) { device_unlock(&dev->dev); pci_cfg_access_unlock(dev); } - return rc; } - /** * __pci_reset_function - reset a PCI device function * @dev: PCI device to reset @@ -3246,7 +3253,7 @@ EXPORT_SYMBOL_GPL(__pci_reset_function); */ int __pci_reset_function_locked(struct pci_dev *dev) { - return pci_dev_reset(dev, 1); + return __pci_dev_reset(dev, 0); } EXPORT_SYMBOL_GPL(__pci_reset_function_locked); -- cgit v1.2.3 From 0f1103e40f9186bd2cdac4dde6c5bbd2f5273365 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 1 May 2012 17:25:18 -0600 Subject: x86/PCI: fix unused variable warning in amd_bus.c Fix this warning: arch/x86/pci/amd_bus.c: In function 'early_fill_mp_bus_info': arch/x86/pci/amd_bus.c:56:6: warning: unused variable 'j' [-Wunused-variable] introduced by commit d28e5ac2a07e ("x86/PCI: dynamically allocate pci_root_info for native host bridge drivers"). Reported-by: Stephen Rothwell Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/amd_bus.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c index 459a7316375c..5aed49bff058 100644 --- a/arch/x86/pci/amd_bus.c +++ b/arch/x86/pci/amd_bus.c @@ -44,6 +44,15 @@ static struct pci_root_info __init *find_pci_root_info(int node, int link) return NULL; } +static void __init set_mp_bus_range_to_node(int min_bus, int max_bus, int node) +{ +#ifdef CONFIG_NUMA + int j; + + for (j = min_bus; j <= max_bus; j++) + set_mp_bus_to_node(j, node); +#endif +} /** * early_fill_mp_bus_to_node() * called before pcibios_scan_root and pci_scan_bus @@ -53,7 +62,6 @@ static struct pci_root_info __init *find_pci_root_info(int node, int link) static int __init early_fill_mp_bus_info(void) { int i; - int j; unsigned bus; unsigned slot; int node; @@ -109,10 +117,7 @@ static int __init early_fill_mp_bus_info(void) min_bus = (reg >> 16) & 0xff; max_bus = (reg >> 24) & 0xff; node = (reg >> 4) & 0x07; -#ifdef CONFIG_NUMA - for (j = min_bus; j <= max_bus; j++) - set_mp_bus_to_node(j, node); -#endif + set_mp_bus_range_to_node(min_bus, max_bus, node); link = (reg >> 8) & 0x03; info = alloc_pci_root_info(min_bus, max_bus, node, link); -- cgit v1.2.3 From df558de16c8a90e44ffb405e9224980b15158c93 Mon Sep 17 00:00:00 2001 From: Xudong Hao Date: Fri, 27 Apr 2012 09:16:46 -0600 Subject: PCI: work around IvyBridge internal graphics FLR erratum For IvyBridge Mobile platform, a system hang may occur if a FLR (Function Level Reset) is asserted to internal graphics. This quirk is a workaround for the IVB FLR errata issue. We are disabling the FLR reset handshake between the PCH and CPU display, then manually powering down the panel power sequencing and resetting the PCH display. Signed-off-by: Xudong Hao Signed-off-by: Kay, Allen M Signed-off-by: Matthew Wilcox Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4bf71028556b..6279d5b85993 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3085,16 +3085,74 @@ static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe) return 0; } +#include "../gpu/drm/i915/i915_reg.h" +#define MSG_CTL 0x45010 +#define NSDE_PWR_STATE 0xd0100 +#define IGD_OPERATION_TIMEOUT 10000 /* set timeout 10 seconds */ + +static int reset_ivb_igd(struct pci_dev *dev, int probe) +{ + void __iomem *mmio_base; + unsigned long timeout; + u32 val; + + if (probe) + return 0; + + mmio_base = pci_iomap(dev, 0, 0); + if (!mmio_base) + return -ENOMEM; + + iowrite32(0x00000002, mmio_base + MSG_CTL); + + /* + * Clobbering SOUTH_CHICKEN2 register is fine only if the next + * driver loaded sets the right bits. However, this's a reset and + * the bits have been set by i915 previously, so we clobber + * SOUTH_CHICKEN2 register directly here. + */ + iowrite32(0x00000005, mmio_base + SOUTH_CHICKEN2); + + val = ioread32(mmio_base + PCH_PP_CONTROL) & 0xfffffffe; + iowrite32(val, mmio_base + PCH_PP_CONTROL); + + timeout = jiffies + msecs_to_jiffies(IGD_OPERATION_TIMEOUT); + do { + val = ioread32(mmio_base + PCH_PP_STATUS); + if ((val & 0xb0000000) == 0) + goto reset_complete; + msleep(10); + } while (time_before(jiffies, timeout)); + dev_warn(&dev->dev, "timeout during reset\n"); + +reset_complete: + iowrite32(0x00000002, mmio_base + NSDE_PWR_STATE); + + pci_iounmap(dev, mmio_base); + return 0; +} + #define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed +#define PCI_DEVICE_ID_INTEL_IVB_M_VGA 0x0156 +#define PCI_DEVICE_ID_INTEL_IVB_M2_VGA 0x0166 static const struct pci_dev_reset_methods pci_dev_reset_methods[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF, reset_intel_82599_sfp_virtfn }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_M_VGA, + reset_ivb_igd }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_M2_VGA, + reset_ivb_igd }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, reset_intel_generic_dev }, { 0 } }; +/* + * These device-specific reset methods are here rather than in a driver + * because when a host assigns a device to a guest VM, the host may need + * to reset the device but probably doesn't have a driver for it. + */ int pci_dev_specific_reset(struct pci_dev *dev, int probe) { const struct pci_dev_reset_methods *i; -- cgit v1.2.3 From b566a22c23327f18ce941ffad0ca907e50a53d41 Mon Sep 17 00:00:00 2001 From: Khalid Aziz Date: Fri, 27 Apr 2012 13:00:33 -0600 Subject: PCI: disable Bus Master on PCI device shutdown Disable Bus Master bit on the device in pci_device_shutdown() to ensure PCI devices do not continue to DMA data after shutdown. This can cause memory corruption in case of a kexec where the current kernel shuts down and transfers control to a new kernel while a PCI device continues to DMA to memory that does not belong to it any more in the new kernel. I have tested this code on two laptops, two workstations and a 16-socket server. kexec worked correctly on all of them. Signed-off-by: Khalid Aziz Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 6b54b23b990b..bf0cee629b60 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -420,6 +420,12 @@ static void pci_device_shutdown(struct device *dev) pci_msi_shutdown(pci_dev); pci_msix_shutdown(pci_dev); + /* + * Turn off Bus Master bit on the device to tell it to not + * continue to do DMA + */ + pci_disable_device(pci_dev); + /* * Devices may be enabled to wake up by runtime PM, but they need not * be supposed to wake up the system from its "power off" state (e.g. -- cgit v1.2.3 From 99662dd1ce05dbe6394771fcb6ca21bd2aa35987 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 7 May 2012 08:36:08 -0600 Subject: MAINTAINERS: update PCI git tree and patchwork Update the git tree address and patchwork. Drop the separate PCI hotplug entry because it's redundant. Signed-off-by: Bjorn Helgaas --- MAINTAINERS | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 1a2f8f5823e0..a8556f842257 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5132,19 +5132,13 @@ F: Documentation/powerpc/eeh-pci-error-recovery.txt PCI SUBSYSTEM M: Bjorn Helgaas L: linux-pci@vger.kernel.org -Q: http://patchwork.kernel.org/project/linux-pci/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci.git +Q: http://patchwork.ozlabs.org/project/linux-pci/list/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/linux.git S: Supported F: Documentation/PCI/ F: drivers/pci/ F: include/linux/pci* -PCI HOTPLUG -M: Bjorn Helgaas -L: linux-pci@vger.kernel.org -S: Supported -F: drivers/pci/hotplug - PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org -- cgit v1.2.3 From 1267b3a325f00291e847ea4a001ccabe5d5516f2 Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Wed, 7 Mar 2012 15:16:26 +0800 Subject: PCI: fix uninitialized variable 'cap_mask' Get rid of these: drivers/pci/pcie/portdrv_core.c: In function 'pcie_port_device_register': drivers/pci/pcie/portdrv_core.c:275:16: warning: 'cap_mask' may be used uninitialized in this function [-Wuninitialized] drivers/pci/pcie/portdrv_core.c:240:6: note: 'cap_mask' was declared here In some cases, 'cap_mask' may be not set in pcie_port_platform_notify, holding a garbage value. Signed-off-by: Chunhe Lan Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 2f589a54f9bd..75915b30ad19 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -249,7 +249,7 @@ static int get_port_device_capability(struct pci_dev *dev) int services = 0, pos; u16 reg16; u32 reg32; - int cap_mask; + int cap_mask = 0; int err; if (pcie_ports_disabled) -- cgit v1.2.3 From 74d24b219bc4ebb20b75d63af2bb577bc1b10b5e Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 26 Apr 2012 15:32:55 +0800 Subject: resources: add resource_overlaps() Add resource_overlaps(), which returns true if two resources overlap at all. Use this to replace the complicated check in coalesce_windows(). Signed-Off-By: Wei Yang Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 12 +----------- include/linux/ioport.h | 7 +++++++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 8a17b23f8c84..fc09c2754e08 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -245,13 +245,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) return AE_OK; } -static bool resource_contains(struct resource *res, resource_size_t point) -{ - if (res->start <= point && point <= res->end) - return true; - return false; -} - static void coalesce_windows(struct pci_root_info *info, unsigned long type) { int i, j; @@ -272,10 +265,7 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type) * our resources no longer match the ACPI _CRS, but * the kernel resource tree doesn't allow overlaps. */ - if (resource_contains(res1, res2->start) || - resource_contains(res1, res2->end) || - resource_contains(res2, res1->start) || - resource_contains(res2, res1->end)) { + if (resource_overlaps(res1, res2)) { res1->start = min(res1->start, res2->start); res1->end = max(res1->end, res2->end); dev_info(&info->bridge->dev, diff --git a/include/linux/ioport.h b/include/linux/ioport.h index e885ba23de70..589e0e75efae 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -223,5 +223,12 @@ extern int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, void *arg, int (*func)(unsigned long, unsigned long, void *)); +/* True if any part of r1 overlaps r2 */ +static inline bool resource_overlaps(struct resource *r1, struct resource *r2) +{ + return (r1->start <= r2->end && r1->end >= r2->start); +} + + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_IOPORT_H */ -- cgit v1.2.3 From 867aae6ebe593db73fb8a676475ee20227292cfe Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 15 May 2012 17:01:09 -0600 Subject: x86/PCI: only check for spinlock being held in SMP kernels spin_is_locked() is always false on UP kernels: spin_lock_irqsave() does no locking, so we can't tell whether the lock is held or not. Therefore, this warning is only valid for SMP kernels. CC: Myron Stowe Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 831971e731f7..dd8ca6f7223b 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -57,7 +57,7 @@ static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) { struct pcibios_fwaddrmap *map; - WARN_ON(!spin_is_locked(&pcibios_fwaddrmap_lock)); + WARN_ON_SMP(!spin_is_locked(&pcibios_fwaddrmap_lock)); list_for_each_entry(map, &pcibios_fwaddrmappings, list) if (map->dev == dev) -- cgit v1.2.3 From 5420e46d4d79bcd5d5952df98d022c8412385d32 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 15 May 2012 17:03:25 -0600 Subject: microblaze/PCI: fix "io_offset undeclared" error There is a compile error for microblaze pci because io_offset is not declared. This patch adds declaration of io_offset. [bhelgaas: I introduced this problem with 58de74b8053] Signed-off-by: Hiroo MATSUMOTO Signed-off-by: Bjorn Helgaas --- arch/microblaze/pci/pci-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index d10403dadd2b..ed22bfc5db14 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -1422,6 +1422,7 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) static void __devinit pcibios_setup_phb_resources(struct pci_controller *hose, struct list_head *resources) { + unsigned long io_offset; struct resource *res; int i; -- cgit v1.2.3