1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <linux/kernel.h>
26#include <linux/delay.h>
27#include <linux/nmi.h>
28#include <linux/timer.h>
29#include <linux/interrupt.h>
30#include <linux/workqueue.h>
31#include <linux/genhd.h>
32#include <linux/namei.h>
33#include <linux/mount.h>
34#include <linux/diskdump.h>
35#include <asm/diskdump.h>
36#include <asm/crashdump.h>
37#include "../fs/sysfs/sysfs.h"
38
39static DECLARE_MUTEX(dump_ops_mutex);
40struct disk_dump_ops* dump_ops = NULL;
41
42int diskdump_mode = 0;
43EXPORT_SYMBOL_GPL(diskdump_mode);
44
45void (*diskdump_func) (struct pt_regs *regs) = NULL;
46EXPORT_SYMBOL_GPL(diskdump_func);
47
48static unsigned long long timestamp_base;
49static unsigned long timestamp_hz;
50
51
52
53
54
55int diskdump_register_ops(struct disk_dump_ops* op)
56{
57 down(&dump_ops_mutex);
58 if (dump_ops) {
59 up(&dump_ops_mutex);
60 return -EEXIST;
61 }
62 dump_ops = op;
63 up(&dump_ops_mutex);
64
65 return 0;
66}
67
68EXPORT_SYMBOL_GPL(diskdump_register_ops);
69
70void diskdump_unregister_ops(void)
71{
72 down(&dump_ops_mutex);
73 dump_ops = NULL;
74 up(&dump_ops_mutex);
75}
76
77EXPORT_SYMBOL_GPL(diskdump_unregister_ops);
78
79
80
81
82
83static struct gendisk *device_to_gendisk(struct device *dev)
84{
85 struct nameidata nd;
86 struct sysfs_dirent *sd;
87 struct kobject *kobj;
88 int rc;
89
90
91 nd.mnt = mntget(sysfs_mount);
92 nd.dentry = dget(dev->kobj.dentry);
93 nd.flags = LOOKUP_FOLLOW;
94 nd.last_type = LAST_ROOT;
95 nd.depth = 0;
96 rc = link_path_walk("block", &nd);
97 if (rc < 0) {
98 if (rc == -ENOENT)
99 return NULL;
100 goto err;
101 }
102 sd = nd.dentry->d_fsdata;
103 kobj = sd ? kobject_get(sd->s_element) : NULL;
104 path_release(&nd);
105 if (!kobj)
106 goto err;
107 return container_of(kobj, struct gendisk, kobj);
108
109err:
110 printk(KERN_WARNING "dump: device has no block attribute\n");
111 return NULL;
112}
113
114ssize_t diskdump_sysfs_store(struct device *dev, const char *buf, size_t count)
115{
116 struct gendisk *disk;
117
118
119 if (!dump_ops || !dump_ops->add_dump || !dump_ops->remove_dump)
120 return count;
121
122
123 disk = device_to_gendisk(dev);
124
125 if (disk) {
126 count = diskdump_sysfs_store_disk(disk, dev, buf, count);
127 put_disk(disk);
128 }
129
130 return count;
131}
132
133EXPORT_SYMBOL_GPL(diskdump_sysfs_store);
134
135ssize_t diskdump_sysfs_store_disk(struct gendisk *disk, struct device *dev, const char *buf, size_t count)
136{
137 struct block_device *bdev;
138 int part, remove = 0;
139
140 if (!dump_ops || !dump_ops->add_dump || !dump_ops->remove_dump)
141 return count;
142
143
144 if (sscanf (buf, "%d\n", &part) != 1)
145 return -EINVAL;
146
147 if (part < 0) {
148 part = -part;
149 remove = 1;
150 }
151
152 if (part >= disk->minors)
153 return -EINVAL;
154
155
156 if (!(bdev = bdget_disk(disk, part)))
157 return count;
158
159
160 down(&dump_ops_mutex);
161 if (!remove)
162 dump_ops->add_dump(dev, bdev);
163 else
164 dump_ops->remove_dump(bdev);
165 up(&dump_ops_mutex);
166
167 return count;
168}
169
170EXPORT_SYMBOL_GPL(diskdump_sysfs_store_disk);
171
172ssize_t diskdump_sysfs_show(struct device *dev, char *buf)
173{
174 struct gendisk *disk;
175 ssize_t len;
176
177
178 if (!dump_ops || !dump_ops->find_dump)
179 return 0;
180
181
182 disk = device_to_gendisk(dev);
183 if (!disk)
184 return 0;
185
186 len = diskdump_sysfs_show_disk(disk, buf);
187
188 put_disk(disk);
189
190 return len;
191}
192
193EXPORT_SYMBOL_GPL(diskdump_sysfs_show);
194
195ssize_t diskdump_sysfs_show_disk(struct gendisk *disk, char *buf)
196{
197 struct block_device *bdev;
198 int part, tmp, len = 0, maxlen = 1024;
199 char* p = buf;
200 char name[BDEVNAME_SIZE];
201
202 if (!dump_ops || !dump_ops->find_dump)
203 return 0;
204
205 if (!disk->part)
206 return 0;
207
208
209 down(&dump_ops_mutex);
210 for (part = 0; part < disk->minors; part++) {
211 bdev = bdget_disk(disk, part);
212 if (dump_ops->find_dump(bdev)) {
213 tmp = sprintf(p, "%s\n", bdevname(bdev, name));
214 len += tmp;
215 p += tmp;
216 }
217 bdput(bdev);
218 if(len >= maxlen)
219 break;
220 }
221 up(&dump_ops_mutex);
222
223 return len;
224}
225
226EXPORT_SYMBOL_GPL(diskdump_sysfs_show_disk);
227
228
229
230
231void diskdump_setup_timestamp(void)
232{
233 unsigned long long t;
234
235 platform_timestamp(timestamp_base);
236 udelay(1000000/HZ);
237 platform_timestamp(t);
238 timestamp_hz = (unsigned long)(t - timestamp_base);
239 diskdump_update();
240}
241
242EXPORT_SYMBOL_GPL(diskdump_setup_timestamp);
243
244void diskdump_update(void)
245{
246 unsigned long long t;
247
248 touch_nmi_watchdog();
249
250
251 platform_timestamp(t);
252 while (t > timestamp_base + timestamp_hz) {
253 timestamp_base += timestamp_hz;
254 jiffies++;
255 platform_timestamp(t);
256 }
257
258 dump_run_timers();
259 dump_run_tasklet();
260 dump_run_workqueue();
261}
262
263EXPORT_SYMBOL_GPL(diskdump_update);
264
265
266
267
268
269int diskdump_register_hook(void (*dump_func) (struct pt_regs *))
270{
271 if (diskdump_func)
272 return -EEXIST;
273
274 diskdump_func = dump_func;
275
276 return 0;
277}
278
279EXPORT_SYMBOL_GPL(diskdump_register_hook);
280
281void diskdump_unregister_hook(void)
282{
283 diskdump_func = NULL;
284}
285
286EXPORT_SYMBOL_GPL(diskdump_unregister_hook);
287
288void (*netdump_func) (struct pt_regs *regs) = NULL;
289int netdump_mode = 0;
290EXPORT_SYMBOL_GPL(netdump_mode);
291
292
293
294
295
296
297
298void try_crashdump(struct pt_regs *regs)
299{
300 void (*func)(struct pt_regs *);
301
302 if (diskdump_func) {
303 system_state = SYSTEM_DUMPING;
304 func = diskdump_func;
305 diskdump_func = NULL;
306 func(regs);
307 }
308 if (netdump_func)
309 netdump_func(regs);
310}
311
312extern unsigned long max_pfn;
313
314int diskdump_mark_free_pages(void)
315{
316 struct zone *zone;
317 unsigned long start_pfn, err_pfn, i, pfn;
318 int order, free_page_cnt = 0;
319 struct list_head *curr, *previous, *dlhead;
320
321
322
323
324
325 for (pfn = next_ram_page(ULONG_MAX); pfn < max_pfn; pfn = next_ram_page(pfn))
326 if (pfn_valid(pfn))
327 ClearPageNosaveFree(pfn_to_page(pfn));
328
329 for_each_zone(zone) {
330 if (!zone->spanned_pages)
331 continue;
332
333 for (order = MAX_ORDER - 1; order >= 0; --order) {
334
335
336
337 dlhead = &zone->free_area[order].free_list;
338
339 for (previous = dlhead, curr = dlhead->next;
340 curr != dlhead;
341 previous=curr, curr = curr->next) {
342
343 start_pfn = page_to_pfn(
344 list_entry(curr, struct page, lru));
345
346 if (!pfn_valid(start_pfn) ||
347 (previous != curr->prev)) {
348 err_pfn = start_pfn;
349 goto mark_err;
350 }
351
352 for (i = 0; i < (1<<order); i++) {
353 pfn = start_pfn + i;
354 if (!pfn_valid(pfn) ||
355 TestSetPageNosaveFree(
356 pfn_to_page(pfn))) {
357 err_pfn = pfn;
358 goto mark_err;
359 }
360 }
361 free_page_cnt += i;
362 }
363 }
364 }
365 return free_page_cnt;
366
367mark_err:
368 printk(KERN_WARNING "dump: Bad page. PFN %lu.", err_pfn);
369 printk(KERN_WARNING "DUMP_LEVEL will be ignored. Free pages will be dumped.");
370 return -1;
371}
372
373EXPORT_SYMBOL_GPL(diskdump_mark_free_pages);
374