PCI: pci_host_bridge related cleanup
cover PCI: pci_host_bridge related cleanup
[PATCH 1/8] PCI: Separate host_bridge code out from probe.c [PATCH 2/8] x86, PCI: have own version for pcibios_bus_to_resource [PATCH 3/8] x86, PCI: Fix memleak with get_current_resources [PATCH 4/8] PCI: make pci_host_bridge more robust
[PATCH 5/8] PCI: add generic device into pci_host_bridge struct [PATCH 6/8] PCI: add host bridge release support
[PATCH 7/8] x86, PCI: break down get_current_resource() [PATCH 8/8] x86, PCI: add host bridge resource release for using _CRS
will add struct device dev into host_bridge struct.
also will add release support to make sure allocated resource get freed during root bus removal.
Thanks
Yinghai Lu
PATCH 1/8 PCI: Separate host_bridge code out from probe.c
-v2: Bjorn want add_to_pci_host_bridges to pass struct pci_host_bridge *
instead of list head pointer.
--- 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(-)
drivers/pci/host-bridge.c new file +93 -0
@@ -0,0 +1,93 @@ +/* +* host_bridge.c - host bridge related code +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/module.h> + +#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);
drivers/pci/pci.h +2 -0
@@ -231,6 +231,8 @@ static inline int pci_ari_enabled(structvoid 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 */
drivers/pci/probe.c +1 -80
103 lines not on this page · raw message
drivers/pci/Makefile +1 -1
@@ -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
PATCH 2/8 x86, PCI: have own version for pcibios_bus_to_resource
In reply to Yinghai Lux86 does not need to offset the address. So we can skip that costing offset searching.
--- arch/x86/pci/i386.c | 14 ++++++++++++++ drivers/pci/host-bridge.c | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-)
arch/x86/pci/i386.c +14 -0
@@ -335,6 +335,20 @@ void __init pcibios_resource_survey(void*/ fs_initcall (pcibios_assign_resources); +void pcibios_resource_to_bus (struct pci_dev *dev,struct pci_bus_region *region, +struct resource *res) +{ + region->start = res->start; + region->end = res->end; +} + +void pcibios_bus_to_resource (struct pci_dev *dev,struct resource *res, +struct pci_bus_region *region) +{ + res->start = region->start; + res->end = region->end; +} +static const struct vm_operations_struct pci_mmap_ops = { .access = generic_access_phys, };
drivers/pci/host-bridge.c +5 -4
@@ -38,8 +38,9 @@ static bool resource_contains(struct resreturn 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) +void __weak 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;
@@ -66,8 +67,8 @@ static bool region_contains(struct pci_breturn 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) +void __weak 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;
Bjorn Helgaas
maintainerRe: [PATCH 2/8] x86, PCI: have own version for pcibios_bus_to_resource
Quoted 2 lines · Yinghai Lu
x86 does not need to offset the address. So we can skip that costing offset searching.
I don't understand the point of this. I'm trying hard to *remove* PCI code from arch/ because most of it is not really arch-specific. This is a good example. There's nothing in the pcibios_bus_to_resource() that you're adding back to x86 that is specific to x86, and it makes things more complicated. Using __weak adds ambiguity for the reader, now the pci_add_resource_offset() interface exists but doesn't work, etc.
I agree that this patch will make x86 a tiny bit faster and a tiny bit smaller, but I don't think it's enough to matter. Do you have numbers that say otherwise?
Bjorn
Quoted 57 lines
---
arch/x86/pci/i386.c | 14 ++++++++++++++
drivers/pci/host-bridge.c | 9 +++++----2 files changed, 19 insertions(+), 4 deletions(-)Index:
linux-2.6/arch/x86/pci/i386.c
===================================================================arch/x86/pci/i386.c +14
-0@@ -335,6 +335,20 @@ void __init pcibios_resource_survey(void*/ fs_initcall(pcibios_assign_resources); +void pcibios_resource_to_bus (struct pci_dev *dev,struct pci_bus_region *region, + struct resource *res) +{ + region->start = res->start; + region->end = res->end; +} + +void pcibios_bus_to_resource (struct pci_dev *dev,struct resource *res, + struct pci_bus_region *region) +{ + res->start = region->start; + res->end = region->end; +} + static const struct vm_operations_struct pci_mmap_ops = { .access = generic_access_phys, };Index:
linux-2.6/drivers/pci/host-bridge.c
===================================================================drivers/pci/host-bridge.c +5
-4@@ -38,8 +38,9 @@ static bool resource_contains(struct res 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) +void __weak 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;@@ -66,8 +67,8 @@ static bool region_contains(struct pci_b 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) +void __weak 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;
PATCH 3/8 x86, PCI: Fix memleak with get_current_resources
In reply to Yinghai Luin 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 res 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 have local info.
--- arch/x86/pci/acpi.c | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-)
arch/x86/pci/acpi.c +30 -19
@@ -305,49 +305,55 @@ static void add_resources(struct pci_roo } } +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);
@@ -392,6 +398,7 @@ struct pci_bus * __devinit pci_acpi_scan 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.
@@ -405,7 +412,8 @@ struct pci_bus * __devinit pci_acpi_scanmemcpy (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
@@ -419,6 +427,9 @@ struct pci_bus * __devinit pci_acpi_scan 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,
PATCH 4/8 PCI: make pci_host_bridge more robust
In reply to Yinghai Lu1. change pci_host_bridge to find_pci_root_bridge.
2. separate find_pci_root_bus().
3. on any possible path return NULL.
--- drivers/pci/host-bridge.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-)
drivers/pci/host-bridge.c +28 -7
@@ -16,19 +16,31 @@ void add_to_pci_host_bridges(struct pci_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; -list_for_each_entry (bridge, &pci_host_bridges, list) { +if (!pci_is_root_bus (bus)) +return NULL ; + +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; + +if (!bus) +return NULL ; + +list_for_each_entry (bridge, &pci_host_bridges, list)if (bridge->bus == bus)return bridge; - }return NULL ; }
@@ -42,10 +54,13 @@ void __weak pcibios_resource_to_bus(strustruct 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 ; +if (!bridge) + goto no_bridge; +list_for_each_entry (window, &bridge->windows, list) {if (resource_type (res) !=resource_type (window->res))continue ;
@@ -56,6 +71,7 @@ void __weak pcibios_resource_to_bus(stru } } +no_bridge: region->start = res->start - offset; region->end = res->end - offset; }
@@ -70,12 +86,16 @@ static bool region_contains(struct pci_bvoid __weak 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 ; +if (!bridge) + goto no_bridge; +list_for_each_entry (window, &bridge->windows, list) { +struct pci_bus_region bus_region; +if (resource_type (res) !=resource_type (window->res))continue ;
@@ -88,6 +108,7 @@ void __weak pcibios_bus_to_resource(stru } } +no_bridge: res->start = region->start + offset; res->end = region->end + offset; }
Bjorn Helgaas
maintainerRe: [PATCH 4/8] PCI: make pci_host_bridge more robust
Quoted 21 lines · Yinghai Lu
1. change
pci_host_bridgetofind_pci_root_bridge.
2. separatefind_pci_root_bus().
3. on any possible path return NULL.Signed-off-by Yinghai Lu <[email protected]>---
drivers/pci/host-bridge.c | 35 ++++++++++++++++++++++++++++-------1 file changed, 28 insertions(+), 7 deletions(-)Index:
linux-2.6/drivers/pci/host-bridge.c
===================================================================drivers/pci/host-bridge.c +1
-1@@ -16,19 +16,31 @@ void add_to_pci_host_bridges(struct pci_ 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) {
drivers/pci/host-bridge.c void add_to_pci_host_bridges(struct pci_ · line 28
Quoted 10 lines
drivers/pci/host-bridge.c +2
-2struct 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 (!pci_is_root_bus(bus)) + return NULL;
pci_is_root_bus() returns "!(pbus->parent)", so the effect of this patch is:
while (bus->parent)
bus = bus->parent;
if (bus->parent)
return NULL;The only reason we exited the "while" loop is because "bus->parent == NULL", so what's the point of adding the "if" check afterwards?
drivers/pci/host-bridge.c void add_to_pci_host_bridges(struct pci_ · line 39
Quoted 10 lines
drivers/pci/host-bridge.c +10
-0+ 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; + + if (!bus) + return NULL;
This should never happen. If there's a way we can create a pci_dev that's not under a pci_host_bridge, I think that is a bug, and we should fix that rather than papering over it here.
I don't think we should apply this patch.
Bjorn
Quoted 57 lines
drivers/pci/host-bridge.c +13
-4+ list_for_each_entry(bridge, &pci_host_bridges, list) if (bridge->bus == bus) return bridge; - } return NULL; }@@ -42,10 +54,13 @@ void __weak pcibios_resource_to_bus(stru 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; + if (!bridge) + goto no_bridge; + list_for_each_entry(window, &bridge->windows, list) { if (resource_type(res) != resource_type(window->res)) continue;@@ -56,6 +71,7 @@ void __weak pcibios_resource_to_bus(stru } } +no_bridge: region->start = res->start - offset; region->end = res->end - offset; }@@ -70,12 +86,16 @@ static bool region_contains(struct pci_b void __weak 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; + if (!bridge) + goto no_bridge; + list_for_each_entry(window, &bridge->windows, list) { + struct pci_bus_region bus_region; + if (resource_type(res) != resource_type(window->res)) continue;@@ -88,6 +108,7 @@ void __weak pcibios_bus_to_resource(stru } } +no_bridge: res->start = region->start + offset; res->end = region->end + offset; }
PATCH 5/8 PCI: add generic device into pci_host_bridge struct
In reply to Yinghai Luuse that device for pci_root_bus bridge pointer.
With that make code more simple.
Also we can use pci_release_bus_bridge_dev() to release allocated pci_host_bridge during removing path.
At last, we can use root bus bridge pointer to get host bridge pointer instead of going over host bridge list, so could kill that host bridge list.
--- drivers/pci/host-bridge.c | 14 --------- drivers/pci/pci.h | 2 - drivers/pci/probe.c | 65 ++++++++++++++++++++++++---------------------- include/linux/pci.h | 4 ++ 4 files changed, 38 insertions(+), 47 deletions(-)
drivers/pci/host-bridge.c +1 -13
@@ -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;
@@ -33,16 +26,11 @@ static struct pci_bus *find_pci_root_busstatic 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;if (!bus)return NULL ; -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)
drivers/pci/pci.h +0 -2
@@ -231,8 +231,6 @@ static inline int pci_ari_enabled(structvoid 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 */
drivers/pci/probe.c +34 -31
122 lines not on this page · raw message
include/linux/pci.h +3 -1
@@ -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
Bjorn Helgaas
maintainerRe: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 210 lines · Yinghai Lu
use that device for
pci_root_busbridge pointer.With that make code more simple.
Also we can use
pci_release_bus_bridge_dev()to release allocatedpci_host_bridgeduring removing path.At last, we can use root bus bridge pointer to get host bridge pointer instead of going over host bridge list, so could kill that host bridge list.
Signed-off-by Yinghai Lu <[email protected]>---
drivers/pci/host-bridge.c | 14 ---------
drivers/pci/pci.h | 2 -
drivers/pci/probe.c | 65 ++++++++++++++++++++++++----------------------include/linux/pci.h| 4 ++4 files changed, 38 insertions(+), 47 deletions(-)Index:
linux-2.6/drivers/pci/host-bridge.c
===================================================================drivers/pci/host-bridge.c +1
-13@@ -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;@@ -33,16 +26,11 @@ static struct pci_bus *find_pci_root_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; if (!bus) return NULL; - 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)Index:
linux-2.6/drivers/pci/pci.h
===================================================================drivers/pci/pci.h +0
-2@@ -231,8 +231,6 @@ static inline int pci_ari_enabled(struct 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 */
Index:linux-2.6/drivers/pci/probe.c
===================================================================drivers/pci/probe.c +13
-0@@ -421,6 +421,19 @@ static struct pci_bus * pci_alloc_bus(vo 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 */drivers/pci/probe.c +18
-20@@ -1121,7 +1134,13 @@ int pci_cfg_space_size(struct pci_dev *d 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)@@ -1570,28 +1589,19 @@ struct pci_bus *pci_create_root_bus(stru 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 */@@ -1599,13 +1609,17 @@ struct pci_bus *pci_create_root_bus(stru 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);@@ -1624,9 +1638,6 @@ struct pci_bus *pci_create_root_bus(stru 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
drivers/pci/probe.c +0
-1@@ -1652,25 +1663,17 @@ struct pci_bus *pci_create_root_bus(stru } 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);drivers/pci/probe.c +3
-5-dev_reg_err: - down_write(&pci_bus_sem); - list_del(&bridge->list); - list_del(&b->node); - up_write(&pci_bus_sem); + device_unregister(&bridge->dev); +bridge_dev_reg_err: + kfree(bridge);err_out:
- kfree(dev);
-err_dev:
kfree(b);
-err_bus:
- kfree(bridge);
return NULL;
}Index:
linux-2.6/include/linux/pci.h
===================================================================include/linux/pci.h +1
-1@@ -375,11 +375,13 @@ struct pci_host_bridge_window { }; struct pci_host_bridge { - struct list_head list; + struct device dev;
Quoted 3 lines
struct
pci_bus*bus; /* root bus */ structlist_headwindows; /*pci_host_bridge_windows*/ };
This doesn't feel right to me. You're allocating a new struct device here, but the arch likely already has one. In fact, I think we even passed it in to pci_create_root_bus().
On x86, we already have an ACPI device for the host bridge, and now we'll have a second one. (Actually a *third* one, because PNPACPI also has one, but that's a long-standing problem.)
Quoted 5 lines
include/linux/pci.h +2
-0+#define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev) +/*
* The firstPCI_BRIDGE_RESOURCE_NUMPCI bus resources (those that correspond
* to P2P or CardBus bridge windows) go in a table. Additional ones (for
Re: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 20 lines · Bjorn Helgaas
Quoted 19 lines · Yinghai Lu
use that device for
pci_root_busbridge pointer.With that make code more simple.
Also we can use
pci_release_bus_bridge_dev()to release allocatedpci_host_bridgeduring removing path.At last, we can use root bus bridge pointer to get host bridge pointer instead of going over host bridge list, so could kill that host bridge list.
Signed-off-by Yinghai Lu <[email protected]>---
drivers/pci/host-bridge.c | 14 ---------
drivers/pci/pci.h | 2 -
drivers/pci/probe.c | 65 ++++++++++++++++++++++++----------------------include/linux/pci.h| 4 ++4 files changed, 38 insertions(+), 47 deletions(-)
Quoted 9 lines
drivers/pci/host-bridge.c
Quoted 9 lines
Index:
linux-2.6/drivers/pci/host-bridge.c
===================================================================drivers/pci/host-bridge.c +1
-3- dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - goto err_dev; + return NULL;
...
here old code allocate dummy struct device.
Quoted 13 lines
Quoted 13 lines
+ bridge =
pci_alloc_host_bridge(b);
+ if (!bridge)
+ gotoerr_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)
- gotodev_reg_err;
-b->bridge=get_device(dev);
+ gotobridge_dev_reg_err;
+b->bridge=get_device(&bridge->dev);
...
Quoted 5 lines
Quoted 5 lines
include/linux/pci.h +0
-0@@ -375,11 +375,13 @@ struct pci_host_bridge_window { };
Quoted 10 lines
Quoted 6 lines
include/linux/pci.h +1
-1struct pci_host_bridge { - struct list_head list; + struct device dev;struct
pci_bus*bus; /* root bus */ structlist_headwindows; /*pci_host_bridge_windows*/ };This doesn't feel right to me. You're allocating a new struct device here, but the arch likely already has one. In fact, I think we even passed it in to
pci_create_root_bus().
no.
original pci_create_root_bus() will create one dummy struct device and use it as parent for bus->dev.
Quoted 4 lines
On x86, we already have an ACPI device for the host bridge, and now we'll have a second one. (Actually a *third* one, because PNPACPI also has one, but that's a long-standing problem.)
not related.
Yinghai
Bjorn Helgaas
maintainerRe: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 70 lines · Yinghai Lu
Quoted 20 lines · Bjorn Helgaas
Quoted 19 lines · Yinghai Lu
use that device for
pci_root_busbridge pointer.With that make code more simple.
Also we can use
pci_release_bus_bridge_dev()to release allocatedpci_host_bridgeduring removing path.At last, we can use root bus bridge pointer to get host bridge pointer instead of going over host bridge list, so could kill that host bridge list.
Signed-off-by Yinghai Lu <[email protected]>---
drivers/pci/host-bridge.c | 14 ---------
drivers/pci/pci.h | 2 -
drivers/pci/probe.c | 65 ++++++++++++++++++++++++----------------------include/linux/pci.h| 4 ++4 files changed, 38 insertions(+), 47 deletions(-)Quoted 9 lines
drivers/pci/host-bridge.c
Quoted 9 lines
Index:
linux-2.6/drivers/pci/host-bridge.c
===================================================================drivers/pci/host-bridge.c +1
-3- dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - goto err_dev; + return NULL;...
here old code allocate dummy struct device.
Quoted 13 lines
Quoted 13 lines
+ bridge =
pci_alloc_host_bridge(b);
+ if (!bridge)
+ gotoerr_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)
- gotodev_reg_err;
-b->bridge=get_device(dev);
+ gotobridge_dev_reg_err;
+b->bridge=get_device(&bridge->dev);...
Quoted 5 lines
Quoted 5 lines
include/linux/pci.h +0
-0@@ -375,11 +375,13 @@ struct pci_host_bridge_window { };Quoted 10 lines
Quoted 6 lines
include/linux/pci.h +1
-1struct pci_host_bridge { - struct list_head list; + struct device dev;struct
pci_bus*bus; /* root bus */ structlist_headwindows; /*pci_host_bridge_windows*/ };This doesn't feel right to me. You're allocating a new struct device here, but the arch likely already has one. In fact, I think we even passed it in to
pci_create_root_bus().no.
original
pci_create_root_bus()will create one dummy struct device and use it as parent forbus->dev.Quoted 1 line
Quoted 5 lines
Quoted 3 lines
On x86, we already have an ACPI device for the host bridge, and now we'll have a second one. (Actually a *third* one, because PNPACPI also has one, but that's a long-standing problem.)
not related.
OK. Can you educate me? What is the device we pass in, what does the dummy one correspond to, why do we have two, what's the difference, etc., etc.?
Bjorn
Re: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 16 lines · Bjorn Helgaas
Quoted 14 lines · Yinghai Lu
Quoted 3 lines
This doesn't feel right to me. You're allocating a new struct device here, but the arch likely already has one. In fact, I think we even passed it in to
pci_create_root_bus().no.
original
pci_create_root_bus()will create one dummy struct device and use it as parent forbus->dev.Quoted 4 lines
On x86, we already have an ACPI device for the host bridge, and now we'll have a second one. (Actually a *third* one, because PNPACPI also has one, but that's a long-standing problem.)
Quoted 5 lines
Quoted 1 line
not related.
OK. Can you educate me? What is the device we pass in, what does the dummy one correspond to, why do we have two, what's the difference, etc., etc.?
for every bus will have ->bridge, and that is device pointer. to pci bridge dev.
for root bus, there is not pci bridge for that. So create_root_bus allocate local dummy one.
that dummy device will be bus->dev's parent.
Now pci_host_bridge is added, and it is allocated, so just put the struct device into that pci_host_bridge.
and use pci_host_bridge->dev as parent of root bus->dev's parent.
Yinghai
Bjorn Helgaas
maintainerRe: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 29 lines · Yinghai Lu
Quoted 16 lines · Bjorn Helgaas
Quoted 14 lines · Yinghai Lu
Quoted 3 lines
This doesn't feel right to me. You're allocating a new struct device here, but the arch likely already has one. In fact, I think we even passed it in to
pci_create_root_bus().no.
original
pci_create_root_bus()will create one dummy struct device and use it as parent forbus->dev.Quoted 4 lines
On x86, we already have an ACPI device for the host bridge, and now we'll have a second one. (Actually a *third* one, because PNPACPI also has one, but that's a long-standing problem.)
Quoted 5 lines
Quoted 1 line
not related.
OK. Can you educate me? What is the device we pass in, what does the dummy one correspond to, why do we have two, what's the difference, etc., etc.?
for every bus will have ->bridge, and that is device pointer. to pci bridge dev.
for root bus, there is not pci bridge for that. So
create_root_busallocate local dummy one.
Quoted 6 lines
that dummy device will be bus->dev's parent.
Now
pci_host_bridgeis added, and it is allocated, so just put the struct device into thatpci_host_bridge.and use
pci_host_bridge->devas parent of root bus->dev's parent.
What's the device passed in to pci_create_root_bus()? Why is that different than the root bus dev's parent?
Re: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 9 lines · Bjorn Helgaas
Quoted 9 lines
for every bus will have ->bridge, and that is device pointer. to pci bridge dev.
for root bus, there is not pci bridge for that. So
create_root_busallocate local dummy one.that dummy device will be bus->dev's parent.
Quoted 7 lines
Quoted 4 lines
Now
pci_host_bridgeis added, and it is allocated, so just put the struct device into thatpci_host_bridge.and use
pci_host_bridge->devas parent of root bus->dev's parent.What's the device passed in to
pci_create_root_bus()? Why is that different than the root bus dev's parent?
ia64 and x86 doesn't pass that.
arch/ia64/pci/pci.c: pbus = pci_create_root_bus(NULL, bus,&pci_root_ops, controller,arch/powerpc/kernel/pci-common.c: bus =
pci_create_root_bus(hose->parent, hose->first_busno,arch/sparc/kernel/pci.c: bus = pci_create_root_bus(parent,pbm->pci_first_busno, pbm->pci_ops,arch/x86/pci/acpi.c: bus = pci_create_root_bus(NULL,
busnum, &pci_root_ops, sd, drivers/parisc/dino.c: dino_dev->hba.hba_bus = bus = pci_create_root_bus(&dev->dev,drivers/parisc/lba_pci.c: pci_create_root_bus(&dev->dev,lba_dev->hba.bus_num.start,
sparc, powerpc, parisc have their local ...
so local hose etc's dev ---> allocated dev ---> root bus's dev
this patch change to
local hose etc's dev ---> allocated host bridge's dev ---> root bus's dev
Bjorn Helgaas
maintainerRe: [PATCH 5/8] PCI: add generic device into pci_host_bridge struct
Quoted 33 lines · Yinghai Lu
Quoted 9 lines · Bjorn Helgaas
Quoted 9 lines
for every bus will have ->bridge, and that is device pointer. to pci bridge dev.
for root bus, there is not pci bridge for that. So
create_root_busallocate local dummy one.that dummy device will be bus->dev's parent.
Quoted 7 lines
Quoted 4 lines
Now
pci_host_bridgeis added, and it is allocated, so just put the struct device into thatpci_host_bridge.and use
pci_host_bridge->devas parent of root bus->dev's parent.What's the device passed in to
pci_create_root_bus()? Why is that different than the root bus dev's parent?ia64 and x86 doesn't pass that.
arch/ia64/pci/pci.c: pbus = pci_create_root_bus(NULL, bus,&pci_root_ops, controller,arch/powerpc/kernel/pci-common.c: bus = pci_create_root_bus(hose->parent,hose->first_busno,arch/sparc/kernel/pci.c: bus = pci_create_root_bus(parent,pbm->pci_first_busno,pbm->pci_ops,arch/x86/pci/acpi.c: bus = pci_create_root_bus(NULL, busnum,&pci_root_ops, sd,drivers/parisc/dino.c: dino_dev->hba.hba_bus = bus = pci_create_root_bus(&dev->dev,drivers/parisc/lba_pci.c: pci_create_root_bus(&dev->dev,lba_dev->hba.bus_num.start,
Quoted 7 lines
sparc, powerpc, parisc have their local ...
so local hose etc's dev ---> allocated dev ---> root bus's dev
this patch change to
local hose etc's dev ---> allocated host bridge's dev ---> root bus's dev
Why do we have a separate dev for the hose and the root bus's dev? The hose *is* a PCI host bridge.
PATCH 6/8 PCI: add host bridge release support
In reply to Yinghai LuWe need one hook to release host bridge resource that are created for host bridge.
--- drivers/pci/host-bridge.c | 8 ++++++++ drivers/pci/probe.c | 3 ++- include/linux/pci.h | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-)
drivers/pci/host-bridge.c +8 -0
@@ -33,6 +33,14 @@ static struct pci_host_bridge *find_pci_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;
drivers/pci/probe.c +2 -1
@@ -1136,7 +1136,8 @@ static void pci_release_bus_bridge_dev(s {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);
include/linux/pci.h +5 -0
@@ -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
PATCH 7/8 x86, PCI: break down get_current_resource()
In reply to Yinghai Luchange it to probe_pci_root_info, aka only do one thing.
1. remove resource list head from pci_root_info
2. make get_current_resources() not pass resources move out add_resources calling out of yet.
3. rename get_current_resourcs name to probe_pci_root_info
--- arch/x86/pci/acpi.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-)
arch/x86/pci/acpi.c +13 -21
@@ -12,7 +12,6 @@ struct pci_root_info {char *name;unsigned int res_num;struct resource *res; -struct list_head *resources;int busnum; };
@@ -277,7 +276,8 @@ static void coalesce_windows(struct pci_ } } -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;
@@ -301,7 +301,7 @@ static void add_resources(struct pci_roo"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 ); } }
@@ -313,41 +313,30 @@ static void free_pci_root_info(struct pc }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)
@@ -412,15 +401,18 @@ struct pci_bus * __devinit pci_acpi_scanmemcpy (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)
PATCH 8/8 x86, PCI: add host bridge resource release for using _CRS
In reply to Yinghai Lu1. allocate pci_root_info instead of use local stack. we need to pass around info for release fn.
2. add release_pci_root_info
3. set x86 own host bridge release fn, so will make sure root bridge related resources get freed during root bus removal.
--- arch/x86/pci/acpi.c | 66 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 13 deletions(-)
arch/x86/pci/acpi.c +53 -13
@@ -309,7 +309,33 @@ static void free_pci_root_info(struct pc {kfree (info->name);kfree (info->res); -memset (info,0 ,sizeof (struct pci_root_info )); +kfree (info); +} + +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 (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
@@ -342,7 +368,7 @@ probe_pci_root_info(struct pci_root_infostruct 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);
@@ -379,15 +405,16 @@ struct pci_bus * __devinit pci_acpi_scan* It's arguable whether it's worth the trouble to care. */ sd =kzalloc (sizeof (*sd),GFP_KERNEL ); -if (!sd) { -printk (KERN_WARNING "pci_bus %04x:%02x: " -"ignored (out of memory)\n" , domain, busnum); -return NULL ; - } +if (!sd) + goto out_no_memory; sd->domain = domain; sd->node = node; -memset (&info,0 ,sizeof (struct pci_root_info )); + info =kzalloc (sizeof (*info),GFP_KERNEL ); +if (!info) { +kfree (sd); + goto out_no_memory; + }/* * 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.
@@ -399,20 +426,22 @@ struct pci_bus * __devinit pci_acpi_scan* 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 (info);x86_pci_root_bus_resources (busnum, &resources); } + bus =pci_create_root_bus (NULL , busnum, &pci_root_ops, sd, &resources);if (bus)
@@ -420,8 +449,14 @@ struct pci_bus * __devinit pci_acpi_scanelse pci_free_resource_list (&resources); -if (!bus && pci_use_crs) -free_pci_root_info (&info); +if (pci_use_crs) { +if (bus) +pci_set_host_bridge_release ( +to_pci_host_bridge (bus->bridge), + release_pci_root_info, info); +else +__release_pci_root_info (info); + } }/* After the PCI-E bus has been walked and all devices discovered,
@@ -452,6 +487,11 @@ struct pci_bus * __devinit pci_acpi_scan }return bus; + +out_no_memory: +printk (KERN_WARNING "pci_bus %04x:%02x: " +"ignored (out of memory)\n" , domain, busnum); +return NULL ; }int __init pci_acpi_init (void )