From 115684961a335a1c97074158e8f789118ac8b00d Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:41:35 +0100 Subject: [PATCH 01/11] GHES: Carve out error queueing in a separate function Make the handler more readable. No functionality change. Signed-off-by: Borislav Petkov --- drivers/acpi/apei/ghes.c | 51 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index e82d0976a5d0..fe1e41bf5609 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -797,6 +797,32 @@ static void ghes_print_queued_estatus(void) } } +/* Save estatus for further processing in IRQ context */ +static void __process_error(struct ghes *ghes) +{ +#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + u32 len, node_len; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic_status *estatus; + + if (ghes_estatus_cached(ghes->estatus)) + return; + + len = cper_estatus_len(ghes->estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + + estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len); + if (!estatus_node) + return; + + estatus_node->ghes = ghes; + estatus_node->generic = ghes->generic; + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + memcpy(estatus, ghes->estatus, len); + llist_add(&estatus_node->llnode, &ghes_estatus_llist); +#endif +} + static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { struct ghes *ghes, *ghes_global = NULL; @@ -832,32 +858,13 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) } list_for_each_entry_rcu(ghes, &ghes_nmi, list) { -#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - u32 len, node_len; - struct ghes_estatus_node *estatus_node; - struct acpi_hest_generic_status *estatus; -#endif if (!(ghes->flags & GHES_TO_CLEAR)) continue; -#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - if (ghes_estatus_cached(ghes->estatus)) - goto next; - /* Save estatus for further processing in IRQ context */ - len = cper_estatus_len(ghes->estatus); - node_len = GHES_ESTATUS_NODE_LEN(len); - estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, - node_len); - if (estatus_node) { - estatus_node->ghes = ghes; - estatus_node->generic = ghes->generic; - estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - memcpy(estatus, ghes->estatus, len); - llist_add(&estatus_node->llnode, &ghes_estatus_llist); - } -next: -#endif + + __process_error(ghes); ghes_clear_estatus(ghes); } + #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif From e10be03f603d521d5c8ac0bb0f48e5723ce19d58 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:52:39 +0100 Subject: [PATCH 02/11] GHES: Carve out the panic functionality ... into another function for more clarity. No functionality change. Signed-off-by: Borislav Petkov --- drivers/acpi/apei/ghes.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index fe1e41bf5609..712ed95b1dca 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -823,6 +823,18 @@ static void __process_error(struct ghes *ghes) #endif } +static void __ghes_panic(struct ghes *ghes) +{ + oops_begin(); + ghes_print_queued_estatus(); + __ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus); + + /* reboot to log the error! */ + if (panic_timeout == 0) + panic_timeout = ghes_panic_timeout; + panic("Fatal hardware error!"); +} + static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { struct ghes *ghes, *ghes_global = NULL; @@ -846,16 +858,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (ret == NMI_DONE) goto out; - if (sev_global >= GHES_SEV_PANIC) { - oops_begin(); - ghes_print_queued_estatus(); - __ghes_print_estatus(KERN_EMERG, ghes_global->generic, - ghes_global->estatus); - /* reboot to log the error! */ - if (panic_timeout == 0) - panic_timeout = ghes_panic_timeout; - panic("Fatal hardware error!"); - } + if (sev_global >= GHES_SEV_PANIC) + __ghes_panic(ghes_global); list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) From 6169ddf846c528509e66a0fe7804393aa330a970 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:55:21 +0100 Subject: [PATCH 03/11] GHES: Panic right after detection The moment we log an error of panic severity, there's no need to noodle through the ghes_nmi list anymore. So panic instead right then and there. Signed-off-by: Borislav Petkov --- drivers/acpi/apei/ghes.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 712ed95b1dca..0de3adcca03e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -837,9 +837,8 @@ static void __ghes_panic(struct ghes *ghes) static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { - struct ghes *ghes, *ghes_global = NULL; - int sev, sev_global = -1; - int ret = NMI_DONE; + struct ghes *ghes; + int sev, ret = NMI_DONE; raw_spin_lock(&ghes_nmi_lock); list_for_each_entry_rcu(ghes, &ghes_nmi, list) { @@ -847,20 +846,17 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) ghes_clear_estatus(ghes); continue; } + sev = ghes_severity(ghes->estatus->error_severity); - if (sev > sev_global) { - sev_global = sev; - ghes_global = ghes; - } + if (sev >= GHES_SEV_PANIC) + __ghes_panic(ghes); + ret = NMI_HANDLED; } if (ret == NMI_DONE) goto out; - if (sev_global >= GHES_SEV_PANIC) - __ghes_panic(ghes_global); - list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) continue; From 2383844d4850888cfdf6d202563d2ddb4125a4e9 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 10:12:35 +0100 Subject: [PATCH 04/11] GHES: Elliminate double-loop in the NMI handler There's no real need to iterate twice over the HW error sources in the NMI handler. With the previous cleanups, elliminating the second loop is almost trivial. Signed-off-by: Borislav Petkov --- drivers/acpi/apei/ghes.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0de3adcca03e..94a44bad5576 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -851,25 +851,18 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (sev >= GHES_SEV_PANIC) __ghes_panic(ghes); - ret = NMI_HANDLED; - } - - if (ret == NMI_DONE) - goto out; - - list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) continue; __process_error(ghes); ghes_clear_estatus(ghes); + + ret = NMI_HANDLED; } #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif - -out: raw_spin_unlock(&ghes_nmi_lock); return ret; } From 6fe9e7c26a97105645fd24f264f1b94e21aade3e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 27 Mar 2015 10:05:00 +0100 Subject: [PATCH 05/11] GHES: Make NMI handler have a single reader Since GHES sources are global, we theoretically need only a single CPU reading them per NMI instead of a thundering herd of CPUs waiting on a spinlock in NMI context for no reason at all. Do that. Signed-off-by: Jiri Kosina Signed-off-by: Borislav Petkov --- drivers/acpi/apei/ghes.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 94a44bad5576..2bfd53cbfe80 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -729,10 +729,10 @@ static struct llist_head ghes_estatus_llist; static struct irq_work ghes_proc_irq_work; /* - * NMI may be triggered on any CPU, so ghes_nmi_lock is used for - * mutual exclusion. + * NMI may be triggered on any CPU, so ghes_in_nmi is used for + * having only one concurrent reader. */ -static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); +static atomic_t ghes_in_nmi = ATOMIC_INIT(0); static LIST_HEAD(ghes_nmi); @@ -840,7 +840,9 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) struct ghes *ghes; int sev, ret = NMI_DONE; - raw_spin_lock(&ghes_nmi_lock); + if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) + return ret; + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (ghes_read_estatus(ghes, 1)) { ghes_clear_estatus(ghes); @@ -863,7 +865,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif - raw_spin_unlock(&ghes_nmi_lock); + atomic_dec(&ghes_in_nmi); return ret; } From 6656bde5ec868d89cc803539f9edf85a89497b6a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2015 22:41:05 +0200 Subject: [PATCH 06/11] ACPI / PM: Drop stale comment from acpi_power_transition() An old comment in acpi_power_transition() indicates that support for ordering power resources needs to be added, but the current code handles that already. Drop the comment to avoid confusion. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index e0bcfb642b52..59a6bf707f91 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -710,8 +710,6 @@ int acpi_power_transition(struct acpi_device *device, int state) || (device->power.state > ACPI_STATE_D3_COLD)) return -ENODEV; - /* TBD: Resources must be ordered. */ - /* * First we reference all power resources required in the target list * (e.g. so the device doesn't lose power while transitioning). Then, From 2bad7e27e02ce0984c17e4074c63e7691291244f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 14 May 2015 15:31:25 +0200 Subject: [PATCH 07/11] ACPI / osl: use same type for acpi_predefined_names values as in definition In the definition of struct acpi_predefined_names, value is of type char *. Make the OSL override function also work with type char * (or, more precisely, with a pointer to it). Signed-off-by: Dominik Brodowski Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 2 +- include/acpi/acpiosxf.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 39748bb3a543..7a327b24df95 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -543,7 +543,7 @@ static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN]; acpi_status acpi_os_predefined_override(const struct acpi_predefined_names *init_val, - acpi_string * new_val) + char **new_val) { if (!init_val || !new_val) return AE_BAD_PARAMETER; diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 0bc78df66d4b..d02df0a49d98 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -95,7 +95,7 @@ acpi_physical_address acpi_os_get_root_pointer(void); #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_predefined_override acpi_status acpi_os_predefined_override(const struct acpi_predefined_names *init_val, - acpi_string * new_val); + char **new_val); #endif #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_table_override From 20dacb71ad283b9506ee7e01286a424999fb8309 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 May 2015 01:55:35 +0200 Subject: [PATCH 08/11] ACPI / PM: Rework device power management to follow ACPI 6 The ACPI 6 specification has made some changes in the device power management area. In particular: * The D3hot power state is now supposed to be always available (instead of D3cold) and D3cold is only regarded as valid if the _PR3 object is present for the given device. * The required ordering of transitions into power states deeper than D0 is now such that for a transition into state Dx the _PSx method is supposed to be executed first, if present, and the states of the power resources the device depends on are supposed to be changed after that. * It is now explicitly forbidden to transition devices from lower-power (deeper) into higher-power (shallower) power states other than D0. Those changes have been made so the specification reflects the Windows' device power management code that the vast majority of systems using ACPI is validated against. To avoid artificial differences in ACPI device power management between Windows and Linux, modify the ACPI device power management code to follow the new specification. Add comments explaining the code flow in some unclear places. This only may affect some real corner cases in which the OS behavior expected by the firmware is different from the Windows one, but that's quite unlikely. The transition ordering change affects transitions to D1 and D2 which are rarely used (if at all) and into D3hot and D3cold for devices actually having _PR3, but those are likely to be validated against Windows anyway. The other changes may affect code calling acpi_device_get_power() or acpi_device_update_power() where ACPI_STATE_D3_HOT may be returned instead of ACPI_STATE_D3_COLD (that's why the ACPI fan driver needs to be updated too) and since transitions into ACPI_STATE_D3_HOT may remove power now, it is better to avoid this one in acpi_pm_device_sleep_state() if the "no power off" PM QoS flag is set. The only existing user of acpi_device_can_poweroff() really cares about the case when _PR3 is present, so the change in that function should not cause any problems to happen too. A plus is that PCI_D3hot can be mapped to ACPI_STATE_D3_HOT now and the compatibility with older systems should be covered automatically. In any case, if any real problems result from this, it still will be better to follow the Windows' behavior (which now is reflected by the specification too) in general and handle the cases when it doesn't work via quirks. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 97 ++++++++++++++++++++++++---------------- drivers/acpi/fan.c | 5 ++- drivers/acpi/power.c | 3 +- drivers/acpi/scan.c | 26 +++-------- drivers/pci/pci-acpi.c | 2 +- include/acpi/acpi_bus.h | 3 +- 6 files changed, 71 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 735db11a9b00..87c16a5af748 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -98,17 +98,16 @@ int acpi_device_get_power(struct acpi_device *device, int *state) /* * The power resources settings may indicate a power state - * shallower than the actual power state of the device. + * shallower than the actual power state of the device, because + * the same power resources may be referenced by other devices. * - * Moreover, on systems predating ACPI 4.0, if the device - * doesn't depend on any power resources and _PSC returns 3, - * that means "power off". We need to maintain compatibility - * with those systems. + * For systems predating ACPI 4.0 we assume that D3hot is the + * deepest state that can be supported. */ if (psc > result && psc < ACPI_STATE_D3_COLD) result = psc; else if (result == ACPI_STATE_UNKNOWN) - result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; + result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc; } /* @@ -153,8 +152,8 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state) */ int acpi_device_set_power(struct acpi_device *device, int state) { + int target_state = state; int result = 0; - bool cut_power = false; if (!device || !device->flags.power_manageable || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) @@ -169,11 +168,21 @@ int acpi_device_set_power(struct acpi_device *device, int state) return 0; } - if (!device->power.states[state].flags.valid) { + if (state == ACPI_STATE_D3_COLD) { + /* + * For transitions to D3cold we need to execute _PS3 and then + * possibly drop references to the power resources in use. + */ + state = ACPI_STATE_D3_HOT; + /* If _PR3 is not available, use D3hot as the target state. */ + if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid) + target_state = state; + } else if (!device->power.states[state].flags.valid) { dev_warn(&device->dev, "Power state %s not supported\n", acpi_power_state_string(state)); return -ENODEV; } + if (!device->power.flags.ignore_parent && device->parent && (state < device->parent->power.state)) { dev_warn(&device->dev, @@ -183,39 +192,38 @@ int acpi_device_set_power(struct acpi_device *device, int state) return -ENODEV; } - /* For D3cold we should first transition into D3hot. */ - if (state == ACPI_STATE_D3_COLD - && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { - state = ACPI_STATE_D3_HOT; - cut_power = true; - } - - if (state < device->power.state && state != ACPI_STATE_D0 - && device->power.state >= ACPI_STATE_D3_HOT) { - dev_warn(&device->dev, - "Cannot transition to non-D0 state from D3\n"); - return -ENODEV; - } - /* * Transition Power * ---------------- - * In accordance with the ACPI specification first apply power (via - * power resources) and then evaluate _PSx. + * In accordance with ACPI 6, _PSx is executed before manipulating power + * resources, unless the target state is D0, in which case _PS0 is + * supposed to be executed after turning the power resources on. */ - if (device->power.flags.power_resources) { - result = acpi_power_transition(device, state); + if (state > ACPI_STATE_D0) { + /* + * According to ACPI 6, devices cannot go from lower-power + * (deeper) states to higher-power (shallower) states. + */ + if (state < device->power.state) { + dev_warn(&device->dev, "Cannot transition from %s to %s\n", + acpi_power_state_string(device->power.state), + acpi_power_state_string(state)); + return -ENODEV; + } + + result = acpi_dev_pm_explicit_set(device, state); if (result) goto end; - } - result = acpi_dev_pm_explicit_set(device, state); - if (result) - goto end; - if (cut_power) { - device->power.state = state; - state = ACPI_STATE_D3_COLD; - result = acpi_power_transition(device, state); + if (device->power.flags.power_resources) + result = acpi_power_transition(device, target_state); + } else { + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, ACPI_STATE_D0); + if (result) + goto end; + } + result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); } end: @@ -264,13 +272,24 @@ int acpi_bus_init_power(struct acpi_device *device) return result; if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { + /* Reference count the power resources. */ result = acpi_power_on_resources(device, state); if (result) return result; - result = acpi_dev_pm_explicit_set(device, state); - if (result) - return result; + if (state == ACPI_STATE_D0) { + /* + * If _PSC is not present and the state inferred from + * power resources appears to be D0, it still may be + * necessary to execute _PS0 at this point, because + * another device using the same power resources may + * have been put into D0 previously and that's why we + * see D0 here. + */ + result = acpi_dev_pm_explicit_set(device, state); + if (result) + return result; + } } else if (state == ACPI_STATE_UNKNOWN) { /* * No power resources and missing _PSC? Cross fingers and make @@ -603,12 +622,12 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) return -EINVAL; - if (d_max_in > ACPI_STATE_D3_HOT) { + if (d_max_in > ACPI_STATE_D2) { enum pm_qos_flags_status stat; stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); if (stat == PM_QOS_FLAGS_ALL) - d_max_in = ACPI_STATE_D3_HOT; + d_max_in = ACPI_STATE_D2; } adev = ACPI_COMPANION(dev); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 7a36f02598a6..bea0bbaafa97 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -158,8 +158,9 @@ static int fan_get_state(struct acpi_device *device, unsigned long *state) if (result) return result; - *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 : - (acpi_state == ACPI_STATE_D0 ? 1 : -1)); + *state = acpi_state == ACPI_STATE_D3_COLD + || acpi_state == ACPI_STATE_D3_HOT ? + 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1); return 0; } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 59a6bf707f91..1f8138f24d72 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -684,7 +684,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) } } - *state = ACPI_STATE_D3_COLD; + *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ? + ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT; return 0; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 03141aa4ea95..ccf15d754448 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1766,15 +1766,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) if (acpi_has_method(device->handle, pathname)) ps->flags.explicit_set = 1; - /* - * State is valid if there are means to put the device into it. - * D3hot is only valid if _PR3 present. - */ - if (!list_empty(&ps->resources) - || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { + /* State is valid if there are means to put the device into it. */ + if (!list_empty(&ps->resources) || ps->flags.explicit_set) ps->flags.valid = 1; - ps->flags.os_accessible = 1; - } ps->power = -1; /* Unknown - driver assigned */ ps->latency = -1; /* Unknown - driver assigned */ @@ -1810,21 +1804,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) acpi_bus_init_power_state(device, i); INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); + if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources)) + device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; - /* Set defaults for D0 and D3 states (always valid) */ + /* Set defaults for D0 and D3hot states (always valid) */ device->power.states[ACPI_STATE_D0].flags.valid = 1; device->power.states[ACPI_STATE_D0].power = 100; - device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; - device->power.states[ACPI_STATE_D3_COLD].power = 0; - - /* Set D3cold's explicit_set flag if _PS3 exists. */ - if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) - device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; - - /* Presence of _PS3 or _PRx means we can put the device into D3 cold */ - if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set || - device->power.flags.power_resources) - device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; + device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1; if (acpi_bus_init_power(device)) device->flags.power_manageable = 0; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6f6f175f51f7..314a625b78d6 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -420,7 +420,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D0] = ACPI_STATE_D0, [PCI_D1] = ACPI_STATE_D1, [PCI_D2] = ACPI_STATE_D2, - [PCI_D3hot] = ACPI_STATE_D3_COLD, + [PCI_D3hot] = ACPI_STATE_D3_HOT, [PCI_D3cold] = ACPI_STATE_D3_COLD, }; int error = -EINVAL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8de4fa90e8c4..1ba841f8fd3c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -271,7 +271,6 @@ struct acpi_device_power_flags { struct acpi_device_power_state { struct { u8 valid:1; - u8 os_accessible:1; u8 explicit_set:1; /* _PSx present? */ u8 reserved:6; } flags; @@ -601,7 +600,7 @@ static inline bool acpi_device_can_wakeup(struct acpi_device *adev) static inline bool acpi_device_can_poweroff(struct acpi_device *adev) { - return adev->power.states[ACPI_STATE_D3_COLD].flags.os_accessible; + return adev->power.states[ACPI_STATE_D3_COLD].flags.valid; } #else /* CONFIG_ACPI */ From cae756fbaa02a24f868330e68631b3f5ea345cb8 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Thu, 21 May 2015 23:29:26 +0800 Subject: [PATCH 09/11] ACPI / PCI: remove stale list_head in struct acpi_prt_entry list_head "list" in struct acpi_prt_entry was used to connect _PRT entries for PCI irq, but after commit 181380b702ee ("PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers"), the list head for _PRT entries was removed, but left "list" in struct acpi_prt_entry which is useless and stale, remove it now. Signed-off-by: Hanjun Guo Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_irq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index b1def411c0b8..03e4b6c49c6d 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -44,7 +44,6 @@ ACPI_MODULE_NAME("pci_irq"); struct acpi_prt_entry { - struct list_head list; struct acpi_pci_id id; u8 pin; acpi_handle link; From d5eefa8280a8bb1e8aef059154bc1d63e1ac3336 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 May 2015 04:19:49 +0200 Subject: [PATCH 10/11] ACPI / PM: Turn power resources on and off in the right order during resume According to Section 7.2 of ACPI 6.0, power resources should always be enabled and disabled in order given by the "resourceorder" field of the corresponding Power Resource objects: "Power Resource levels are enabled from low values to high values and are disabled from high values to low values." However, this is not what happens during system resume, because in that case the enabling/disabling is carried out in the power resource registration order which may not reflect the ordering required by the platform. For this reason, make the ordering of the global list of all power resources in the system (used by the system resume code) reflect the one given by the "resourceorder" attributes of the Power Resource objects in the ACPI namespace and modify acpi_resume_power_resources() to walk the list in the reverse order when turning off the power resources that had been off before the system was suspended. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 1f8138f24d72..93eac53b5110 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -760,6 +760,25 @@ static void acpi_power_sysfs_remove(struct acpi_device *device) device_remove_file(&device->dev, &dev_attr_resource_in_use); } +static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource) +{ + mutex_lock(&power_resource_list_lock); + + if (!list_empty(&acpi_power_resource_list)) { + struct acpi_power_resource *r; + + list_for_each_entry(r, &acpi_power_resource_list, list_node) + if (r->order > resource->order) { + list_add_tail(&resource->list_node, &r->list_node); + goto out; + } + } + list_add_tail(&resource->list_node, &acpi_power_resource_list); + + out: + mutex_unlock(&power_resource_list_lock); +} + int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; @@ -810,9 +829,7 @@ int acpi_add_power_resource(acpi_handle handle) if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) device->remove = acpi_power_sysfs_remove; - mutex_lock(&power_resource_list_lock); - list_add(&resource->list_node, &acpi_power_resource_list); - mutex_unlock(&power_resource_list_lock); + acpi_power_add_resource_to_list(resource); acpi_device_add_finalize(device); return 0; @@ -843,7 +860,22 @@ void acpi_resume_power_resources(void) && resource->ref_count) { dev_info(&resource->device.dev, "Turning ON\n"); __acpi_power_on(resource); - } else if (state == ACPI_POWER_RESOURCE_STATE_ON + } + + mutex_unlock(&resource->resource_lock); + } + list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { + int result, state; + + mutex_lock(&resource->resource_lock); + + result = acpi_power_get_state(resource->device.handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + continue; + } + + if (state == ACPI_POWER_RESOURCE_STATE_ON && !resource->ref_count) { dev_info(&resource->device.dev, "Turning OFF\n"); __acpi_power_off(resource); From 3d56402d3fa8d10749eeb36293dd1992bd5ad0c3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Jun 2015 01:32:38 +0200 Subject: [PATCH 11/11] ACPI / PM: Add missing pm_generic_complete() invocation Add missing invocation of pm_generic_complete() to acpi_subsys_complete() to allow ->complete callbacks provided by the drivers of devices using the ACPI PM domain to be executed during system resume. Fixes: f25c0ae2b4c4 (ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend) Cc: 3.16+ # 3.16+ Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 87c16a5af748..717afcdb5f4a 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -972,6 +972,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare); */ void acpi_subsys_complete(struct device *dev) { + pm_generic_complete(dev); /* * If the device had been runtime-suspended before the system went into * the sleep state it is going out of and it has never been resumed till