commit 3d87c12c0cf345ed6cf776522495d957e8d50f69 Author: Bjorn Helgaas Date: Tue Jul 28 11:06:18 2009 -0600 diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index dd1a6d4..f7c43fb 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -166,6 +166,12 @@ and is between 256 and 4096 characters. It is defined in the file (e.g. thinkpad_acpi, sony_acpi, etc.) instead of the ACPI video.ko driver. + acpi_bind_workqueues [ACPI] + Run ACPI workqueues only on CPU 0, to work around + BIOS defects. If this helps, report it with the + dmidecode output so future kernels can do it + automatically. + acpi.debug_layer= [HW,ACPI,ACPI_DEBUG] acpi.debug_level= [HW,ACPI,ACPI_DEBUG] Format: diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7167071..f4f1295 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -189,6 +189,78 @@ acpi_status __init acpi_os_initialize(void) return AE_OK; } +static void bind_to_cpu0(struct work_struct *work) +{ + set_cpus_allowed(current, cpumask_of_cpu(0)); + kfree(work); +} + +static void bind_workqueue(struct workqueue_struct *wq) +{ + struct work_struct *work; + + work = kzalloc(sizeof(struct work_struct), GFP_KERNEL); + INIT_WORK(work, bind_to_cpu0); + queue_work(wq, work); +} + +static int acpi_bind_workqueues; +static const char *acpi_bind_reason; + +static int bind_workqueues_to_0(void) +{ + /* + * Some machines seem to have SMI-related state corruption if software + * SMIs are initiated anywhere other than CPU 0. SMIs could be + * initiated anywhere in AML, but most problem cases seem to be in + * GPE-related AML that's executed via workqueues. + * + * Work around it by binding the ACPI workqueues to CPU 0. + */ + + printk(KERN_ERR PREFIX "%s; binding ACPI workqueues to CPU 0\n", + acpi_bind_reason); + bind_workqueue(kacpid_wq); + bind_workqueue(kacpi_notify_wq); + bind_workqueue(kacpi_hotplug_wq); + return 0; +} + +static int __init acpi_bind_setup(char *str) +{ + acpi_bind_workqueues = 1; + acpi_bind_reason = "user request"; + return 1; +} +__setup("acpi_bind_workqueues", acpi_bind_setup); + +static int acpi_bind_setup_dmi(const struct dmi_system_id *id) +{ + acpi_bind_workqueues = 1; + acpi_bind_reason = id->ident; + return 0; +} + +static const struct dmi_system_id acpi_cpu0_dmi_table[] = { + { + .callback = acpi_bind_setup_dmi, + .ident = "HP Compaq 2510p notebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 2510p"), + }, + }, + { + .callback = acpi_bind_setup_dmi, + .ident = "HP Compaq 6910p notebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 6910p"), + }, + }, + {} +}; + acpi_status acpi_os_initialize1(void) { kacpid_wq = create_singlethread_workqueue("kacpid"); @@ -197,6 +269,9 @@ acpi_status acpi_os_initialize1(void) BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); BUG_ON(!kacpi_hotplug_wq); + dmi_check_system(acpi_cpu0_dmi_table); + if (acpi_bind_workqueues) + bind_workqueues_to_0(); return AE_OK; }