LCOV - code coverage report
Current view: top level - kernel/power - suspend.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3 118 2.5 %
Date: 2014-02-18 Functions: 1 14 7.1 %
Branches: 0 98 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * kernel/power/suspend.c - Suspend to RAM and standby functionality.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2003 Patrick Mochel
       5                 :            :  * Copyright (c) 2003 Open Source Development Lab
       6                 :            :  * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
       7                 :            :  *
       8                 :            :  * This file is released under the GPLv2.
       9                 :            :  */
      10                 :            : 
      11                 :            : #include <linux/string.h>
      12                 :            : #include <linux/delay.h>
      13                 :            : #include <linux/errno.h>
      14                 :            : #include <linux/init.h>
      15                 :            : #include <linux/console.h>
      16                 :            : #include <linux/cpu.h>
      17                 :            : #include <linux/syscalls.h>
      18                 :            : #include <linux/gfp.h>
      19                 :            : #include <linux/io.h>
      20                 :            : #include <linux/kernel.h>
      21                 :            : #include <linux/list.h>
      22                 :            : #include <linux/mm.h>
      23                 :            : #include <linux/slab.h>
      24                 :            : #include <linux/export.h>
      25                 :            : #include <linux/suspend.h>
      26                 :            : #include <linux/syscore_ops.h>
      27                 :            : #include <linux/ftrace.h>
      28                 :            : #include <linux/rtc.h>
      29                 :            : #include <trace/events/power.h>
      30                 :            : 
      31                 :            : #include "power.h"
      32                 :            : 
      33                 :            : const char *const pm_states[PM_SUSPEND_MAX] = {
      34                 :            :         [PM_SUSPEND_FREEZE]     = "freeze",
      35                 :            :         [PM_SUSPEND_STANDBY]    = "standby",
      36                 :            :         [PM_SUSPEND_MEM]        = "mem",
      37                 :            : };
      38                 :            : 
      39                 :            : static const struct platform_suspend_ops *suspend_ops;
      40                 :            : 
      41                 :            : static bool need_suspend_ops(suspend_state_t state)
      42                 :            : {
      43                 :            :         return !!(state > PM_SUSPEND_FREEZE);
      44                 :            : }
      45                 :            : 
      46                 :            : static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
      47                 :            : static bool suspend_freeze_wake;
      48                 :            : 
      49                 :            : static void freeze_begin(void)
      50                 :            : {
      51                 :          0 :         suspend_freeze_wake = false;
      52                 :            : }
      53                 :            : 
      54                 :          0 : static void freeze_enter(void)
      55                 :            : {
      56 [ #  # ][ #  # ]:          0 :         wait_event(suspend_freeze_wait_head, suspend_freeze_wake);
      57                 :          0 : }
      58                 :            : 
      59                 :          0 : void freeze_wake(void)
      60                 :            : {
      61                 :      87231 :         suspend_freeze_wake = true;
      62                 :      87231 :         wake_up(&suspend_freeze_wait_head);
      63                 :      87231 : }
      64                 :            : EXPORT_SYMBOL_GPL(freeze_wake);
      65                 :            : 
      66                 :            : /**
      67                 :            :  * suspend_set_ops - Set the global suspend method table.
      68                 :            :  * @ops: Suspend operations to use.
      69                 :            :  */
      70                 :          0 : void suspend_set_ops(const struct platform_suspend_ops *ops)
      71                 :            : {
      72                 :            :         lock_system_sleep();
      73                 :          0 :         suspend_ops = ops;
      74                 :            :         unlock_system_sleep();
      75                 :          0 : }
      76                 :            : EXPORT_SYMBOL_GPL(suspend_set_ops);
      77                 :            : 
      78                 :          0 : bool valid_state(suspend_state_t state)
      79                 :            : {
      80         [ #  # ]:          0 :         if (state == PM_SUSPEND_FREEZE) {
      81                 :            : #ifdef CONFIG_PM_DEBUG
      82                 :            :                 if (pm_test_level != TEST_NONE &&
      83                 :            :                     pm_test_level != TEST_FREEZER &&
      84                 :            :                     pm_test_level != TEST_DEVICES &&
      85                 :            :                     pm_test_level != TEST_PLATFORM) {
      86                 :            :                         printk(KERN_WARNING "Unsupported pm_test mode for "
      87                 :            :                                         "freeze state, please choose "
      88                 :            :                                         "none/freezer/devices/platform.\n");
      89                 :            :                         return false;
      90                 :            :                 }
      91                 :            : #endif
      92                 :            :                         return true;
      93                 :            :         }
      94                 :            :         /*
      95                 :            :          * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
      96                 :            :          * support and need to be valid to the lowlevel
      97                 :            :          * implementation, no valid callback implies that none are valid.
      98                 :            :          */
      99 [ #  # ][ #  # ]:          0 :         return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
                 [ #  # ]
     100                 :            : }
     101                 :            : 
     102                 :            : /**
     103                 :            :  * suspend_valid_only_mem - Generic memory-only valid callback.
     104                 :            :  *
     105                 :            :  * Platform drivers that implement mem suspend only and only need to check for
     106                 :            :  * that in their .valid() callback can use this instead of rolling their own
     107                 :            :  * .valid() callback.
     108                 :            :  */
     109                 :          0 : int suspend_valid_only_mem(suspend_state_t state)
     110                 :            : {
     111                 :          0 :         return state == PM_SUSPEND_MEM;
     112                 :            : }
     113                 :            : EXPORT_SYMBOL_GPL(suspend_valid_only_mem);
     114                 :            : 
     115                 :            : static int suspend_test(int level)
     116                 :            : {
     117                 :            : #ifdef CONFIG_PM_DEBUG
     118                 :            :         if (pm_test_level == level) {
     119                 :            :                 printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
     120                 :            :                 mdelay(5000);
     121                 :            :                 return 1;
     122                 :            :         }
     123                 :            : #endif /* !CONFIG_PM_DEBUG */
     124                 :            :         return 0;
     125                 :            : }
     126                 :            : 
     127                 :            : /**
     128                 :            :  * suspend_prepare - Prepare for entering system sleep state.
     129                 :            :  *
     130                 :            :  * Common code run for every system sleep state that can be entered (except for
     131                 :            :  * hibernation).  Run suspend notifiers, allocate the "suspend" console and
     132                 :            :  * freeze processes.
     133                 :            :  */
     134                 :          0 : static int suspend_prepare(suspend_state_t state)
     135                 :            : {
     136                 :            :         int error;
     137                 :            : 
     138 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
                 [ #  # ]
     139                 :            :                 return -EPERM;
     140                 :            : 
     141                 :          0 :         pm_prepare_console();
     142                 :            : 
     143                 :          0 :         error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
     144         [ #  # ]:          0 :         if (error)
     145                 :            :                 goto Finish;
     146                 :            : 
     147                 :            :         error = suspend_freeze_processes();
     148         [ #  # ]:          0 :         if (!error)
     149                 :            :                 return 0;
     150                 :            : 
     151                 :          0 :         suspend_stats.failed_freeze++;
     152                 :            :         dpm_save_failed_step(SUSPEND_FREEZE);
     153                 :            :  Finish:
     154                 :          0 :         pm_notifier_call_chain(PM_POST_SUSPEND);
     155                 :          0 :         pm_restore_console();
     156                 :          0 :         return error;
     157                 :            : }
     158                 :            : 
     159                 :            : /* default implementation */
     160                 :          0 : void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
     161                 :            : {
     162                 :            :         local_irq_disable();
     163                 :          0 : }
     164                 :            : 
     165                 :            : /* default implementation */
     166                 :          0 : void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
     167                 :            : {
     168                 :            :         local_irq_enable();
     169                 :          0 : }
     170                 :            : 
     171                 :            : /**
     172                 :            :  * suspend_enter - Make the system enter the given sleep state.
     173                 :            :  * @state: System sleep state to enter.
     174                 :            :  * @wakeup: Returns information that the sleep state should not be re-entered.
     175                 :            :  *
     176                 :            :  * This function should be called after devices have been suspended.
     177                 :            :  */
     178                 :          0 : static int suspend_enter(suspend_state_t state, bool *wakeup)
     179                 :            : {
     180                 :            :         int error;
     181                 :            : 
     182 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->prepare) {
     183                 :          0 :                 error = suspend_ops->prepare();
     184         [ #  # ]:          0 :                 if (error)
     185                 :            :                         goto Platform_finish;
     186                 :            :         }
     187                 :            : 
     188                 :          0 :         error = dpm_suspend_end(PMSG_SUSPEND);
     189         [ #  # ]:          0 :         if (error) {
     190                 :          0 :                 printk(KERN_ERR "PM: Some devices failed to power down\n");
     191                 :          0 :                 goto Platform_finish;
     192                 :            :         }
     193                 :            : 
     194 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->prepare_late) {
     195                 :          0 :                 error = suspend_ops->prepare_late();
     196         [ #  # ]:          0 :                 if (error)
     197                 :            :                         goto Platform_wake;
     198                 :            :         }
     199                 :            : 
     200                 :            :         if (suspend_test(TEST_PLATFORM))
     201                 :            :                 goto Platform_wake;
     202                 :            : 
     203                 :            :         /*
     204                 :            :          * PM_SUSPEND_FREEZE equals
     205                 :            :          * frozen processes + suspended devices + idle processors.
     206                 :            :          * Thus we should invoke freeze_enter() soon after
     207                 :            :          * all the devices are suspended.
     208                 :            :          */
     209         [ #  # ]:          0 :         if (state == PM_SUSPEND_FREEZE) {
     210                 :          0 :                 freeze_enter();
     211                 :          0 :                 goto Platform_wake;
     212                 :            :         }
     213                 :            : 
     214                 :            :         ftrace_stop();
     215                 :          0 :         error = disable_nonboot_cpus();
     216         [ #  # ]:          0 :         if (error || suspend_test(TEST_CPUS))
     217                 :            :                 goto Enable_cpus;
     218                 :            : 
     219                 :          0 :         arch_suspend_disable_irqs();
     220         [ #  # ]:          0 :         BUG_ON(!irqs_disabled());
     221                 :            : 
     222                 :          0 :         error = syscore_suspend();
     223         [ #  # ]:          0 :         if (!error) {
     224                 :          0 :                 *wakeup = pm_wakeup_pending();
     225         [ #  # ]:          0 :                 if (!(suspend_test(TEST_CORE) || *wakeup)) {
     226                 :          0 :                         error = suspend_ops->enter(state);
     227                 :          0 :                         events_check_enabled = false;
     228                 :            :                 }
     229                 :          0 :                 syscore_resume();
     230                 :            :         }
     231                 :            : 
     232                 :          0 :         arch_suspend_enable_irqs();
     233         [ #  # ]:          0 :         BUG_ON(irqs_disabled());
     234                 :            : 
     235                 :            :  Enable_cpus:
     236                 :          0 :         enable_nonboot_cpus();
     237                 :            :         ftrace_start();
     238                 :            : 
     239                 :            :  Platform_wake:
     240 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->wake)
     241                 :          0 :                 suspend_ops->wake();
     242                 :            : 
     243                 :          0 :         dpm_resume_start(PMSG_RESUME);
     244                 :            : 
     245                 :            :  Platform_finish:
     246 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->finish)
     247                 :          0 :                 suspend_ops->finish();
     248                 :            : 
     249                 :          0 :         return error;
     250                 :            : }
     251                 :            : 
     252                 :            : /**
     253                 :            :  * suspend_devices_and_enter - Suspend devices and enter system sleep state.
     254                 :            :  * @state: System sleep state to enter.
     255                 :            :  */
     256                 :          0 : int suspend_devices_and_enter(suspend_state_t state)
     257                 :            : {
     258                 :            :         int error;
     259                 :          0 :         bool wakeup = false;
     260                 :            : 
     261 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && !suspend_ops)
     262                 :            :                 return -ENOSYS;
     263                 :            : 
     264                 :          0 :         trace_machine_suspend(state);
     265 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->begin) {
     266                 :          0 :                 error = suspend_ops->begin(state);
     267         [ #  # ]:          0 :                 if (error)
     268                 :            :                         goto Close;
     269                 :            :         }
     270                 :          0 :         suspend_console();
     271                 :            :         suspend_test_start();
     272                 :          0 :         error = dpm_suspend_start(PMSG_SUSPEND);
     273         [ #  # ]:          0 :         if (error) {
     274                 :          0 :                 pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
     275                 :            :                 goto Recover_platform;
     276                 :            :         }
     277                 :            :         suspend_test_finish("suspend devices");
     278                 :            :         if (suspend_test(TEST_DEVICES))
     279                 :            :                 goto Recover_platform;
     280                 :            : 
     281                 :            :         do {
     282                 :          0 :                 error = suspend_enter(state, &wakeup);
     283 [ #  # ][ #  # ]:          0 :         } while (!error && !wakeup && need_suspend_ops(state)
     284 [ #  # ][ #  # ]:          0 :                 && suspend_ops->suspend_again && suspend_ops->suspend_again());
                 [ #  # ]
     285                 :            : 
     286                 :            :  Resume_devices:
     287                 :            :         suspend_test_start();
     288                 :          0 :         dpm_resume_end(PMSG_RESUME);
     289                 :            :         suspend_test_finish("resume devices");
     290                 :          0 :         resume_console();
     291                 :            :  Close:
     292 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->end)
     293                 :          0 :                 suspend_ops->end();
     294                 :            :         trace_machine_suspend(PWR_EVENT_EXIT);
     295                 :          0 :         return error;
     296                 :            : 
     297                 :            :  Recover_platform:
     298 [ #  # ][ #  # ]:          0 :         if (need_suspend_ops(state) && suspend_ops->recover)
     299                 :          0 :                 suspend_ops->recover();
     300                 :            :         goto Resume_devices;
     301                 :            : }
     302                 :            : 
     303                 :            : /**
     304                 :            :  * suspend_finish - Clean up before finishing the suspend sequence.
     305                 :            :  *
     306                 :            :  * Call platform code to clean up, restart processes, and free the console that
     307                 :            :  * we've allocated. This routine is not called for hibernation.
     308                 :            :  */
     309                 :          0 : static void suspend_finish(void)
     310                 :            : {
     311                 :            :         suspend_thaw_processes();
     312                 :          0 :         pm_notifier_call_chain(PM_POST_SUSPEND);
     313                 :          0 :         pm_restore_console();
     314                 :          0 : }
     315                 :            : 
     316                 :            : /**
     317                 :            :  * enter_state - Do common work needed to enter system sleep state.
     318                 :            :  * @state: System sleep state to enter.
     319                 :            :  *
     320                 :            :  * Make sure that no one else is trying to put the system into a sleep state.
     321                 :            :  * Fail if that's not the case.  Otherwise, prepare for system suspend, make the
     322                 :            :  * system enter the given sleep state and clean up after wakeup.
     323                 :            :  */
     324                 :          0 : static int enter_state(suspend_state_t state)
     325                 :            : {
     326                 :            :         int error;
     327                 :            : 
     328         [ #  # ]:          0 :         if (!valid_state(state))
     329                 :            :                 return -ENODEV;
     330                 :            : 
     331         [ #  # ]:          0 :         if (!mutex_trylock(&pm_mutex))
     332                 :            :                 return -EBUSY;
     333                 :            : 
     334         [ #  # ]:          0 :         if (state == PM_SUSPEND_FREEZE)
     335                 :            :                 freeze_begin();
     336                 :            : 
     337                 :          0 :         printk(KERN_INFO "PM: Syncing filesystems ... ");
     338                 :          0 :         sys_sync();
     339                 :          0 :         printk("done.\n");
     340                 :            : 
     341                 :            :         pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
     342                 :          0 :         error = suspend_prepare(state);
     343         [ #  # ]:          0 :         if (error)
     344                 :            :                 goto Unlock;
     345                 :            : 
     346                 :            :         if (suspend_test(TEST_FREEZER))
     347                 :            :                 goto Finish;
     348                 :            : 
     349                 :            :         pr_debug("PM: Entering %s sleep\n", pm_states[state]);
     350                 :          0 :         pm_restrict_gfp_mask();
     351                 :          0 :         error = suspend_devices_and_enter(state);
     352                 :          0 :         pm_restore_gfp_mask();
     353                 :            : 
     354                 :            :  Finish:
     355                 :            :         pr_debug("PM: Finishing wakeup.\n");
     356                 :          0 :         suspend_finish();
     357                 :            :  Unlock:
     358                 :          0 :         mutex_unlock(&pm_mutex);
     359                 :          0 :         return error;
     360                 :            : }
     361                 :            : 
     362                 :          0 : static void pm_suspend_marker(char *annotation)
     363                 :            : {
     364                 :            :         struct timespec ts;
     365                 :            :         struct rtc_time tm;
     366                 :            : 
     367                 :          0 :         getnstimeofday(&ts);
     368                 :          0 :         rtc_time_to_tm(ts.tv_sec, &tm);
     369                 :          0 :         pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
     370                 :            :                 annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
     371                 :            :                 tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
     372                 :          0 : }
     373                 :            : 
     374                 :            : /**
     375                 :            :  * pm_suspend - Externally visible function for suspending the system.
     376                 :            :  * @state: System sleep state to enter.
     377                 :            :  *
     378                 :            :  * Check if the value of @state represents one of the supported states,
     379                 :            :  * execute enter_state() and update system suspend statistics.
     380                 :            :  */
     381                 :          0 : int pm_suspend(suspend_state_t state)
     382                 :            : {
     383                 :            :         int error;
     384                 :            : 
     385         [ #  # ]:          0 :         if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
     386                 :            :                 return -EINVAL;
     387                 :            : 
     388                 :          0 :         pm_suspend_marker("entry");
     389                 :          0 :         error = enter_state(state);
     390         [ #  # ]:          0 :         if (error) {
     391                 :          0 :                 suspend_stats.fail++;
     392                 :            :                 dpm_save_failed_errno(error);
     393                 :            :         } else {
     394                 :          0 :                 suspend_stats.success++;
     395                 :            :         }
     396                 :          0 :         pm_suspend_marker("exit");
     397                 :          0 :         return error;
     398                 :            : }
     399                 :            : EXPORT_SYMBOL(pm_suspend);

Generated by: LCOV version 1.9