[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bug#404143: Fans unreliable under load, permanent memory leak



On Sun, Dec 24, 2006 at 03:07:55AM +0100, Frederik Schueler wrote:
> 
> Hi *,
> 
> this is indeed a severe issue which requires all our attention and care
> to solve or circumvent in order for nobodies boxes to get any harm, you
> know how expensive these laptops are.
> 
> I basically see 3 solutions/workarounds:
> 
> 1. the brutal one: deactivate ACPI in 2.6.18, have the bios keep control
> of the fans - better a noisy laptop until I upgrade the kernel than a
> fried box.
> 
> 2. port 2.6.19 ACPI - noop because way too much work, unless someone 
> "crazy enough" to accomplish this task.

I have reviewed the information available on the thermal problems with 
HP laptops, and it appears that there is a fairly conservative set of 
patches which takes care of the problems (thanks to Bas for pointing 
most of the out). I might have missed some upstream bugs, so please 
let me know if there is anything else available on the issue. Below is 
the summary, describing the relevant patches:

Bug #5534: No thermal events until acpi -t - HP nx6125
------------------------------------------------------
Summary: thermal events generated by the ACPI subsystem do not get
processed by the kernel because both the interrupt due to a thermal
event and event handler are managed by the same thread (kacpid). The
solution is to create a separate thread for the handler, so that the
processing of thermal events may happen asynchronously.

I have identified the following patches which appear to finally resolve
the problem:

#8951 from comment #159		Don't defer release of the global lock.
				(applies to drivers/acpi/events/evmisc.c)
#8952 from comment #160		Create another workqueue for notify()
				execution.
				(applies to drivers/acpi/osl.c)

These patches presumably solve the problem, but the problem persists after
suspend/resume cycle. Followup patches which are supposed to improve the
situation include:

#9631 from comment #171		Improved version of #8952, which prevents
				flooding of certain machines with thermal
				events (Linus owns one of those, so he was
				very unhappy :-)
#9746 from comment #180		Some further improvements. AFAICT, supersedes
				#9631 and #8952.

So, it looks like we need #8951 and #9746 from this bug. Both apply cleanly
to our 2.6.18-8 source.

Bug #7122: Thermal management problems - HPC nx6325
---------------------------------------------------
Summary: the fans do not come on properly after resume/suspend cycle. Looks
like the reason for the problem is that the ACPI logic which turns on the
fans cannot cope with the fact that it might be needed to execute the
"power on" method for fans a few times before they actually turn on.

The following patches appear to be relevant:

#9254 from comment #37		Reset number of resource references on resume
				and make power on/off routines more strict and
				robust.
#9255 from comment #38		Make ACPI suspend handlers to occur before 
				_PTS/_GTS methods and ACPI resume handlers to
				occur after _WAK method.
#9263 from comment #41		A modification of #9254 to apply to 2.6.19-rc1-mm1

#9355 from comment #48		Implement power resource references as a list,
				so if two devices using the same power resource,
				it cannot be disabled by two subsequent calls from
				a single device. Supersedes #9254 and #9263.
#9337 from comment #52		Improved final version of #9355.

We need #9255 and #9337 from this bug. They apply cleanly to 2.6.18-8.

Bug 7570: S3: fan doesn't work properly after resume
----------------------------------------------------
Summary: one of the four fans is not turned on after suspend/resume cycle.

Relevant patch:

#9802 from comment #8		'force_power_state' flag being set, disables the
				check if the required power state is the same as
				the current one. In that case the list of power
				resources being enabled is the same as the list of
				power resources being disabled, and follows to
				consequent enabling and disabling of these resources.	

This patch may be included, even though the issue it fixes is not as critical
as the other ones. Applies fine to 2.6.18-8 too.

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.

Best regards, 
-- 
Jurij Smakov                                           jurij@wooyd.org
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-12-26 18:00:37.000000000 -0800
+++ b/drivers/acpi/bus.c	2006-09-19 20:42:06.000000000 -0700
@@ -202,14 +202,15 @@
 	 * Get device's current power state if it's unknown
 	 * This means device power state isn't initialized or previous setting failed
 	 */
-	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->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.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-12-26 18:00:22.000000000 -0800
+++ b/drivers/acpi/events/evmisc.c	2006-09-19 20:42:06.000000000 -0700
@@ -342,8 +342,20 @@
 	if (acquired) {
 
 		/* Got the lock, now wake all threads waiting for it */
+
 		acpi_gbl_global_lock_acquired = TRUE;
-		acpi_ev_global_lock_thread(context);
+
+		/* 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);
+		}
 	}
 
 	return (ACPI_INTERRUPT_HANDLED);
diff -aur a/drivers/acpi/osl.c b/drivers/acpi/osl.c
--- a/drivers/acpi/osl.c	2006-12-26 18:00:33.000000000 -0800
+++ b/drivers/acpi/osl.c	2006-09-19 20:42:06.000000000 -0700
@@ -73,7 +73,6 @@
 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)
 {
@@ -92,9 +91,8 @@
 		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;
 }
 
@@ -106,7 +104,6 @@
 	}
 
 	destroy_workqueue(kacpid_wq);
-	destroy_workqueue(kacpi_notify_wq);
 
 	return AE_OK;
 }
@@ -569,24 +566,10 @@
 
 static void acpi_os_execute_deferred(void *context)
 {
-	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();
+	struct acpi_os_dpc *dpc = NULL;
 
-	return;
-}
 
-static void acpi_os_execute_notify(void *context)
-{
-	struct acpi_os_dpc *dpc = context;
+	dpc = (struct acpi_os_dpc *)context;
 	if (!dpc) {
 		printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
 		return;
@@ -621,12 +604,14 @@
 	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 AE_BAD_PARAMETER;
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
 
 	/*
 	 * Allocate/initialize DPC structure.  Note that this memory will be
@@ -639,27 +624,23 @@
 	 * from the same memory.
 	 */
 
-	dpc = kzalloc(sizeof(struct acpi_os_dpc) +
-			sizeof(struct work_struct), GFP_ATOMIC);
+	dpc =
+	    kmalloc(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 = (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);
-		}
+	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;
 	}
 
 	return_ACPI_STATUS(status);
diff -aur a/drivers/acpi/power.c b/drivers/acpi/power.c
--- a/drivers/acpi/power.c	2006-12-26 18:00:37.000000000 -0800
+++ b/drivers/acpi/power.c	2006-09-19 20:42:06.000000000 -0700
@@ -57,7 +57,6 @@
 #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 = {
@@ -67,23 +66,16 @@
 	.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;
-	struct mutex resource_lock;
-	struct list_head reference;
+	int references;
 };
 
 static struct list_head acpi_power_resource_list;
@@ -179,47 +171,22 @@
 	return result;
 }
 
-static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
+static int acpi_power_on(acpi_handle handle)
 {
 	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;
 
-	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;
-		}
-	}
+	resource->references++;
 
-	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) {
+	if ((resource->references > 1)
+	    || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
 				  resource->name));
 		return 0;
@@ -236,49 +203,40 @@
 		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, struct acpi_device *dev)
+static int acpi_power_off_device(acpi_handle handle)
 {
 	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;
 
-	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)
+		resource->references--;
 
-	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);
+	if (resource->references) {
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+				  "Resource [%s] is still in use, dereferencing\n",
+				  device->pnp.bus_id));
 		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",
-				  resource->name));
+				  device->pnp.bus_id));
 		return 0;
 	}
 
@@ -293,7 +251,8 @@
 		return -ENOEXEC;
 
 	/* Update the power resource's _device_ power state */
-	resource->device->power.state = ACPI_STATE_D3;
+	device = resource->device;
+	device->power.state = ACPI_STATE_D3;
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
 			  resource->name));
@@ -320,7 +279,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], dev);
+		ret = acpi_power_on(dev->wakeup.resources.handles[i]);
 		if (ret) {
 			printk(KERN_ERR PREFIX "Transition power state\n");
 			dev->wakeup.flags.valid = 0;
@@ -367,7 +326,7 @@
 
 	/* Close power resource */
 	for (i = 0; i < dev->wakeup.resources.count; i++) {
-		ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
+		ret = acpi_power_off_device(dev->wakeup.resources.handles[i]);
 		if (ret) {
 			printk(KERN_ERR PREFIX "Transition power state\n");
 			dev->wakeup.flags.valid = 0;
@@ -451,20 +410,16 @@
 	 * (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], device);
+		result = acpi_power_on(tl->handles[i]);
 		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], device);
+		result = acpi_power_off_device(cl->handles[i]);
 		if (result)
 			goto end;
 	}
@@ -487,11 +442,7 @@
 
 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;
@@ -499,10 +450,6 @@
 	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:
@@ -516,18 +463,11 @@
 		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, count);
+		   resource->order, resource->references);
 
       end:
 	return 0;
@@ -601,8 +541,6 @@
 	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);
@@ -650,7 +588,6 @@
 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))
@@ -660,54 +597,11 @@
 
 	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 a/drivers/acpi: power.c.orig
diff -aur a/drivers/acpi/scan.c b/drivers/acpi/scan.c
--- a/drivers/acpi/scan.c	2006-12-26 18:00:25.000000000 -0800
+++ b/drivers/acpi/scan.c	2006-09-19 20:42:06.000000000 -0700
@@ -1353,15 +1353,18 @@
 	return result;
 }
 
-int acpi_root_suspend(void)
+
+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)
 {
 	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) {
@@ -1379,15 +1382,29 @@
 	return 0;
 }
 
-int acpi_root_resume(void)
+
+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)
 {
 	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) {
@@ -1405,6 +1422,31 @@
 	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;
@@ -1418,6 +1460,12 @@
 	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
 	 */
@@ -1426,7 +1474,17 @@
 	if (result)
 		goto Done;
 
-	acpi_start_single_object(acpi_root);
+	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");
+	}
 
 	/*
 	 * 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-12-26 18:00:25.000000000 -0800
+++ b/drivers/acpi/sleep/main.c	2006-09-19 20:42:06.000000000 -0700
@@ -47,9 +47,6 @@
 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];
@@ -58,7 +55,6 @@
 		printk("acpi_pm_prepare does not support %d \n", pm_state);
 		return -EPERM;
 	}
-	acpi_root_suspend();
 	return acpi_sleep_prepare(acpi_state);
 }
 
@@ -145,7 +141,6 @@
 
 	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-12-26 18:00:37.000000000 -0800
+++ b/drivers/acpi/thermal.c	2006-09-19 20:42:06.000000000 -0700
@@ -1359,32 +1359,28 @@
 static int acpi_thermal_resume(struct acpi_device *device, int state)
 {
 	struct acpi_thermal *tz = NULL;
-	int i, j, power_state, result;
-
+	int i;
 
 	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]))
-			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;
-			}
+		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;
 		}
-		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-12-26 18:00:25.000000000 -0800
+++ b/include/acpi/acpi_bus.h	2006-09-19 20:42:06.000000000 -0700
@@ -26,7 +26,7 @@
 #ifndef __ACPI_BUS_H__
 #define __ACPI_BUS_H__
 
-#include <linux/kobject.h>
+#include <linux/device.h>
 
 #include <acpi/acpi.h>
 
@@ -297,6 +297,7 @@
 	struct acpi_driver *driver;
 	void *driver_data;
 	struct kobject kobj;
+	struct device dev;
 };
 
 #define acpi_driver_data(d)	((d)->driver_data)

Reply to: