Commit 40dc166cb5dddbd36aa4ad11c03915ea538f5a61

Authored by Rafael J. Wysocki
1 parent f9b9e806ae

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>

Showing 6 changed files with 164 additions and 1 deletions Side-by-side Diff

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
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 +}
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
kernel/power/hibernate.c
... ... @@ -23,6 +23,7 @@
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  
... ... @@ -272,6 +273,8 @@
272 273 local_irq_disable();
273 274  
274 275 error = sysdev_suspend(PMSG_FREEZE);
  276 + if (!error)
  277 + error = syscore_suspend();
275 278 if (error) {
276 279 printk(KERN_ERR "PM: Some system devices failed to power down, "
277 280 "aborting hibernation\n");
... ... @@ -295,6 +298,7 @@
295 298 }
296 299  
297 300 Power_up:
  301 + syscore_resume();
298 302 sysdev_resume();
299 303 /* NOTE: dpm_resume_noirq() is just a resume() for devices
300 304 * that suspended with irqs off ... no overall powerup.
... ... @@ -403,6 +407,8 @@
403 407 local_irq_disable();
404 408  
405 409 error = sysdev_suspend(PMSG_QUIESCE);
  410 + if (!error)
  411 + error = syscore_suspend();
406 412 if (error)
407 413 goto Enable_irqs;
408 414  
... ... @@ -429,6 +435,7 @@
429 435 restore_processor_state();
430 436 touch_softlockup_watchdog();
431 437  
  438 + syscore_resume();
432 439 sysdev_resume();
433 440  
434 441 Enable_irqs:
... ... @@ -516,6 +523,7 @@
516 523  
517 524 local_irq_disable();
518 525 sysdev_suspend(PMSG_HIBERNATE);
  526 + syscore_suspend();
519 527 if (pm_wakeup_pending()) {
520 528 error = -EAGAIN;
521 529 goto Power_up;
... ... @@ -526,6 +534,7 @@
526 534 while (1);
527 535  
528 536 Power_up:
  537 + syscore_resume();
529 538 sysdev_resume();
530 539 local_irq_enable();
531 540 enable_nonboot_cpus();
kernel/power/suspend.c
... ... @@ -22,6 +22,7 @@
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"
28 29  
... ... @@ -163,11 +164,14 @@
163 164 BUG_ON(!irqs_disabled());
164 165  
165 166 error = sysdev_suspend(PMSG_SUSPEND);
  167 + if (!error)
  168 + error = syscore_suspend();
166 169 if (!error) {
167 170 if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
168 171 error = suspend_ops->enter(state);
169 172 events_check_enabled = false;
170 173 }
  174 + syscore_resume();
171 175 sysdev_resume();
172 176 }
173 177  
... ... @@ -37,6 +37,7 @@
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>
... ... @@ -298,6 +299,7 @@
298 299 system_state = SYSTEM_RESTART;
299 300 device_shutdown();
300 301 sysdev_shutdown();
  302 + syscore_shutdown();
301 303 }
302 304  
303 305 /**
... ... @@ -336,6 +338,7 @@
336 338 {
337 339 kernel_shutdown_prepare(SYSTEM_HALT);
338 340 sysdev_shutdown();
  341 + syscore_shutdown();
339 342 printk(KERN_EMERG "System halted.\n");
340 343 kmsg_dump(KMSG_DUMP_HALT);
341 344 machine_halt();
... ... @@ -355,6 +358,7 @@
355 358 pm_power_off_prepare();
356 359 disable_nonboot_cpus();
357 360 sysdev_shutdown();
  361 + syscore_shutdown();
358 362 printk(KERN_EMERG "Power down.\n");
359 363 kmsg_dump(KMSG_DUMP_POWEROFF);
360 364 machine_power_off();