From b9417f84e17b93a6976a8a88b38bf9567975cb38 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 27 Apr 2009 16:33:36 -0600 Subject: ACPI: use LNXCPU, not ACPI_CPU, for Linux-specific processor _HID ACPI_PROCESSOR_OBJECT_HID is a synthetic _HID that Linux generates for "Processor" definitions. Unlike "Device" definitions, "Processor" definitions do not have a _HID in the namespace, so we generate a fake _HID. By convention, all these fake _HIDs begin with "LNX". This does change the user-visible _HID for "Processor" objects -- previously, we used "ACPI_CPU" and this changes that to "LNXCPU", which starts with "LNX" as do all the other made-up _HIDs. This change is visible in processor filenames and "hid" files under /sys/devices/LNXSYSTM:00/. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- include/acpi/acpi_drivers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/acpi') diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 0352c8f0b05b..c9922b362005 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -57,7 +57,7 @@ */ #define ACPI_POWER_HID "LNXPOWER" -#define ACPI_PROCESSOR_OBJECT_HID "ACPI_CPU" +#define ACPI_PROCESSOR_OBJECT_HID "LNXCPU" #define ACPI_PROCESSOR_HID "ACPI0007" #define ACPI_SYSTEM_HID "LNXSYSTM" #define ACPI_THERMAL_HID "LNXTHERM" -- cgit v1.2.3 From 9eccbc2f67efd0d19c47f40182abf2965c287add Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 27 Apr 2009 16:33:46 -0600 Subject: ACPI: processor: move device _HID into driver The ACPI0007 _HID used for processor "Device" objects in the namespace is not needed outside the processor driver, so move it there. Also, the #define is only used once, so just remove it and hard-code "ACPI0007". Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 2 +- include/acpi/acpi_drivers.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index cf627d64cde9..cabff4cb21f0 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -89,7 +89,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, - {ACPI_PROCESSOR_HID, 0}, + {"ACPI0007", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index c9922b362005..5e8ed3a78cd0 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -58,7 +58,6 @@ #define ACPI_POWER_HID "LNXPOWER" #define ACPI_PROCESSOR_OBJECT_HID "LNXCPU" -#define ACPI_PROCESSOR_HID "ACPI0007" #define ACPI_SYSTEM_HID "LNXSYSTM" #define ACPI_THERMAL_HID "LNXTHERM" #define ACPI_BUTTON_HID_POWERF "LNXPWRBN" -- cgit v1.2.3 From 24c5c4c2f506bf87ef2343669fb892c944c3fdde Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 21 May 2009 16:25:35 +0800 Subject: ACPI: increase size of acpi_bus_id[] Previously [5], now [8]. sprintf(acpi_device_bid(device), "CPU%X", cpu_id) now looks better on systems with more than 0xFF processors. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- include/acpi/acpi_bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/acpi') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c34b11022908..0be24101a48f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -168,7 +168,7 @@ struct acpi_device_dir { /* Plug and Play */ -typedef char acpi_bus_id[5]; +typedef char acpi_bus_id[8]; typedef unsigned long acpi_bus_address; typedef char acpi_hardware_id[15]; typedef char acpi_unique_id[9]; -- cgit v1.2.3 From ce597bb42aa84bc73db80509b7c37e7fbc0b75c4 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:09 +0000 Subject: ACPI: make acpi_pci_bind() static acpi_pci_root_add() explicitly assigns device->ops.bind, and later calls acpi_pci_bind_root(), which also does the same thing. We don't need to repeat ourselves; removing the explicit assignment allows us to make acpi_pci_bind() static. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 3 ++- drivers/acpi/pci_root.c | 2 -- include/acpi/acpi_drivers.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index bc46de3d967f..236765c6017b 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -44,6 +44,7 @@ struct acpi_pci_data { struct pci_dev *dev; }; +static int acpi_pci_bind(struct acpi_device *device); static int acpi_pci_unbind(struct acpi_device *device); static void acpi_pci_data_handler(acpi_handle handle, u32 function, @@ -108,7 +109,7 @@ acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id) EXPORT_SYMBOL(acpi_get_pci_id); -int acpi_pci_bind(struct acpi_device *device) +static int acpi_pci_bind(struct acpi_device *device) { int result = 0; acpi_status status; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 196f97d00956..ca8dba3b40b9 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -386,8 +386,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; - device->ops.bind = acpi_pci_bind; - /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 0352c8f0b05b..7a2ce53146aa 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -99,7 +99,6 @@ void acpi_pci_irq_del_prt(int segment, int bus); struct pci_bus; acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id); -int acpi_pci_bind(struct acpi_device *device); int acpi_pci_bind_root(struct acpi_device *device, struct acpi_pci_id *id, struct pci_bus *bus); -- cgit v1.2.3 From 275582031f9b3597a1b973f3ff617adfe639faa2 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:14 +0000 Subject: ACPI: Introduce acpi_is_root_bridge() Returns whether an ACPI CA node is a PCI root bridge or not. This API is generically useful, and shouldn't just be a hotplug function. The implementation becomes much simpler as well. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_root.c | 24 +++++++++++++++++++++++ drivers/pci/hotplug/acpi_pcihp.c | 40 ++------------------------------------ drivers/pci/hotplug/acpiphp_glue.c | 2 +- include/acpi/acpi_bus.h | 1 + include/linux/pci_hotplug.h | 1 - 5 files changed, 28 insertions(+), 40 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ca8dba3b40b9..888cb9f5c5fb 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -142,6 +142,30 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); +/** + * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge + * @handle - the ACPI CA node in question. + * + * Note: we could make this API take a struct acpi_device * instead, but + * for now, it's more convenient to operate on an acpi_handle. + */ +int acpi_is_root_bridge(acpi_handle handle) +{ + int ret; + struct acpi_device *device; + + ret = acpi_bus_get_device(handle, &device); + if (ret) + return 0; + + ret = acpi_match_device_ids(device, root_device_ids); + if (ret) + return 0; + else + return 1; +} +EXPORT_SYMBOL_GPL(acpi_is_root_bridge); + static acpi_status get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index fbc63d5e459f..eb159587d0bf 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -354,7 +354,7 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, status = acpi_run_hpp(handle, hpp); if (ACPI_SUCCESS(status)) break; - if (acpi_root_bridge(handle)) + if (acpi_is_root_bridge(handle)) break; status = acpi_get_parent(handle, &phandle); if (ACPI_FAILURE(status)) @@ -428,7 +428,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) status = acpi_run_oshp(handle); if (ACPI_SUCCESS(status)) goto got_one; - if (acpi_root_bridge(handle)) + if (acpi_is_root_bridge(handle)) break; chandle = handle; status = acpi_get_parent(chandle, &handle); @@ -449,42 +449,6 @@ got_one: } EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); -/* acpi_root_bridge - check to see if this acpi object is a root bridge - * - * @handle - the acpi object in question. - */ -int acpi_root_bridge(acpi_handle handle) -{ - acpi_status status; - struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - int i; - - status = acpi_get_object_info(handle, &buffer); - if (ACPI_SUCCESS(status)) { - info = buffer.pointer; - if ((info->valid & ACPI_VALID_HID) && - !strcmp(PCI_ROOT_HID_STRING, - info->hardware_id.value)) { - kfree(buffer.pointer); - return 1; - } - if (info->valid & ACPI_VALID_CID) { - for (i=0; i < info->compatibility_id.count; i++) { - if (!strcmp(PCI_ROOT_HID_STRING, - info->compatibility_id.id[i].value)) { - kfree(buffer.pointer); - return 1; - } - } - } - kfree(buffer.pointer); - } - return 0; -} -EXPORT_SYMBOL_GPL(acpi_root_bridge); - - static int is_ejectable(acpi_handle handle) { acpi_status status; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3a6064bce561..fc6636e3300b 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1631,7 +1631,7 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) { int *count = (int *)context; - if (acpi_root_bridge(handle)) { + if (acpi_is_root_bridge(handle)) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_bridge, NULL); (*count)++; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c34b11022908..96d593ee4859 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -369,6 +369,7 @@ struct device *acpi_get_physical_pci_device(acpi_handle); /* helper */ acpi_handle acpi_get_child(acpi_handle, acpi_integer); +int acpi_is_root_bridge(acpi_handle); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle)) diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 20998746518e..a3576ef9fc74 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -226,7 +226,6 @@ struct hotplug_params { extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, struct hotplug_params *hpp); int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags); -int acpi_root_bridge(acpi_handle handle); int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle); int acpi_pci_detect_ejectable(struct pci_bus *pbus); #endif -- cgit v1.2.3 From 2f7bbceb5b6aa938024bb4dad93c410fa59ed3b9 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:20 +0000 Subject: ACPI: Introduce acpi_get_pci_dev() Convert an ACPI CA handle to a struct pci_dev. Performing this lookup dynamically allows us to get rid of the ACPI-PCI binding code, which: - eliminates struct acpi_device vs struct pci_dev lifetime issues - lays more groundwork for eliminating .start from acpi_device_ops and thus simplifying ACPI drivers - whacks out a lot of code This change lays the groundwork for eliminating much of pci_bind.c. Although pci_root.c may not be the most logical place for this change, putting it here saves us from having to export acpi_pci_find_root. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_root.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_drivers.h | 1 + 2 files changed, 82 insertions(+) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 888cb9f5c5fb..e5099919e574 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -329,6 +329,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) return NULL; } +struct acpi_handle_node { + struct list_head node; + acpi_handle handle; +}; + +/** + * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev + * @handle: the handle in question + * + * Given an ACPI CA handle, the desired PCI device is located in the + * list of PCI devices. + * + * If the device is found, its reference count is increased and this + * function returns a pointer to its data structure. The caller must + * decrement the reference count by calling pci_dev_put(). + * If no device is found, %NULL is returned. + */ +struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +{ + int dev, fn; + unsigned long long adr; + acpi_status status; + acpi_handle phandle; + struct pci_bus *pbus; + struct pci_dev *pdev = NULL; + struct acpi_handle_node *node, *tmp; + struct acpi_pci_root *root; + LIST_HEAD(device_list); + + /* + * Walk up the ACPI CA namespace until we reach a PCI root bridge. + */ + phandle = handle; + while (!acpi_is_root_bridge(phandle)) { + node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL); + if (!node) + goto out; + + INIT_LIST_HEAD(&node->node); + node->handle = phandle; + list_add(&node->node, &device_list); + + status = acpi_get_parent(phandle, &phandle); + if (ACPI_FAILURE(status)) + goto out; + } + + root = acpi_pci_find_root(phandle); + if (!root) + goto out; + + pbus = root->bus; + + /* + * Now, walk back down the PCI device tree until we return to our + * original handle. Assumes that everything between the PCI root + * bridge and the device we're looking for must be a P2P bridge. + */ + list_for_each_entry(node, &device_list, node) { + acpi_handle hnd = node->handle; + status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) + goto out; + dev = (adr >> 16) & 0xffff; + fn = adr & 0xffff; + + pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn)); + if (hnd == handle) + break; + + pbus = pdev->subordinate; + pci_dev_put(pdev); + } +out: + list_for_each_entry_safe(node, tmp, &device_list, node) + kfree(node); + + return pdev; +} +EXPORT_SYMBOL_GPL(acpi_get_pci_dev); + /** * acpi_pci_osc_control_set - commit requested control to Firmware * @handle: acpi_handle for the target ACPI object diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 7a2ce53146aa..dbe3989952ee 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -98,6 +98,7 @@ void acpi_pci_irq_del_prt(int segment, int bus); struct pci_bus; +struct pci_dev *acpi_get_pci_dev(acpi_handle); acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id); int acpi_pci_bind_root(struct acpi_device *device, struct acpi_pci_id *id, struct pci_bus *bus); -- cgit v1.2.3 From 499650de6906722184b639989b47227a362b62f8 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:30 +0000 Subject: ACPI: eviscerate pci_bind.c Now that we can dynamically convert an ACPI CA handle to a struct pci_dev at runtime, there's no need to statically bind them during boot. acpi_pci_bind/unbind are vastly simplified, and are only used to evaluate _PRT methods on P2P bridges and non-bridge children. This patch also changes the time-space tradeoff ever so slightly. Looking up the ACPI-PCI binding is never in the performance path, and by eliminating this caching, we save 24 bytes for each _ADR device in the ACPI namespace. This patch lays further groundwork to eventually eliminate the acpi_driver_ops.bind callback. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 245 +++++++------------------------------------- drivers/acpi/pci_root.c | 2 +- include/acpi/acpi_drivers.h | 3 +- 3 files changed, 39 insertions(+), 211 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index c283c29662a7..703d2a3e8012 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -24,12 +24,7 @@ */ #include -#include -#include #include -#include -#include -#include #include #include #include @@ -111,238 +106,72 @@ EXPORT_SYMBOL(acpi_get_pci_id); static int acpi_pci_unbind(struct acpi_device *device) { - int result = 0; - acpi_status status; - struct acpi_pci_data *data; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - - if (!device || !device->parent) - return -EINVAL; - - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n", - (char *) buffer.pointer)); - kfree(buffer.pointer); + struct pci_dev *dev; - status = - acpi_get_data(device->handle, acpi_pci_data_handler, - (void **)&data); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + dev = acpi_get_pci_dev(device->handle); + if (!dev) + return 0; - status = acpi_detach_data(device->handle, acpi_pci_data_handler); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to detach data from device %s", - acpi_device_bid(device))); - result = -ENODEV; - goto end; - } - if (data->dev->subordinate) { - acpi_pci_irq_del_prt(data->id.segment, data->bus->number); - } - pci_dev_put(data->dev); - kfree(data); + if (dev->subordinate) + acpi_pci_irq_del_prt(pci_domain_nr(dev->bus), + dev->subordinate->number); - end: - return result; + pci_dev_put(dev); + return 0; } static int acpi_pci_bind(struct acpi_device *device) { - int result = 0; acpi_status status; - struct acpi_pci_data *data; - struct acpi_pci_data *pdata; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; + unsigned char bus; + struct pci_dev *dev; - if (!device || !device->parent) - return -EINVAL; - - data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) { - kfree(data); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n", - (char *)buffer.pointer)); - - /* - * Segment & Bus - * ------------- - * These are obtained via the parent device's ACPI-PCI context. - */ - status = acpi_get_data(device->parent->handle, acpi_pci_data_handler, - (void **)&pdata); - if (ACPI_FAILURE(status) || !pdata || !pdata->bus) { - ACPI_EXCEPTION((AE_INFO, status, - "Invalid ACPI-PCI context for parent device %s", - acpi_device_bid(device->parent))); - result = -ENODEV; - goto end; - } - data->id.segment = pdata->id.segment; - data->id.bus = pdata->bus->number; - - /* - * Device & Function - * ----------------- - * These are simply obtained from the device's _ADR method. Note - * that a value of zero is valid. - */ - data->id.device = device->pnp.bus_address >> 16; - data->id.function = device->pnp.bus_address & 0xFFFF; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n", - data->id.segment, data->id.bus, data->id.device, - data->id.function)); - - /* - * TBD: Support slot devices (e.g. function=0xFFFF). - */ - - /* - * Locate PCI Device - * ----------------- - * Locate matching device in PCI namespace. If it doesn't exist - * this typically means that the device isn't currently inserted - * (e.g. docking station, port replicator, etc.). - */ - data->dev = pci_get_slot(pdata->bus, - PCI_DEVFN(data->id.device, data->id.function)); - if (!data->dev) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %04x:%02x:%02x.%d not present in PCI namespace\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function)); - result = -ENODEV; - goto end; - } - if (!data->dev->bus) { - printk(KERN_ERR PREFIX - "Device %04x:%02x:%02x.%d has invalid 'bus' field\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function); - result = -ENODEV; - goto end; - } + dev = acpi_get_pci_dev(device->handle); + if (!dev) + return 0; /* - * PCI Bridge? - * ----------- - * If so, set the 'bus' field and install the 'bind' function to - * facilitate callbacks for all of its children. + * Install the 'bind' function to facilitate callbacks for + * children of the P2P bridge. */ - if (data->dev->subordinate) { + if (dev->subordinate) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %04x:%02x:%02x.%d is a PCI bridge\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function)); - data->bus = data->dev->subordinate; + pci_domain_nr(dev->bus), dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn))); device->ops.bind = acpi_pci_bind; device->ops.unbind = acpi_pci_unbind; } /* - * Attach ACPI-PCI Context - * ----------------------- - * Thus binding the ACPI and PCI devices. - */ - status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to attach ACPI-PCI context to device %s", - acpi_device_bid(device))); - result = -ENODEV; - goto end; - } - - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. This code is independent of - * PCI bridges (above) to allow parsing of _PRT objects within the - * scope of non-bridge devices. Note that _PRTs within the scope of - * a PCI bridge assume the bridge's subordinate bus number. + * Evaluate and parse _PRT, if exists. This code allows parsing of + * _PRT objects within the scope of non-bridge devices. Note that + * _PRTs within the scope of a PCI bridge assume the bridge's + * subordinate bus number. * * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? */ status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) { - if (data->bus) /* PCI-PCI bridge */ - acpi_pci_irq_add_prt(device->handle, data->id.segment, - data->bus->number); - else /* non-bridge PCI device */ - acpi_pci_irq_add_prt(device->handle, data->id.segment, - data->id.bus); - } - - end: - kfree(buffer.pointer); - if (result) { - pci_dev_put(data->dev); - kfree(data); - } - return result; -} + if (ACPI_FAILURE(status)) + goto out; -int -acpi_pci_bind_root(struct acpi_device *device, - struct acpi_pci_id *id, struct pci_bus *bus) -{ - int result = 0; - acpi_status status; - struct acpi_pci_data *data = NULL; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + if (dev->subordinate) + bus = dev->subordinate->number; + else + bus = dev->bus->number; - if (!device || !id || !bus) { - return -EINVAL; - } + acpi_pci_irq_add_prt(device->handle, pci_domain_nr(dev->bus), bus); - data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) - return -ENOMEM; +out: + pci_dev_put(dev); + return 0; +} - data->id = *id; - data->bus = bus; +int acpi_pci_bind_root(struct acpi_device *device) +{ device->ops.bind = acpi_pci_bind; device->ops.unbind = acpi_pci_unbind; - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) { - kfree (data); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to " - "%04x:%02x\n", (char *)buffer.pointer, - id->segment, id->bus)); - - status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to attach ACPI-PCI context to device %s", - (char *)buffer.pointer)); - result = -ENODEV; - goto end; - } - - end: - kfree(buffer.pointer); - if (result != 0) - kfree(data); - - return result; + return 0; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index e5099919e574..f23fcc5c9674 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -603,7 +603,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) * ----------------------- * Thus binding the ACPI and PCI devices. */ - result = acpi_pci_bind_root(device, &root->id, root->bus); + result = acpi_pci_bind_root(device); if (result) goto end; diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index dbe3989952ee..2740a2894837 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -100,8 +100,7 @@ struct pci_bus; struct pci_dev *acpi_get_pci_dev(acpi_handle); acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id); -int acpi_pci_bind_root(struct acpi_device *device, struct acpi_pci_id *id, - struct pci_bus *bus); +int acpi_pci_bind_root(struct acpi_device *device); /* Arch-defined function to add a bus to the system */ -- cgit v1.2.3 From 859a3f86ca83346f4097e956d0b27d96aa7a1cff Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:35 +0000 Subject: ACPI: simplify acpi_pci_irq_add_prt() API A PCI domain cannot change as you descend down subordinate buses, which makes the 'segment' argument to acpi_pci_irq_add_prt() useless. Change the interface to take a struct pci_bus *, from whence we can derive the bus number and segment. Reducing the number of arguments makes life simpler for callers. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 8 ++++---- drivers/acpi/pci_irq.c | 10 +++++----- drivers/acpi/pci_root.c | 3 +-- include/acpi/acpi_drivers.h | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 703d2a3e8012..6eb58ef366ef 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -124,7 +124,7 @@ static int acpi_pci_bind(struct acpi_device *device) { acpi_status status; acpi_handle handle; - unsigned char bus; + struct pci_bus *bus; struct pci_dev *dev; dev = acpi_get_pci_dev(device->handle); @@ -157,11 +157,11 @@ static int acpi_pci_bind(struct acpi_device *device) goto out; if (dev->subordinate) - bus = dev->subordinate->number; + bus = dev->subordinate; else - bus = dev->bus->number; + bus = dev->bus; - acpi_pci_irq_add_prt(device->handle, pci_domain_nr(dev->bus), bus); + acpi_pci_irq_add_prt(device->handle, bus); out: pci_dev_put(dev); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 51b9f8280f88..3ed944cefb36 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -182,7 +182,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, +static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus, struct acpi_pci_routing_table *prt) { struct acpi_prt_entry *entry; @@ -196,8 +196,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert * it here. */ - entry->id.segment = segment; - entry->id.bus = bus; + entry->id.segment = pci_domain_nr(bus); + entry->id.bus = bus->number; entry->id.device = (prt->address >> 16) & 0xFFFF; entry->pin = prt->pin + 1; @@ -242,7 +242,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -271,7 +271,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + acpi_pci_irq_add_entry(handle, bus, entry); entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index f23fcc5c9674..f341b0756c9e 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -614,8 +614,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) */ status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->id.segment, - root->id.bus); + result = acpi_pci_irq_add_prt(device->handle, root->bus); /* * Scan and bind all _ADR-Based Devices diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 2740a2894837..686a960bde39 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -91,7 +91,7 @@ int acpi_pci_link_free_irq(acpi_handle handle); /* ACPI PCI Interrupt Routing (pci_irq.c) */ -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus); +int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus); void acpi_pci_irq_del_prt(int segment, int bus); /* ACPI PCI Device Binding (pci_bind.c) */ -- cgit v1.2.3 From d9efae3688addb15994c9ad9761dada6f988bc14 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:40 +0000 Subject: ACPI: simplify acpi_pci_irq_del_prt() API There is no need to pass a segment/bus tuple to this API, as the callsite always has a struct pci_bus. We can derive segment/bus from the struct pci_bus, so let's take this opportunit to simplify the API and make life easier for the callers. Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 3 +-- drivers/acpi/pci_irq.c | 7 ++++--- include/acpi/acpi_drivers.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 6eb58ef366ef..62cb383222f8 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -113,8 +113,7 @@ static int acpi_pci_unbind(struct acpi_device *device) return 0; if (dev->subordinate) - acpi_pci_irq_del_prt(pci_domain_nr(dev->bus), - dev->subordinate->number); + acpi_pci_irq_del_prt(dev->subordinate); pci_dev_put(dev); return 0; diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 3ed944cefb36..ef9509e33191 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -280,16 +280,17 @@ int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) +void acpi_pci_irq_del_prt(struct pci_bus *bus) { struct acpi_prt_entry *entry, *tmp; printk(KERN_DEBUG "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); + pci_domain_nr(bus), bus->number); spin_lock(&acpi_prt_lock); list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { + if (pci_domain_nr(bus) == entry->id.segment + && bus->number == entry->id.bus) { list_del(&entry->list); kfree(entry); } diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 686a960bde39..98ebaaedc07f 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -92,7 +92,7 @@ int acpi_pci_link_free_irq(acpi_handle handle); /* ACPI PCI Interrupt Routing (pci_irq.c) */ int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus); -void acpi_pci_irq_del_prt(int segment, int bus); +void acpi_pci_irq_del_prt(struct pci_bus *bus); /* ACPI PCI Device Binding (pci_bind.c) */ -- cgit v1.2.3 From 80ffdedf6020a77adcd06c01cfe6c488312b28f8 Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:55:55 +0000 Subject: ACPI: kill acpi_get_pci_id acpi_get_pci_dev() is better, and all callers have been converted, so eliminate acpi_get_pci_id(). Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 71 --------------------------------------------- include/acpi/acpi_drivers.h | 1 - 2 files changed, 72 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index a205769f1d00..a5a77b78a723 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -33,77 +33,6 @@ #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_bind"); -struct acpi_pci_data { - struct acpi_pci_id id; - struct pci_bus *bus; - struct pci_dev *dev; -}; - -static int acpi_pci_bind(struct acpi_device *device); -static int acpi_pci_unbind(struct acpi_device *device); - -static void acpi_pci_data_handler(acpi_handle handle, u32 function, - void *context) -{ - - /* TBD: Anything we need to do here? */ - - return; -} - -/** - * acpi_get_pci_id - * ------------------ - * This function is used by the ACPI Interpreter (a.k.a. Core Subsystem) - * to resolve PCI information for ACPI-PCI devices defined in the namespace. - * This typically occurs when resolving PCI operation region information. - */ -acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id) -{ - int result = 0; - acpi_status status = AE_OK; - struct acpi_device *device = NULL; - struct acpi_pci_data *data = NULL; - - - if (!id) - return AE_BAD_PARAMETER; - - result = acpi_bus_get_device(handle, &device); - if (result) { - printk(KERN_ERR PREFIX - "Invalid ACPI Bus context for device %s\n", - acpi_device_bid(device)); - return AE_NOT_EXIST; - } - - status = acpi_get_data(handle, acpi_pci_data_handler, (void **)&data); - if (ACPI_FAILURE(status) || !data) { - ACPI_EXCEPTION((AE_INFO, status, - "Invalid ACPI-PCI context for device %s", - acpi_device_bid(device))); - return status; - } - - *id = data->id; - - /* - id->segment = data->id.segment; - id->bus = data->id.bus; - id->device = data->id.device; - id->function = data->id.function; - */ - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %s has PCI address %04x:%02x:%02x.%d\n", - acpi_device_bid(device), id->segment, id->bus, - id->device, id->function)); - - return AE_OK; -} - -EXPORT_SYMBOL(acpi_get_pci_id); - static int acpi_pci_unbind(struct acpi_device *device) { struct pci_dev *dev; diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 98ebaaedc07f..b69285773177 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -99,7 +99,6 @@ void acpi_pci_irq_del_prt(struct pci_bus *bus); struct pci_bus; struct pci_dev *acpi_get_pci_dev(acpi_handle); -acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id); int acpi_pci_bind_root(struct acpi_device *device); /* Arch-defined function to add a bus to the system */ -- cgit v1.2.3 From 7fe2a6c275a5bcec52fb3ef643daaf8265b7af0d Mon Sep 17 00:00:00 2001 From: Alexander Chiang Date: Wed, 10 Jun 2009 19:56:05 +0000 Subject: ACPI: kill acpi_get_physical_pci_device() acpi_get_pci_dev() is (hopefully) better, and all callers have been converted, so let's get rid of this duplicated functionality. Cc: Thomas Renninger Signed-off-by: Alex Chiang Acked-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/acpi/glue.c | 40 ---------------------------------------- include/acpi/acpi_bus.h | 1 - 2 files changed, 41 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 8bd2c2a6884d..a8a5c29958c8 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -140,46 +140,6 @@ struct device *acpi_get_physical_device(acpi_handle handle) EXPORT_SYMBOL(acpi_get_physical_device); -/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge - * This should work in general, but did not on a Lenovo T61 for the - * graphics card. But this must be fixed when the PCI device is - * bound and the kernel device struct is attached to the acpi device - * Note: A success call will increase reference count by one - * Do call put_device(dev) on the returned device then - */ -struct device *acpi_get_physical_pci_device(acpi_handle handle) -{ - struct device *dev; - long long device_id; - acpi_status status; - - status = - acpi_evaluate_integer(handle, "_ADR", NULL, &device_id); - - if (ACPI_FAILURE(status)) - return NULL; - - /* We need to attempt to determine whether the _ADR refers to a - PCI device or not. There's no terribly good way to do this, - so the best we can hope for is to assume that there'll never - be a device in the host bridge */ - if (device_id >= 0x10000) { - /* It looks like a PCI device. Does it exist? */ - dev = acpi_get_physical_device(handle); - } else { - /* It doesn't look like a PCI device. Does its parent - exist? */ - acpi_handle phandle; - if (acpi_get_parent(handle, &phandle)) - return NULL; - dev = acpi_get_physical_device(phandle); - } - if (!dev) - return NULL; - return dev; -} -EXPORT_SYMBOL(acpi_get_physical_pci_device); - static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 96d593ee4859..700b263c898a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -365,7 +365,6 @@ struct acpi_bus_type { int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); struct device *acpi_get_physical_device(acpi_handle); -struct device *acpi_get_physical_pci_device(acpi_handle); /* helper */ acpi_handle acpi_get_child(acpi_handle, acpi_integer); -- cgit v1.2.3 From 7b768f07dce463a054c9dd84862d15ccc3d2b712 Mon Sep 17 00:00:00 2001 From: "Pallipadi, Venkatesh" Date: Fri, 19 Jun 2009 17:14:59 -0700 Subject: ACPI: pdc init related memory leak with physical CPU hotplug arch_acpi_processor_cleanup_pdc() in x86 and ia64 results in memory allocated for _PDC objects that is never freed and will cause memory leak in case of physical CPU remove and add. Patch fixes the memory leak by freeing the objects soon after _PDC is evaluated. Reported-by: Bjorn Helgaas Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- arch/ia64/kernel/acpi-processor.c | 12 ++++++++++++ arch/x86/kernel/acpi/processor.c | 13 +++++++++++++ drivers/acpi/processor_core.c | 2 ++ include/acpi/processor.h | 1 + 4 files changed, 28 insertions(+) (limited to 'include/acpi') diff --git a/arch/ia64/kernel/acpi-processor.c b/arch/ia64/kernel/acpi-processor.c index cbe6cee5a550..dbda7bde6112 100644 --- a/arch/ia64/kernel/acpi-processor.c +++ b/arch/ia64/kernel/acpi-processor.c @@ -71,3 +71,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr) } EXPORT_SYMBOL(arch_acpi_processor_init_pdc); + +void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr) +{ + if (pr->pdc) { + kfree(pr->pdc->pointer->buffer.pointer); + kfree(pr->pdc->pointer); + kfree(pr->pdc); + pr->pdc = NULL; + } +} + +EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc); diff --git a/arch/x86/kernel/acpi/processor.c b/arch/x86/kernel/acpi/processor.c index 7c074eec39fb..d296f4a195c9 100644 --- a/arch/x86/kernel/acpi/processor.c +++ b/arch/x86/kernel/acpi/processor.c @@ -72,6 +72,7 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) return; } + /* Initialize _PDC data based on the CPU vendor */ void arch_acpi_processor_init_pdc(struct acpi_processor *pr) { @@ -85,3 +86,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr) } EXPORT_SYMBOL(arch_acpi_processor_init_pdc); + +void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr) +{ + if (pr->pdc) { + kfree(pr->pdc->pointer->buffer.pointer); + kfree(pr->pdc->pointer); + kfree(pr->pdc); + pr->pdc = NULL; + } +} + +EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 23f0fb84f1c1..d40d45e904a5 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -731,6 +731,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) /* _PDC call should be done before doing anything else (if reqd.). */ arch_acpi_processor_init_pdc(pr); acpi_processor_set_pdc(pr); + arch_acpi_processor_cleanup_pdc(pr); + #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr); #endif diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 4927c063347c..baf1e0a9a7ee 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -258,6 +258,7 @@ DECLARE_PER_CPU(struct acpi_processor *, processors); extern struct acpi_processor_errata errata; void arch_acpi_processor_init_pdc(struct acpi_processor *pr); +void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr); #ifdef ARCH_HAS_POWER_INIT void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, -- cgit v1.2.3 From 86e437f077c68112edcb6854ec036ed7e3f9a7f3 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 16 Jun 2009 11:23:13 +0800 Subject: ACPI: Add the reference count to avoid unloading ACPI video bus twice Sometimes both acpi video and i915 driver are compiled as modules. And there exists the strict dependency between the two drivers. The acpi video bus will be unloaded in course of unloading the i915 driver. If we unload the acpi video driver, then the kernel oops will be triggered. Add the reference count to avoid unloading the ACPI video bus twice. The reference count should be checked before unregistering the acpi video bus. If the reference count is already zero, it won't unregister it again. And after the acpi video bus is already unregistered, the reference count will be set to zero. http://bugzilla.kernel.org/show_bug.cgi?id=13396 Signed-off-by: Zhao Yakui Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 41 ++++++++++++++++++++++++++++++------ drivers/gpu/drm/i915/i915_opregion.c | 2 +- include/acpi/video.h | 4 ++-- 3 files changed, 38 insertions(+), 9 deletions(-) (limited to 'include/acpi') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 1bdfb37377e3..a63566ba230b 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -76,6 +76,7 @@ MODULE_LICENSE("GPL"); static int brightness_switch_enabled = 1; module_param(brightness_switch_enabled, bool, 0644); +static int register_count = 0; static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device, int type); static int acpi_video_resume(struct acpi_device *device); @@ -2318,6 +2319,13 @@ static int __init intel_opregion_present(void) int acpi_video_register(void) { int result = 0; + if (register_count) { + /* + * if the function of acpi_video_register is already called, + * don't register the acpi_vide_bus again and return no error. + */ + return 0; + } acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) @@ -2329,10 +2337,35 @@ int acpi_video_register(void) return -ENODEV; } + /* + * When the acpi_video_bus is loaded successfully, increase + * the counter reference. + */ + register_count = 1; + return 0; } EXPORT_SYMBOL(acpi_video_register); +void acpi_video_unregister(void) +{ + if (!register_count) { + /* + * If the acpi video bus is already unloaded, don't + * unload it again and return directly. + */ + return; + } + acpi_bus_unregister_driver(&acpi_video_bus); + + remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + + register_count = 0; + + return; +} +EXPORT_SYMBOL(acpi_video_unregister); + /* * This is kind of nasty. Hardware using Intel chipsets may require * the video opregion code to be run first in order to initialise @@ -2350,16 +2383,12 @@ static int __init acpi_video_init(void) return acpi_video_register(); } -void acpi_video_exit(void) +static void __exit acpi_video_exit(void) { - - acpi_bus_unregister_driver(&acpi_video_bus); - - remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + acpi_video_unregister(); return; } -EXPORT_SYMBOL(acpi_video_exit); module_init(acpi_video_init); module_exit(acpi_video_exit); diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index dc425e74a268..e4b4e8898e39 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -419,7 +419,7 @@ void intel_opregion_free(struct drm_device *dev, int suspend) return; if (!suspend) - acpi_video_exit(); + acpi_video_unregister(); opregion->acpi->drdy = 0; diff --git a/include/acpi/video.h b/include/acpi/video.h index af6fe95fd3d0..cf7be3dd157b 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -3,10 +3,10 @@ #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) extern int acpi_video_register(void); -extern int acpi_video_exit(void); +extern void acpi_video_unregister(void); #else static inline int acpi_video_register(void) { return 0; } -static inline void acpi_video_exit(void) { return; } +static inline void acpi_video_unregister(void) { return; } #endif #endif -- cgit v1.2.3