diff options
author | Yinghai Lu <yinghai@kernel.org> | 2012-04-02 21:31:53 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-04-30 16:52:43 -0400 |
commit | fd3b0c1ea482e863d6a2556b6686e35bec7a4f1c (patch) | |
tree | 645d20726707748daec683723c7fb81477a3beb6 /arch/x86/pci | |
parent | 9a03d28d9490b5a04f8b1d98fc08067c6f4f6189 (diff) |
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 <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'arch/x86/pci')
-rw-r--r-- | arch/x86/pci/acpi.c | 63 |
1 files 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, | |||
315 | } | 315 | } |
316 | } | 316 | } |
317 | 317 | ||
318 | static void free_pci_root_info(struct pci_root_info *info) | 318 | static void free_pci_root_info_res(struct pci_root_info *info) |
319 | { | 319 | { |
320 | kfree(info->name); | 320 | kfree(info->name); |
321 | kfree(info->res); | 321 | kfree(info->res); |
322 | memset(info, 0, sizeof(struct pci_root_info)); | 322 | info->res = NULL; |
323 | info->res_num = 0; | ||
324 | } | ||
325 | |||
326 | static void __release_pci_root_info(struct pci_root_info *info) | ||
327 | { | ||
328 | int i; | ||
329 | struct resource *res; | ||
330 | |||
331 | for (i = 0; i < info->res_num; i++) { | ||
332 | res = &info->res[i]; | ||
333 | |||
334 | if (!res->parent) | ||
335 | continue; | ||
336 | |||
337 | if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) | ||
338 | continue; | ||
339 | |||
340 | release_resource(res); | ||
341 | } | ||
342 | |||
343 | free_pci_root_info_res(info); | ||
344 | |||
345 | kfree(info); | ||
346 | } | ||
347 | static void release_pci_root_info(struct pci_host_bridge *bridge) | ||
348 | { | ||
349 | struct pci_root_info *info = bridge->release_data; | ||
350 | |||
351 | __release_pci_root_info(info); | ||
323 | } | 352 | } |
324 | 353 | ||
325 | static void | 354 | static void |
@@ -352,7 +381,7 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, | |||
352 | struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | 381 | struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) |
353 | { | 382 | { |
354 | struct acpi_device *device = root->device; | 383 | struct acpi_device *device = root->device; |
355 | struct pci_root_info info; | 384 | struct pci_root_info *info = NULL; |
356 | int domain = root->segment; | 385 | int domain = root->segment; |
357 | int busnum = root->secondary.start; | 386 | int busnum = root->secondary.start; |
358 | LIST_HEAD(resources); | 387 | LIST_HEAD(resources); |
@@ -397,7 +426,13 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
397 | 426 | ||
398 | sd->domain = domain; | 427 | sd->domain = domain; |
399 | sd->node = node; | 428 | sd->node = node; |
400 | memset(&info, 0, sizeof(struct pci_root_info)); | 429 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
430 | if (!info) { | ||
431 | kfree(sd); | ||
432 | printk(KERN_WARNING "pci_bus %04x:%02x: " | ||
433 | "ignored (out of memory)\n", domain, busnum); | ||
434 | return NULL; | ||
435 | } | ||
401 | /* | 436 | /* |
402 | * Maybe the desired pci bus has been already scanned. In such case | 437 | * Maybe the desired pci bus has been already scanned. In such case |
403 | * it is unnecessary to scan the pci bus with the given domain,busnum. | 438 | * 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) | |||
409 | * be replaced by sd. | 444 | * be replaced by sd. |
410 | */ | 445 | */ |
411 | memcpy(bus->sysdata, sd, sizeof(*sd)); | 446 | memcpy(bus->sysdata, sd, sizeof(*sd)); |
447 | kfree(info); | ||
412 | kfree(sd); | 448 | kfree(sd); |
413 | } else { | 449 | } else { |
414 | probe_pci_root_info(&info, device, busnum, domain); | 450 | probe_pci_root_info(info, device, busnum, domain); |
415 | 451 | ||
416 | /* | 452 | /* |
417 | * _CRS with no apertures is normal, so only fall back to | 453 | * _CRS with no apertures is normal, so only fall back to |
418 | * defaults or native bridge info if we're ignoring _CRS. | 454 | * defaults or native bridge info if we're ignoring _CRS. |
419 | */ | 455 | */ |
420 | if (pci_use_crs) | 456 | if (pci_use_crs) |
421 | add_resources(&info, &resources); | 457 | add_resources(info, &resources); |
422 | else { | 458 | else { |
423 | free_pci_root_info(&info); | 459 | free_pci_root_info_res(info); |
424 | x86_pci_root_bus_resources(busnum, &resources); | 460 | x86_pci_root_bus_resources(busnum, &resources); |
425 | } | 461 | } |
462 | |||
426 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, | 463 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, |
427 | &resources); | 464 | &resources); |
428 | if (bus) | 465 | if (bus) { |
429 | bus->subordinate = pci_scan_child_bus(bus); | 466 | bus->subordinate = pci_scan_child_bus(bus); |
430 | else | 467 | pci_set_host_bridge_release( |
468 | to_pci_host_bridge(bus->bridge), | ||
469 | release_pci_root_info, info); | ||
470 | } else { | ||
431 | pci_free_resource_list(&resources); | 471 | pci_free_resource_list(&resources); |
432 | 472 | __release_pci_root_info(info); | |
433 | if (!bus && pci_use_crs) | 473 | } |
434 | free_pci_root_info(&info); | ||
435 | } | 474 | } |
436 | 475 | ||
437 | /* After the PCI-E bus has been walked and all devices discovered, | 476 | /* After the PCI-E bus has been walked and all devices discovered, |