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