Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / Core: Introduce struct syscore_ops for core subsystems PM

Some subsystems need to carry out suspend/resume and shutdown
operations with one CPU on-line and interrupts disabled. The only
way to register such operations is to define a sysdev class and
a sysdev specifically for this purpose which is cumbersome and
inefficient. Moreover, the arguments taken by sysdev suspend,
resume and shutdown callbacks are practically never necessary.

For this reason, introduce a simpler interface allowing subsystems
to register operations to be executed very late during system suspend
and shutdown and very early during resume in the form of
strcut syscore_ops objects.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>

+164 -1
+1 -1
drivers/base/Makefile
··· 1 1 # Makefile for the Linux device tree 2 2 3 - obj-y := core.o sys.o bus.o dd.o \ 3 + obj-y := core.o sys.o bus.o dd.o syscore.o \ 4 4 driver.o class.o platform.o \ 5 5 cpu.o firmware.o init.o map.o devres.o \ 6 6 attribute_container.o transport_class.o
+117
drivers/base/syscore.c
··· 1 + /* 2 + * syscore.c - Execution of system core operations. 3 + * 4 + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 5 + * 6 + * This file is released under the GPLv2. 7 + */ 8 + 9 + #include <linux/syscore_ops.h> 10 + #include <linux/mutex.h> 11 + #include <linux/module.h> 12 + 13 + static LIST_HEAD(syscore_ops_list); 14 + static DEFINE_MUTEX(syscore_ops_lock); 15 + 16 + /** 17 + * register_syscore_ops - Register a set of system core operations. 18 + * @ops: System core operations to register. 19 + */ 20 + void register_syscore_ops(struct syscore_ops *ops) 21 + { 22 + mutex_lock(&syscore_ops_lock); 23 + list_add_tail(&ops->node, &syscore_ops_list); 24 + mutex_unlock(&syscore_ops_lock); 25 + } 26 + EXPORT_SYMBOL_GPL(register_syscore_ops); 27 + 28 + /** 29 + * unregister_syscore_ops - Unregister a set of system core operations. 30 + * @ops: System core operations to unregister. 31 + */ 32 + void unregister_syscore_ops(struct syscore_ops *ops) 33 + { 34 + mutex_lock(&syscore_ops_lock); 35 + list_del(&ops->node); 36 + mutex_unlock(&syscore_ops_lock); 37 + } 38 + EXPORT_SYMBOL_GPL(unregister_syscore_ops); 39 + 40 + #ifdef CONFIG_PM_SLEEP 41 + /** 42 + * syscore_suspend - Execute all the registered system core suspend callbacks. 43 + * 44 + * This function is executed with one CPU on-line and disabled interrupts. 45 + */ 46 + int syscore_suspend(void) 47 + { 48 + struct syscore_ops *ops; 49 + int ret = 0; 50 + 51 + WARN_ONCE(!irqs_disabled(), 52 + "Interrupts enabled before system core suspend.\n"); 53 + 54 + list_for_each_entry_reverse(ops, &syscore_ops_list, node) 55 + if (ops->suspend) { 56 + if (initcall_debug) 57 + pr_info("PM: Calling %pF\n", ops->suspend); 58 + ret = ops->suspend(); 59 + if (ret) 60 + goto err_out; 61 + WARN_ONCE(!irqs_disabled(), 62 + "Interrupts enabled after %pF\n", ops->suspend); 63 + } 64 + 65 + return 0; 66 + 67 + err_out: 68 + pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); 69 + 70 + list_for_each_entry_continue(ops, &syscore_ops_list, node) 71 + if (ops->resume) 72 + ops->resume(); 73 + 74 + return ret; 75 + } 76 + 77 + /** 78 + * syscore_resume - Execute all the registered system core resume callbacks. 79 + * 80 + * This function is executed with one CPU on-line and disabled interrupts. 81 + */ 82 + void syscore_resume(void) 83 + { 84 + struct syscore_ops *ops; 85 + 86 + WARN_ONCE(!irqs_disabled(), 87 + "Interrupts enabled before system core resume.\n"); 88 + 89 + list_for_each_entry(ops, &syscore_ops_list, node) 90 + if (ops->resume) { 91 + if (initcall_debug) 92 + pr_info("PM: Calling %pF\n", ops->resume); 93 + ops->resume(); 94 + WARN_ONCE(!irqs_disabled(), 95 + "Interrupts enabled after %pF\n", ops->resume); 96 + } 97 + } 98 + #endif /* CONFIG_PM_SLEEP */ 99 + 100 + /** 101 + * syscore_shutdown - Execute all the registered system core shutdown callbacks. 102 + */ 103 + void syscore_shutdown(void) 104 + { 105 + struct syscore_ops *ops; 106 + 107 + mutex_lock(&syscore_ops_lock); 108 + 109 + list_for_each_entry_reverse(ops, &syscore_ops_list, node) 110 + if (ops->shutdown) { 111 + if (initcall_debug) 112 + pr_info("PM: Calling %pF\n", ops->shutdown); 113 + ops->shutdown(); 114 + } 115 + 116 + mutex_unlock(&syscore_ops_lock); 117 + }
+29
include/linux/syscore_ops.h
··· 1 + /* 2 + * syscore_ops.h - System core operations. 3 + * 4 + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 5 + * 6 + * This file is released under the GPLv2. 7 + */ 8 + 9 + #ifndef _LINUX_SYSCORE_OPS_H 10 + #define _LINUX_SYSCORE_OPS_H 11 + 12 + #include <linux/list.h> 13 + 14 + struct syscore_ops { 15 + struct list_head node; 16 + int (*suspend)(void); 17 + void (*resume)(void); 18 + void (*shutdown)(void); 19 + }; 20 + 21 + extern void register_syscore_ops(struct syscore_ops *ops); 22 + extern void unregister_syscore_ops(struct syscore_ops *ops); 23 + #ifdef CONFIG_PM_SLEEP 24 + extern int syscore_suspend(void); 25 + extern void syscore_resume(void); 26 + #endif 27 + extern void syscore_shutdown(void); 28 + 29 + #endif
+9
kernel/power/hibernate.c
··· 23 23 #include <linux/cpu.h> 24 24 #include <linux/freezer.h> 25 25 #include <linux/gfp.h> 26 + #include <linux/syscore_ops.h> 26 27 #include <scsi/scsi_scan.h> 27 28 #include <asm/suspend.h> 28 29 ··· 273 272 local_irq_disable(); 274 273 275 274 error = sysdev_suspend(PMSG_FREEZE); 275 + if (!error) 276 + error = syscore_suspend(); 276 277 if (error) { 277 278 printk(KERN_ERR "PM: Some system devices failed to power down, " 278 279 "aborting hibernation\n"); ··· 298 295 } 299 296 300 297 Power_up: 298 + syscore_resume(); 301 299 sysdev_resume(); 302 300 /* NOTE: dpm_resume_noirq() is just a resume() for devices 303 301 * that suspended with irqs off ... no overall powerup. ··· 407 403 local_irq_disable(); 408 404 409 405 error = sysdev_suspend(PMSG_QUIESCE); 406 + if (!error) 407 + error = syscore_suspend(); 410 408 if (error) 411 409 goto Enable_irqs; 412 410 ··· 435 429 restore_processor_state(); 436 430 touch_softlockup_watchdog(); 437 431 432 + syscore_resume(); 438 433 sysdev_resume(); 439 434 440 435 Enable_irqs: ··· 523 516 524 517 local_irq_disable(); 525 518 sysdev_suspend(PMSG_HIBERNATE); 519 + syscore_suspend(); 526 520 if (pm_wakeup_pending()) { 527 521 error = -EAGAIN; 528 522 goto Power_up; ··· 534 526 while (1); 535 527 536 528 Power_up: 529 + syscore_resume(); 537 530 sysdev_resume(); 538 531 local_irq_enable(); 539 532 enable_nonboot_cpus();
+4
kernel/power/suspend.c
··· 22 22 #include <linux/mm.h> 23 23 #include <linux/slab.h> 24 24 #include <linux/suspend.h> 25 + #include <linux/syscore_ops.h> 25 26 #include <trace/events/power.h> 26 27 27 28 #include "power.h" ··· 164 163 BUG_ON(!irqs_disabled()); 165 164 166 165 error = sysdev_suspend(PMSG_SUSPEND); 166 + if (!error) 167 + error = syscore_suspend(); 167 168 if (!error) { 168 169 if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { 169 170 error = suspend_ops->enter(state); 170 171 events_check_enabled = false; 171 172 } 173 + syscore_resume(); 172 174 sysdev_resume(); 173 175 } 174 176
+4
kernel/sys.c
··· 37 37 #include <linux/ptrace.h> 38 38 #include <linux/fs_struct.h> 39 39 #include <linux/gfp.h> 40 + #include <linux/syscore_ops.h> 40 41 41 42 #include <linux/compat.h> 42 43 #include <linux/syscalls.h> ··· 299 298 system_state = SYSTEM_RESTART; 300 299 device_shutdown(); 301 300 sysdev_shutdown(); 301 + syscore_shutdown(); 302 302 } 303 303 304 304 /** ··· 338 336 { 339 337 kernel_shutdown_prepare(SYSTEM_HALT); 340 338 sysdev_shutdown(); 339 + syscore_shutdown(); 341 340 printk(KERN_EMERG "System halted.\n"); 342 341 kmsg_dump(KMSG_DUMP_HALT); 343 342 machine_halt(); ··· 358 355 pm_power_off_prepare(); 359 356 disable_nonboot_cpus(); 360 357 sysdev_shutdown(); 358 + syscore_shutdown(); 361 359 printk(KERN_EMERG "Power down.\n"); 362 360 kmsg_dump(KMSG_DUMP_POWEROFF); 363 361 machine_power_off();