LCOV - code coverage report
Current view: top level - drivers/base/power - trace.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 58 115 50.4 %
Date: 2022-04-01 14:35:51 Functions: 5 10 50.0 %
Branches: 13 44 29.5 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * drivers/base/power/trace.c
       4                 :            :  *
       5                 :            :  * Copyright (C) 2006 Linus Torvalds
       6                 :            :  *
       7                 :            :  * Trace facility for suspend/resume problems, when none of the
       8                 :            :  * devices may be working.
       9                 :            :  */
      10                 :            : #define pr_fmt(fmt) "PM: " fmt
      11                 :            : 
      12                 :            : #include <linux/pm-trace.h>
      13                 :            : #include <linux/export.h>
      14                 :            : #include <linux/rtc.h>
      15                 :            : #include <linux/suspend.h>
      16                 :            : 
      17                 :            : #include <linux/mc146818rtc.h>
      18                 :            : 
      19                 :            : #include "power.h"
      20                 :            : 
      21                 :            : /*
      22                 :            :  * Horrid, horrid, horrid.
      23                 :            :  *
      24                 :            :  * It turns out that the _only_ piece of hardware that actually
      25                 :            :  * keeps its value across a hard boot (and, more importantly, the
      26                 :            :  * POST init sequence) is literally the realtime clock.
      27                 :            :  *
      28                 :            :  * Never mind that an RTC chip has 114 bytes (and often a whole
      29                 :            :  * other bank of an additional 128 bytes) of nice SRAM that is
      30                 :            :  * _designed_ to keep data - the POST will clear it. So we literally
      31                 :            :  * can just use the few bytes of actual time data, which means that
      32                 :            :  * we're really limited.
      33                 :            :  *
      34                 :            :  * It means, for example, that we can't use the seconds at all
      35                 :            :  * (since the time between the hang and the boot might be more
      36                 :            :  * than a minute), and we'd better not depend on the low bits of
      37                 :            :  * the minutes either.
      38                 :            :  *
      39                 :            :  * There are the wday fields etc, but I wouldn't guarantee those
      40                 :            :  * are dependable either. And if the date isn't valid, either the
      41                 :            :  * hw or POST will do strange things.
      42                 :            :  *
      43                 :            :  * So we're left with:
      44                 :            :  *  - year: 0-99
      45                 :            :  *  - month: 0-11
      46                 :            :  *  - day-of-month: 1-28
      47                 :            :  *  - hour: 0-23
      48                 :            :  *  - min: (0-30)*2
      49                 :            :  *
      50                 :            :  * Giving us a total range of 0-16128000 (0xf61800), ie less
      51                 :            :  * than 24 bits of actual data we can save across reboots.
      52                 :            :  *
      53                 :            :  * And if your box can't boot in less than three minutes,
      54                 :            :  * you're screwed.
      55                 :            :  *
      56                 :            :  * Now, almost 24 bits of data is pitifully small, so we need
      57                 :            :  * to be pretty dense if we want to use it for anything nice.
      58                 :            :  * What we do is that instead of saving off nice readable info,
      59                 :            :  * we save off _hashes_ of information that we can hopefully
      60                 :            :  * regenerate after the reboot.
      61                 :            :  *
      62                 :            :  * In particular, this means that we might be unlucky, and hit
      63                 :            :  * a case where we have a hash collision, and we end up not
      64                 :            :  * being able to tell for certain exactly which case happened.
      65                 :            :  * But that's hopefully unlikely.
      66                 :            :  *
      67                 :            :  * What we do is to take the bits we can fit, and split them
      68                 :            :  * into three parts (16*997*1009 = 16095568), and use the values
      69                 :            :  * for:
      70                 :            :  *  - 0-15: user-settable
      71                 :            :  *  - 0-996: file + line number
      72                 :            :  *  - 0-1008: device
      73                 :            :  */
      74                 :            : #define USERHASH (16)
      75                 :            : #define FILEHASH (997)
      76                 :            : #define DEVHASH (1009)
      77                 :            : 
      78                 :            : #define DEVSEED (7919)
      79                 :            : 
      80                 :            : bool pm_trace_rtc_abused __read_mostly;
      81                 :            : EXPORT_SYMBOL_GPL(pm_trace_rtc_abused);
      82                 :            : 
      83                 :            : static unsigned int dev_hash_value;
      84                 :            : 
      85                 :          0 : static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
      86                 :            : {
      87                 :          0 :         unsigned int n = user + USERHASH*(file + FILEHASH*device);
      88                 :            : 
      89                 :            :         // June 7th, 2006
      90                 :          0 :         static struct rtc_time time = {
      91                 :            :                 .tm_sec = 0,
      92                 :            :                 .tm_min = 0,
      93                 :            :                 .tm_hour = 0,
      94                 :            :                 .tm_mday = 7,
      95                 :            :                 .tm_mon = 5,    // June - counting from zero
      96                 :            :                 .tm_year = 106,
      97                 :            :                 .tm_wday = 3,
      98                 :            :                 .tm_yday = 160,
      99                 :            :                 .tm_isdst = 1
     100                 :            :         };
     101                 :            : 
     102                 :          0 :         time.tm_year = (n % 100);
     103                 :          0 :         n /= 100;
     104                 :          0 :         time.tm_mon = (n % 12);
     105                 :          0 :         n /= 12;
     106                 :          0 :         time.tm_mday = (n % 28) + 1;
     107                 :          0 :         n /= 28;
     108                 :          0 :         time.tm_hour = (n % 24);
     109                 :          0 :         n /= 24;
     110                 :          0 :         time.tm_min = (n % 20) * 3;
     111                 :          0 :         n /= 20;
     112                 :          0 :         mc146818_set_time(&time);
     113                 :          0 :         pm_trace_rtc_abused = true;
     114         [ #  # ]:          0 :         return n ? -1 : 0;
     115                 :            : }
     116                 :            : 
     117                 :         21 : static unsigned int read_magic_time(void)
     118                 :            : {
     119                 :         21 :         struct rtc_time time;
     120                 :         21 :         unsigned int val;
     121                 :            : 
     122                 :         21 :         mc146818_get_time(&time);
     123                 :         21 :         pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
     124                 :         21 :         val = time.tm_year;                             /* 100 years */
     125         [ +  - ]:         21 :         if (val > 100)
     126                 :         21 :                 val -= 100;
     127                 :         21 :         val += time.tm_mon * 100;                       /* 12 months */
     128                 :         21 :         val += (time.tm_mday-1) * 100 * 12;             /* 28 month-days */
     129                 :         21 :         val += time.tm_hour * 100 * 12 * 28;            /* 24 hours */
     130                 :         21 :         val += (time.tm_min / 3) * 100 * 12 * 28 * 24;  /* 20 3-minute intervals */
     131                 :         21 :         return val;
     132                 :            : }
     133                 :            : 
     134                 :            : /*
     135                 :            :  * This is just the sdbm hash function with a user-supplied
     136                 :            :  * seed and final size parameter.
     137                 :            :  */
     138                 :        252 : static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
     139                 :            : {
     140                 :        252 :         unsigned char c;
     141   [ -  -  +  +  :      50169 :         while ((c = *data++) != 0) {
          +  +  -  -  -  
                      - ]
     142                 :      44394 :                 seed = (seed << 16) + (seed << 6) - seed + c;
     143                 :            :         }
     144                 :       5775 :         return seed % mod;
     145                 :            : }
     146                 :            : 
     147                 :          0 : void set_trace_device(struct device *dev)
     148                 :            : {
     149         [ #  # ]:          0 :         dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
     150                 :          0 : }
     151                 :            : EXPORT_SYMBOL(set_trace_device);
     152                 :            : 
     153                 :            : /*
     154                 :            :  * We could just take the "tracedata" index into the .tracedata
     155                 :            :  * section instead. Generating a hash of the data gives us a
     156                 :            :  * chance to work across kernel versions, and perhaps more
     157                 :            :  * importantly it also gives us valid/invalid check (ie we will
     158                 :            :  * likely not give totally bogus reports - if the hash matches,
     159                 :            :  * it's not any guarantee, but it's a high _likelihood_ that
     160                 :            :  * the match is valid).
     161                 :            :  */
     162                 :          0 : void generate_pm_trace(const void *tracedata, unsigned int user)
     163                 :            : {
     164                 :          0 :         unsigned short lineno = *(unsigned short *)tracedata;
     165                 :          0 :         const char *file = *(const char **)(tracedata + 2);
     166                 :          0 :         unsigned int user_hash_value, file_hash_value;
     167                 :            : 
     168                 :          0 :         user_hash_value = user % USERHASH;
     169                 :          0 :         file_hash_value = hash_string(lineno, file, FILEHASH);
     170                 :          0 :         set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
     171                 :          0 : }
     172                 :            : EXPORT_SYMBOL(generate_pm_trace);
     173                 :            : 
     174                 :            : extern char __tracedata_start[], __tracedata_end[];
     175                 :         21 : static int show_file_hash(unsigned int value)
     176                 :            : {
     177                 :         21 :         int match;
     178                 :         21 :         char *tracedata;
     179                 :            : 
     180                 :         21 :         match = 0;
     181         [ +  + ]:        273 :         for (tracedata = __tracedata_start ; tracedata < __tracedata_end ;
     182                 :        252 :                         tracedata += 2 + sizeof(unsigned long)) {
     183                 :        252 :                 unsigned short lineno = *(unsigned short *)tracedata;
     184                 :        252 :                 const char *file = *(const char **)(tracedata + 2);
     185                 :        252 :                 unsigned int hash = hash_string(lineno, file, FILEHASH);
     186         [ +  - ]:        252 :                 if (hash != value)
     187                 :        252 :                         continue;
     188                 :          0 :                 pr_info("  hash matches %s:%u\n", file, lineno);
     189                 :          0 :                 match++;
     190                 :            :         }
     191                 :         21 :         return match;
     192                 :            : }
     193                 :            : 
     194                 :         21 : static int show_dev_hash(unsigned int value)
     195                 :            : {
     196                 :         21 :         int match = 0;
     197                 :         21 :         struct list_head *entry;
     198                 :            : 
     199                 :         21 :         device_pm_lock();
     200                 :         21 :         entry = dpm_list.prev;
     201         [ +  + ]:       5544 :         while (entry != &dpm_list) {
     202         [ +  - ]:       5523 :                 struct device * dev = to_device(entry);
     203         [ +  - ]:       5523 :                 unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
     204         [ -  + ]:       5523 :                 if (hash == value) {
     205                 :          0 :                         dev_info(dev, "hash matches\n");
     206                 :          0 :                         match++;
     207                 :            :                 }
     208                 :       5523 :                 entry = entry->prev;
     209                 :            :         }
     210                 :         21 :         device_pm_unlock();
     211                 :         21 :         return match;
     212                 :            : }
     213                 :            : 
     214                 :            : static unsigned int hash_value_early_read;
     215                 :            : 
     216                 :          0 : int show_trace_dev_match(char *buf, size_t size)
     217                 :            : {
     218                 :          0 :         unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
     219                 :          0 :         int ret = 0;
     220                 :          0 :         struct list_head *entry;
     221                 :            : 
     222                 :            :         /*
     223                 :            :          * It's possible that multiple devices will match the hash and we can't
     224                 :            :          * tell which is the culprit, so it's best to output them all.
     225                 :            :          */
     226                 :          0 :         device_pm_lock();
     227                 :          0 :         entry = dpm_list.prev;
     228   [ #  #  #  # ]:          0 :         while (size && entry != &dpm_list) {
     229         [ #  # ]:          0 :                 struct device *dev = to_device(entry);
     230         [ #  # ]:          0 :                 unsigned int hash = hash_string(DEVSEED, dev_name(dev),
     231                 :            :                                                 DEVHASH);
     232         [ #  # ]:          0 :                 if (hash == value) {
     233                 :          0 :                         int len = snprintf(buf, size, "%s\n",
     234                 :            :                                             dev_driver_string(dev));
     235         [ #  # ]:          0 :                         if (len > size)
     236                 :          0 :                                 len = size;
     237                 :          0 :                         buf += len;
     238                 :          0 :                         ret += len;
     239                 :          0 :                         size -= len;
     240                 :            :                 }
     241                 :          0 :                 entry = entry->prev;
     242                 :            :         }
     243                 :          0 :         device_pm_unlock();
     244                 :          0 :         return ret;
     245                 :            : }
     246                 :            : 
     247                 :            : static int
     248                 :          0 : pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
     249                 :            : {
     250         [ #  # ]:          0 :         switch (mode) {
     251                 :          0 :         case PM_POST_HIBERNATION:
     252                 :            :         case PM_POST_SUSPEND:
     253         [ #  # ]:          0 :                 if (pm_trace_rtc_abused) {
     254                 :          0 :                         pm_trace_rtc_abused = false;
     255                 :          0 :                         pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
     256                 :            :                 }
     257                 :            :                 break;
     258                 :            :         default:
     259                 :            :                 break;
     260                 :            :         }
     261                 :          0 :         return 0;
     262                 :            : }
     263                 :            : 
     264                 :            : static struct notifier_block pm_trace_nb = {
     265                 :            :         .notifier_call = pm_trace_notify,
     266                 :            : };
     267                 :            : 
     268                 :         21 : static int early_resume_init(void)
     269                 :            : {
     270                 :         21 :         hash_value_early_read = read_magic_time();
     271                 :         21 :         register_pm_notifier(&pm_trace_nb);
     272                 :         21 :         return 0;
     273                 :            : }
     274                 :            : 
     275                 :         21 : static int late_resume_init(void)
     276                 :            : {
     277                 :         21 :         unsigned int val = hash_value_early_read;
     278                 :         21 :         unsigned int user, file, dev;
     279                 :            : 
     280                 :         21 :         user = val % USERHASH;
     281                 :         21 :         val = val / USERHASH;
     282                 :         21 :         file = val % FILEHASH;
     283                 :         21 :         val = val / FILEHASH;
     284                 :         21 :         dev = val /* % DEVHASH */;
     285                 :            : 
     286                 :         21 :         pr_info("  Magic number: %d:%d:%d\n", user, file, dev);
     287                 :         21 :         show_file_hash(file);
     288                 :         21 :         show_dev_hash(dev);
     289                 :         21 :         return 0;
     290                 :            : }
     291                 :            : 
     292                 :            : core_initcall(early_resume_init);
     293                 :            : late_initcall(late_resume_init);

Generated by: LCOV version 1.14