Currently, if we test hibernation, it will fails at the ITS driver, because its can't resume and suspend successuly during the phase of hibernation.
Add its herbinate state to support its resume correctly.
Hongbo Yao (2): PM / hibernate: introduce system_in_hibernation irqchip/gic-v3-its: its support herbination
drivers/irqchip/irq-gic-v3-its.c | 19 +++++++++++++++++-- include/linux/suspend.h | 2 ++ kernel/power/hibernate.c | 11 +++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-)
hulk inclusion category: bugfix bugzilla: 26326 CVE: NA
------------------------------------------------- Introduce boolean function system_in_hibernation() returning 'true' when the system carrying out hibernation.
Some device drivers or syscore need such a function to check if it is in the phase of hibernation.
Signed-off-by: Hongbo Yao yaohongbo@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com --- include/linux/suspend.h | 2 ++ kernel/power/hibernate.c | 11 +++++++++++ 2 files changed, 13 insertions(+)
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 50e81bff37df..aff24892583d 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -414,6 +414,7 @@ extern asmlinkage int swsusp_arch_resume(void); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +extern bool system_in_hibernation(void); extern bool hibernation_available(void); asmlinkage int swsusp_save(void); extern struct pbe *restore_pblist; @@ -427,6 +428,7 @@ static inline void swsusp_unset_page_free(struct page *p) {} static inline void hibernation_set_ops(const struct platform_hibernation_ops *ops) {} static inline int hibernate(void) { return -ENOSYS; } static inline bool system_entering_hibernation(void) { return false; } +static inline bool system_in_hibernation(void) { return false; } static inline bool hibernation_available(void) { return false; } #endif /* CONFIG_HIBERNATION */
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index f5ce9f7ec132..72ef9df2a29c 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -104,6 +104,15 @@ bool system_entering_hibernation(void) } EXPORT_SYMBOL(system_entering_hibernation);
+/* To let some devices or syscore know if system carrying out hibernation*/ +static bool carry_out_hibernation; + +bool system_in_hibernation(void) +{ + return carry_out_hibernation; +} +EXPORT_SYMBOL(system_in_hibernation); + #ifdef CONFIG_PM_DEBUG static void hibernation_debug_sleep(void) { @@ -711,6 +720,7 @@ int hibernate(void) }
pr_info("hibernation entry\n"); + carry_out_hibernation = true; pm_prepare_console(); error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); if (error) { @@ -781,6 +791,7 @@ int hibernate(void) atomic_inc(&snapshot_device_available); Unlock: unlock_system_sleep(); + carry_out_hibernation = false; pr_info("hibernation exit\n");
return error;
hulk inclusion category: bugfix bugzilla: 26326 CVE: NA
-------------------------------------------------
Currently, when we test hibernation, it will fail at the ITS. [11384.327419] Call trace: [11384.329854] dump_backtrace+0x0/0x180 [11384.333503] show_stack+0x14/0x20 [11384.336805] dump_stack+0x8c/0xac [11384.340107] dequeue_task_idle+0x2c/0x68 [11384.344016] do_set_cpus_allowed+0x5c/0x148 [11384.348186] cpuset_cpus_allowed_fallback+0x1c/0x48 [11384.353050] select_fallback_rq+0x17c/0x220 [11384.355310] ITS queue not draining [11384.357219] sched_cpu_dying+0x328/0x368 [11384.364513] cpuhp_invoke_callback+0xc0/0x1f0 [11384.368856] _cpu_up+0x164/0x1e0 [11384.372070] enable_nonboot_cpus+0x7c/0xf8 [11384.376153] hibernation_snapshot+0x1c0/0x348 [11384.380496] hibernate+0xf4/0x290 [11384.383797] state_store+0x70/0xa0 [11384.387185] kobj_attr_store+0x14/0x28 [11384.390921] sysfs_kf_write+0x4c/0x60 [11384.394569] kernfs_fop_write+0x11c/0x1d0 [11384.398566] __vfs_write+0x1c/0x138 [11384.402041] vfs_write+0xb4/0x190 [11384.405342] ksys_write+0x50/0xa0 [11384.408643] __arm64_sys_write+0x14/0x20 [11384.412552] el0_svc_common+0x100/0x168 [11384.416374] el0_svc_handler+0x4c/0x68 [11384.420109] el0_svc+0x8/0xc [11384.422984] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
The its doesn't support hibernate, when hibernate() calls, the its doesn't suspend() and resume() successfully.
Add its herbinate state to support its suspend and resume correctly.
https://lkml.org/lkml/2019/12/2/270
Signed-off-by: Hongbo Yao yaohongbo@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com --- drivers/irqchip/irq-gic-v3-its.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 21dfe2266b31..f75f6d788567 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -38,6 +38,7 @@ #include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/suspend.h> #include <linux/syscore_ops.h>
#include <linux/irqchip.h> @@ -53,6 +54,7 @@ #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) #define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3) +#define ITS_FLAGS_SAVE_HIBERNATE_STATE (1ULL << 4)
#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) #define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) @@ -3365,8 +3367,16 @@ static int its_save_disable(void) raw_spin_lock(&its_lock); list_for_each_entry(its, &its_nodes, entry) { void __iomem *base; + u64 flags;
- if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE)) + if (system_in_hibernation()) + its->flags |= ITS_FLAGS_SAVE_HIBERNATE_STATE; + + flags = its->flags; + flags &= (ITS_FLAGS_SAVE_SUSPEND_STATE | + ITS_FLAGS_SAVE_HIBERNATE_STATE); + + if (!flags) continue;
base = its->base; @@ -3407,11 +3417,16 @@ static void its_restore_enable(void) raw_spin_lock(&its_lock); list_for_each_entry(its, &its_nodes, entry) { void __iomem *base; + u64 flags; int i;
- if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE)) + flags = its->flags; + flags &= (ITS_FLAGS_SAVE_SUSPEND_STATE | + ITS_FLAGS_SAVE_HIBERNATE_STATE); + if (!flags) continue;
+ its->flags &= ~ITS_FLAGS_SAVE_HIBERNATE_STATE; base = its->base;
/*