On Tue, Dec 26, 2006 at 06:09:02PM -0800, Jurij Smakov wrote: > So far I have not tried building the kernel with this patches, but I think > this is > a reasonable way to resolve the problem, as the resulting cumulative patch > (attached) > is only 19K.
Sorry, I made this patch reversed by mistake. Use the one attached to this message, or apply the old one with 'patch -R' :-P -- Jurij Smakov [EMAIL PROTECTED] Key: http://www.wooyd.org/pgpkey/ KeyID: C99E03CC
diff -aur a/drivers/acpi/bus.c b/drivers/acpi/bus.c --- a/drivers/acpi/bus.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/bus.c 2006-12-26 19:21:33.000000000 -0800 @@ -202,15 +202,14 @@ * Get device's current power state if it's unknown * This means device power state isn't initialized or previous setting failed */ - if (!device->flags.force_power_state) { - if (device->power.state == ACPI_STATE_UNKNOWN) - acpi_bus_get_power(device->handle, &device->power.state); - if (state == device->power.state) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", - state)); - return 0; - } + if ((device->power.state == ACPI_STATE_UNKNOWN) || device->flags.force_power_state) + acpi_bus_get_power(device->handle, &device->power.state); + if ((state == device->power.state) && !device->flags.force_power_state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", + state)); + return 0; } + if (!device->power.states[state].flags.valid) { printk(KERN_WARNING PREFIX "Device does not support D%d\n", state); return -ENODEV; diff -aur a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c --- a/drivers/acpi/events/evmisc.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/events/evmisc.c 2006-12-26 19:21:15.000000000 -0800 @@ -342,20 +342,8 @@ if (acquired) { /* Got the lock, now wake all threads waiting for it */ - acpi_gbl_global_lock_acquired = TRUE; - - /* Run the Global Lock thread which will signal all waiting threads */ - - status = - acpi_os_execute(OSL_GLOBAL_LOCK_HANDLER, - acpi_ev_global_lock_thread, context); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Could not queue Global Lock thread")); - - return (ACPI_INTERRUPT_NOT_HANDLED); - } + acpi_ev_global_lock_thread(context); } return (ACPI_INTERRUPT_HANDLED); diff -aur a/drivers/acpi/osl.c b/drivers/acpi/osl.c --- a/drivers/acpi/osl.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/osl.c 2006-12-26 19:21:30.000000000 -0800 @@ -73,6 +73,7 @@ static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; +static struct workqueue_struct *kacpi_notify_wq; acpi_status acpi_os_initialize(void) { @@ -91,8 +92,9 @@ return AE_NULL_ENTRY; } kacpid_wq = create_singlethread_workqueue("kacpid"); + kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); BUG_ON(!kacpid_wq); - + BUG_ON(!kacpi_notify_wq); return AE_OK; } @@ -104,6 +106,7 @@ } destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); return AE_OK; } @@ -566,10 +569,24 @@ static void acpi_os_execute_deferred(void *context) { - struct acpi_os_dpc *dpc = NULL; + struct acpi_os_dpc *dpc = context; + if (!dpc) { + printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); + return; + } + + dpc->function(dpc->context); + kfree(dpc); + + /* Yield cpu to notify thread */ + cond_resched(); + return; +} - dpc = (struct acpi_os_dpc *)context; +static void acpi_os_execute_notify(void *context) +{ + struct acpi_os_dpc *dpc = context; if (!dpc) { printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); return; @@ -604,14 +621,12 @@ struct acpi_os_dpc *dpc; struct work_struct *task; - ACPI_FUNCTION_TRACE("os_queue_for_execution"); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); if (!function) - return_ACPI_STATUS(AE_BAD_PARAMETER); + return AE_BAD_PARAMETER; /* * Allocate/initialize DPC structure. Note that this memory will be @@ -624,23 +639,27 @@ * from the same memory. */ - dpc = - kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct), - GFP_ATOMIC); + dpc = kzalloc(sizeof(struct acpi_os_dpc) + + sizeof(struct work_struct), GFP_ATOMIC); if (!dpc) return_ACPI_STATUS(AE_NO_MEMORY); dpc->function = function; dpc->context = context; - task = (void *)(dpc + 1); - INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc); - - if (!queue_work(kacpid_wq, task)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Call to queue_work() failed.\n")); - kfree(dpc); - status = AE_ERROR; + task = (struct work_struct *)(dpc + 1); + if (type == OSL_NOTIFY_HANDLER) { + INIT_WORK(task, acpi_os_execute_notify, (void *)dpc); + if (!queue_work(kacpi_notify_wq, task)) { + status = AE_ERROR; + kfree(dpc); + } + } else { + INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc); + if (!queue_work(kacpid_wq, task)) { + status = AE_ERROR; + kfree(dpc); + } } return_ACPI_STATUS(status); diff -aur a/drivers/acpi/power.c b/drivers/acpi/power.c --- a/drivers/acpi/power.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/power.c 2006-12-26 19:21:33.000000000 -0800 @@ -57,6 +57,7 @@ #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); +static int acpi_power_resume(struct acpi_device *device, int state); static int acpi_power_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_power_driver = { @@ -66,16 +67,23 @@ .ops = { .add = acpi_power_add, .remove = acpi_power_remove, + .resume = acpi_power_resume, }, }; +struct acpi_power_reference { + struct list_head node; + struct acpi_device *device; +}; + struct acpi_power_resource { struct acpi_device * device; acpi_bus_id name; u32 system_level; u32 order; int state; - int references; + struct mutex resource_lock; + struct list_head reference; }; static struct list_head acpi_power_resource_list; @@ -171,22 +179,47 @@ return result; } -static int acpi_power_on(acpi_handle handle) +static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) { int result = 0; + int found = 0; acpi_status status = AE_OK; - struct acpi_device *device = NULL; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; result = acpi_power_get_context(handle, &resource); if (result) return result; - resource->references++; + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + if (dev->handle == ref->device->handle) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n", + dev->pnp.bus_id, resource->name)); + found = 1; + break; + } + } - if ((resource->references > 1) - || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) { + if (!found) { + ref = kmalloc(sizeof (struct acpi_power_reference), + irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); + if (!ref) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); + mutex_unlock(&resource->resource_lock); + return -ENOMEM; + } + list_add_tail(&ref->node, &resource->reference); + ref->device = dev; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n", + dev->pnp.bus_id, resource->name)); + } + mutex_unlock(&resource->resource_lock); + + if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", resource->name)); return 0; @@ -203,40 +236,49 @@ return -ENOEXEC; /* Update the power resource's _device_ power state */ - device = resource->device; resource->device->power.state = ACPI_STATE_D0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", resource->name)); - return 0; } -static int acpi_power_off_device(acpi_handle handle) +static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) { int result = 0; acpi_status status = AE_OK; - struct acpi_device *device = NULL; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; result = acpi_power_get_context(handle, &resource); if (result) return result; - if (resource->references) - resource->references--; + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + if (dev->handle == ref->device->handle) { + list_del(&ref->node); + kfree(ref); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", + dev->pnp.bus_id, resource->name)); + break; + } + } - if (resource->references) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Resource [%s] is still in use, dereferencing\n", - device->pnp.bus_id)); + if (!list_empty(&resource->reference)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", + resource->name)); + mutex_unlock(&resource->resource_lock); return 0; } + mutex_unlock(&resource->resource_lock); if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", - device->pnp.bus_id)); + resource->name)); return 0; } @@ -251,8 +293,7 @@ return -ENOEXEC; /* Update the power resource's _device_ power state */ - device = resource->device; - device->power.state = ACPI_STATE_D3; + resource->device->power.state = ACPI_STATE_D3; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", resource->name)); @@ -279,7 +320,7 @@ arg.integer.value = 1; /* Open power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - ret = acpi_power_on(dev->wakeup.resources.handles[i]); + ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -326,7 +367,7 @@ /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); + ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -410,16 +451,20 @@ * (e.g. so the device doesn't lose power while transitioning). */ for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i]); + result = acpi_power_on(tl->handles[i], device); if (result) goto end; } + if (device->power.state == state) { + goto end; + } + /* * Then we dereference all power resources used in the current list. */ for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i]); + result = acpi_power_off_device(cl->handles[i], device); if (result) goto end; } @@ -442,7 +487,11 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) { + int count = 0; + int result = 0; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; resource = (struct acpi_power_resource *)seq->private; @@ -450,6 +499,10 @@ if (!resource) goto end; + result = acpi_power_get_state(resource); + if (result) + goto end; + seq_puts(seq, "state: "); switch (resource->state) { case ACPI_POWER_RESOURCE_STATE_ON: @@ -463,11 +516,18 @@ break; } + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + count++; + } + mutex_unlock(&resource->resource_lock); + seq_printf(seq, "system level: S%d\n" "order: %d\n" "reference count: %d\n", resource->system_level, - resource->order, resource->references); + resource->order, count); end: return 0; @@ -541,6 +601,8 @@ memset(resource, 0, sizeof(struct acpi_power_resource)); resource->device = device; + mutex_init(&resource->resource_lock); + INIT_LIST_HEAD(&resource->reference); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); @@ -588,6 +650,7 @@ static int acpi_power_remove(struct acpi_device *device, int type) { struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; if (!device || !acpi_driver_data(device)) @@ -597,11 +660,54 @@ acpi_power_remove_fs(device); + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); + list_del(&ref->node); + kfree(ref); + } + mutex_unlock(&resource->resource_lock); + kfree(resource); return 0; } +static int acpi_power_resume(struct acpi_device *device, int state) +{ + int result = 0; + struct acpi_power_resource *resource = NULL; + struct acpi_power_reference *ref; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + resource = (struct acpi_power_resource *)acpi_driver_data(device); + + result = acpi_power_get_state(resource); + if (result) + return result; + + mutex_lock(&resource->resource_lock); + if ((resource->state == ACPI_POWER_RESOURCE_STATE_ON) && + list_empty(&resource->reference)) { + mutex_unlock(&resource->resource_lock); + result = acpi_power_off_device(device->handle, NULL); + return result; + } + + if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) && + !list_empty(&resource->reference)) { + ref = container_of(resource->reference.next, struct acpi_power_reference, node); + mutex_unlock(&resource->resource_lock); + result = acpi_power_on(device->handle, ref->device); + return result; + } + + mutex_unlock(&resource->resource_lock); + return 0; +} + static int __init acpi_power_init(void) { int result = 0; Only in b/drivers/acpi: power.c.orig diff -aur a/drivers/acpi/scan.c b/drivers/acpi/scan.c --- a/drivers/acpi/scan.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/scan.c 2006-12-26 19:21:21.000000000 -0800 @@ -1353,18 +1353,15 @@ return result; } - -static inline struct acpi_device * to_acpi_dev(struct device * dev) -{ - return container_of(dev, struct acpi_device, dev); -} - - -static int root_suspend(struct acpi_device * acpi_dev, pm_message_t state) +int acpi_root_suspend(void) { struct acpi_device * dev, * next; int result; + result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &dev); + if (result) + return result; + spin_lock(&acpi_device_lock); list_for_each_entry_safe_reverse(dev, next, &acpi_device_list, g_list) { if (dev->driver && dev->driver->ops.suspend) { @@ -1382,29 +1379,15 @@ return 0; } - -static int acpi_device_suspend(struct device * dev, pm_message_t state) -{ - struct acpi_device * acpi_dev = to_acpi_dev(dev); - - /* - * For now, we should only register 1 generic device - - * the ACPI root device - and from there, we walk the - * tree of ACPI devices to suspend each one using the - * ACPI driver methods. - */ - if (acpi_dev->handle == ACPI_ROOT_OBJECT) - root_suspend(acpi_dev, state); - return 0; -} - - - -static int root_resume(struct acpi_device * acpi_dev) +int acpi_root_resume(void) { struct acpi_device * dev, * next; int result; + result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &dev); + if (result) + return result; + spin_lock(&acpi_device_lock); list_for_each_entry_safe(dev, next, &acpi_device_list, g_list) { if (dev->driver && dev->driver->ops.resume) { @@ -1422,31 +1405,6 @@ return 0; } - -static int acpi_device_resume(struct device * dev) -{ - struct acpi_device * acpi_dev = to_acpi_dev(dev); - - /* - * For now, we should only register 1 generic device - - * the ACPI root device - and from there, we walk the - * tree of ACPI devices to resume each one using the - * ACPI driver methods. - */ - if (acpi_dev->handle == ACPI_ROOT_OBJECT) - root_resume(acpi_dev); - return 0; -} - - -static struct bus_type acpi_bus_type = { - .name = "acpi", - .suspend = acpi_device_suspend, - .resume = acpi_device_resume, -}; - - - static int __init acpi_scan_init(void) { int result; @@ -1460,12 +1418,6 @@ if (result < 0) printk(KERN_ERR PREFIX "kset_register error: %d\n", result); - result = bus_register(&acpi_bus_type); - if (result) { - /* We don't want to quit even if we failed to add suspend/resume */ - printk(KERN_ERR PREFIX "Could not register bus type\n"); - } - /* * Create the root device in the bus's device tree */ @@ -1474,17 +1426,7 @@ if (result) goto Done; - result = acpi_start_single_object(acpi_root); - if (result) - goto Done; - - acpi_root->dev.bus = &acpi_bus_type; - snprintf(acpi_root->dev.bus_id, BUS_ID_SIZE, "%s", acpi_bus_type.name); - result = device_register(&acpi_root->dev); - if (result) { - /* We don't want to quit even if we failed to add suspend/resume */ - printk(KERN_ERR PREFIX "Could not register device\n"); - } + acpi_start_single_object(acpi_root); /* * Enumerate devices in the ACPI namespace. diff -aur a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c --- a/drivers/acpi/sleep/main.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/sleep/main.c 2006-12-26 19:21:21.000000000 -0800 @@ -47,6 +47,9 @@ extern int acpi_sleep_prepare(u32 acpi_state); extern void acpi_power_off(void); +extern int acpi_root_suspend(void); +extern int acpi_root_resume(void); + static int acpi_pm_prepare(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; @@ -55,6 +58,7 @@ printk("acpi_pm_prepare does not support %d \n", pm_state); return -EPERM; } + acpi_root_suspend(); return acpi_sleep_prepare(acpi_state); } @@ -141,6 +145,7 @@ acpi_leave_sleep_state(acpi_state); acpi_disable_wakeup_device(acpi_state); + acpi_root_resume(); /* reset firmware waking vector */ acpi_set_firmware_waking_vector((acpi_physical_address) 0); diff -aur a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c --- a/drivers/acpi/thermal.c 2006-09-19 20:42:06.000000000 -0700 +++ b/drivers/acpi/thermal.c 2006-12-26 19:21:33.000000000 -0800 @@ -1359,28 +1359,32 @@ static int acpi_thermal_resume(struct acpi_device *device, int state) { struct acpi_thermal *tz = NULL; - int i; + int i, j, power_state, result; + if (!device || !acpi_driver_data(device)) return -EINVAL; tz = (struct acpi_thermal *)acpi_driver_data(device); - acpi_thermal_get_temperature(tz); - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (tz->trips.active[i].flags.valid) { - tz->temperature = tz->trips.active[i].temperature; - tz->trips.active[i].flags.enabled = 0; - - acpi_thermal_active(tz); - - tz->state.active |= tz->trips.active[i].flags.enabled; - tz->state.active_index = i; + if (!(&tz->trips.active[i])) + break; + if (!tz->trips.active[i].flags.valid) + break; + tz->trips.active[i].flags.enabled = 1; + for (j = 0; j < tz->trips.active[i].devices.count; j++) { + result = acpi_bus_get_power(tz->trips.active[i].devices. + handles[j], &power_state); + if (result || (power_state != ACPI_STATE_D0)) { + tz->trips.active[i].flags.enabled = 0; + break; + } } + tz->state.active |= tz->trips.active[i].flags.enabled; } - acpi_thermal_check(tz); + acpi_thermal_check(tz); return AE_OK; } diff -aur a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h --- a/include/acpi/acpi_bus.h 2006-09-19 20:42:06.000000000 -0700 +++ b/include/acpi/acpi_bus.h 2006-12-26 19:21:21.000000000 -0800 @@ -26,7 +26,7 @@ #ifndef __ACPI_BUS_H__ #define __ACPI_BUS_H__ -#include <linux/device.h> +#include <linux/kobject.h> #include <acpi/acpi.h> @@ -297,7 +297,6 @@ struct acpi_driver *driver; void *driver_data; struct kobject kobj; - struct device dev; }; #define acpi_driver_data(d) ((d)->driver_data)