mtd: mtdoops: Create a header structure for the saved mtdoops.
[linux-2.6-block.git] / drivers / mtd / mtdoops.c
CommitLineData
2b27bdcc 1// SPDX-License-Identifier: GPL-2.0-only
4b23aff0
RP
2/*
3 * MTD Oops/Panic logger
4 *
a1452a37 5 * Copyright © 2007 Nokia Corporation. All rights reserved.
4b23aff0
RP
6 *
7 * Author: Richard Purdie <rpurdie@openedhand.com>
4b23aff0
RP
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/console.h>
13#include <linux/vmalloc.h>
14#include <linux/workqueue.h>
15#include <linux/sched.h>
16#include <linux/wait.h>
621e4f8e 17#include <linux/delay.h>
f9f7dd22 18#include <linux/interrupt.h>
4b23aff0 19#include <linux/mtd/mtd.h>
2e386e4b 20#include <linux/kmsg_dump.h>
4b23aff0 21
1114e3d0
SK
22/* Maximum MTD partition size */
23#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
24
9507b0c8
SK
25static unsigned long record_size = 4096;
26module_param(record_size, ulong, 0400);
27MODULE_PARM_DESC(record_size,
28 "record size for MTD OOPS pages in bytes (default 4096)");
4b23aff0 29
2e386e4b
SK
30static char mtddev[80];
31module_param_string(mtddev, mtddev, 80, 0400);
32MODULE_PARM_DESC(mtddev,
33 "name or index number of the MTD device to use");
34
35static int dump_oops = 1;
36module_param(dump_oops, int, 0600);
37MODULE_PARM_DESC(dump_oops,
38 "set to 1 to dump oopses, 0 to only dump panics (default 1)");
39
0bd359ee
JME
40#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
41
42struct mtdoops_hdr {
43 u32 seq;
44 u32 magic;
45} __packed;
46
7903cbab 47static struct mtdoops_context {
2e386e4b
SK
48 struct kmsg_dumper dump;
49
4b23aff0 50 int mtd_index;
6ce0a856
RP
51 struct work_struct work_erase;
52 struct work_struct work_write;
4b23aff0
RP
53 struct mtd_info *mtd;
54 int oops_pages;
55 int nextpage;
56 int nextcount;
be95745f 57 unsigned long *oops_page_used;
4b23aff0 58
40ddbbac 59 unsigned long oops_buf_busy;
4b23aff0 60 void *oops_buf;
4b23aff0
RP
61} oops_cxt;
62
be95745f
SK
63static void mark_page_used(struct mtdoops_context *cxt, int page)
64{
65 set_bit(page, cxt->oops_page_used);
66}
67
68static void mark_page_unused(struct mtdoops_context *cxt, int page)
69{
70 clear_bit(page, cxt->oops_page_used);
71}
72
73static int page_is_used(struct mtdoops_context *cxt, int page)
74{
75 return test_bit(page, cxt->oops_page_used);
76}
77
be95745f 78static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
4b23aff0 79{
be95745f
SK
80 struct mtd_info *mtd = cxt->mtd;
81 u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize;
9507b0c8
SK
82 u32 start_page = start_page_offset / record_size;
83 u32 erase_pages = mtd->erasesize / record_size;
4b23aff0 84 struct erase_info erase;
4b23aff0 85 int ret;
be95745f 86 int page;
4b23aff0 87
4b23aff0 88 erase.addr = offset;
79dcd8e9 89 erase.len = mtd->erasesize;
4b23aff0 90
7e1f0dc0 91 ret = mtd_erase(mtd, &erase);
4b23aff0 92 if (ret) {
a15b124f
AB
93 printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
94 (unsigned long long)erase.addr,
2e386e4b 95 (unsigned long long)erase.len, mtddev);
4b23aff0
RP
96 return ret;
97 }
98
be95745f
SK
99 /* Mark pages as unused */
100 for (page = start_page; page < start_page + erase_pages; page++)
101 mark_page_unused(cxt, page);
102
4b23aff0
RP
103 return 0;
104}
105
6ce0a856 106static void mtdoops_inc_counter(struct mtdoops_context *cxt)
4b23aff0 107{
4b23aff0 108 cxt->nextpage++;
ecd5b310 109 if (cxt->nextpage >= cxt->oops_pages)
4b23aff0
RP
110 cxt->nextpage = 0;
111 cxt->nextcount++;
112 if (cxt->nextcount == 0xffffffff)
113 cxt->nextcount = 0;
114
be95745f 115 if (page_is_used(cxt, cxt->nextpage)) {
6ce0a856
RP
116 schedule_work(&cxt->work_erase);
117 return;
118 }
4b23aff0 119
a15b124f
AB
120 printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
121 cxt->nextpage, cxt->nextcount);
4b23aff0
RP
122}
123
6ce0a856
RP
124/* Scheduled work - when we can't proceed without erasing a block */
125static void mtdoops_workfunc_erase(struct work_struct *work)
4b23aff0 126{
6ce0a856
RP
127 struct mtdoops_context *cxt =
128 container_of(work, struct mtdoops_context, work_erase);
4b23aff0
RP
129 struct mtd_info *mtd = cxt->mtd;
130 int i = 0, j, ret, mod;
131
132 /* We were unregistered */
133 if (!mtd)
134 return;
135
9507b0c8 136 mod = (cxt->nextpage * record_size) % mtd->erasesize;
4b23aff0 137 if (mod != 0) {
9507b0c8 138 cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / record_size);
ecd5b310 139 if (cxt->nextpage >= cxt->oops_pages)
4b23aff0
RP
140 cxt->nextpage = 0;
141 }
142
9cb93fbb 143 while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
4b23aff0 144badblock:
9507b0c8
SK
145 printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
146 cxt->nextpage * record_size);
4b23aff0 147 i++;
9507b0c8 148 cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
ecd5b310 149 if (cxt->nextpage >= cxt->oops_pages)
4b23aff0 150 cxt->nextpage = 0;
9507b0c8 151 if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
a15b124f 152 printk(KERN_ERR "mtdoops: all blocks bad!\n");
4b23aff0
RP
153 return;
154 }
155 }
156
9cb93fbb
BN
157 if (ret < 0) {
158 printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
159 return;
160 }
161
4b23aff0 162 for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
9507b0c8 163 ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
4b23aff0 164
2986bd2a 165 if (ret >= 0) {
a15b124f
AB
166 printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
167 cxt->nextpage, cxt->nextcount);
2986bd2a 168 return;
4b23aff0
RP
169 }
170
bb4a0986 171 if (ret == -EIO) {
5942ddbc 172 ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
bb4a0986 173 if (ret < 0 && ret != -EOPNOTSUPP) {
a15b124f 174 printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
2986bd2a
RP
175 return;
176 }
177 }
178 goto badblock;
4b23aff0
RP
179}
180
621e4f8e 181static void mtdoops_write(struct mtdoops_context *cxt, int panic)
4b23aff0 182{
6ce0a856
RP
183 struct mtd_info *mtd = cxt->mtd;
184 size_t retlen;
0bd359ee 185 struct mtdoops_hdr *hdr;
6ce0a856 186 int ret;
4b23aff0 187
40ddbbac
JO
188 if (test_and_set_bit(0, &cxt->oops_buf_busy))
189 return;
190
2e386e4b 191 /* Add mtdoops header to the buffer */
0bd359ee
JME
192 hdr = (struct mtdoops_hdr *)cxt->oops_buf;
193 hdr->seq = cxt->nextcount;
194 hdr->magic = MTDOOPS_KERNMSG_MAGIC;
6ce0a856 195
016c1291 196 if (panic) {
7ae79d7f
AB
197 ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
198 record_size, &retlen, cxt->oops_buf);
016c1291
AB
199 if (ret == -EOPNOTSUPP) {
200 printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
40ddbbac 201 goto out;
016c1291
AB
202 }
203 } else
eda95cbf
AB
204 ret = mtd_write(mtd, cxt->nextpage * record_size,
205 record_size, &retlen, cxt->oops_buf);
6ce0a856 206
9507b0c8
SK
207 if (retlen != record_size || ret < 0)
208 printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
209 cxt->nextpage * record_size, retlen, record_size, ret);
be95745f 210 mark_page_used(cxt, cxt->nextpage);
2e386e4b 211 memset(cxt->oops_buf, 0xff, record_size);
6ce0a856
RP
212
213 mtdoops_inc_counter(cxt);
40ddbbac
JO
214out:
215 clear_bit(0, &cxt->oops_buf_busy);
621e4f8e
RP
216}
217
621e4f8e
RP
218static void mtdoops_workfunc_write(struct work_struct *work)
219{
220 struct mtdoops_context *cxt =
221 container_of(work, struct mtdoops_context, work_write);
222
223 mtdoops_write(cxt, 0);
a15b124f 224}
4b23aff0 225
6ce0a856 226static void find_next_position(struct mtdoops_context *cxt)
4b23aff0
RP
227{
228 struct mtd_info *mtd = cxt->mtd;
0bd359ee 229 struct mtdoops_hdr hdr;
2986bd2a 230 int ret, page, maxpos = 0;
0bd359ee 231 u32 maxcount = 0xffffffff;
4b23aff0
RP
232 size_t retlen;
233
234 for (page = 0; page < cxt->oops_pages; page++) {
bb4a0986 235 if (mtd_block_isbad(mtd, page * record_size))
3538c563 236 continue;
be95745f
SK
237 /* Assume the page is used */
238 mark_page_used(cxt, page);
0bd359ee
JME
239 ret = mtd_read(mtd, page * record_size, sizeof(hdr),
240 &retlen, (u_char *)&hdr);
241 if (retlen != sizeof(hdr) ||
d57f4054 242 (ret < 0 && !mtd_is_bitflip(ret))) {
0bd359ee
JME
243 printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n",
244 page * record_size, retlen, sizeof(hdr), ret);
2986bd2a
RP
245 continue;
246 }
247
0bd359ee 248 if (hdr.seq == 0xffffffff && hdr.magic == 0xffffffff)
be95745f 249 mark_page_unused(cxt, page);
0bd359ee 250 if (hdr.seq == 0xffffffff || hdr.magic != MTDOOPS_KERNMSG_MAGIC)
4b23aff0
RP
251 continue;
252 if (maxcount == 0xffffffff) {
0bd359ee 253 maxcount = hdr.seq;
4b23aff0 254 maxpos = page;
0bd359ee
JME
255 } else if (hdr.seq < 0x40000000 && maxcount > 0xc0000000) {
256 maxcount = hdr.seq;
4b23aff0 257 maxpos = page;
0bd359ee
JME
258 } else if (hdr.seq > maxcount && hdr.seq < 0xc0000000) {
259 maxcount = hdr.seq;
4b23aff0 260 maxpos = page;
0bd359ee 261 } else if (hdr.seq > maxcount && hdr.seq > 0xc0000000
a15b124f 262 && maxcount > 0x80000000) {
0bd359ee 263 maxcount = hdr.seq;
4b23aff0
RP
264 maxpos = page;
265 }
266 }
267 if (maxcount == 0xffffffff) {
cd409c61
MC
268 cxt->nextpage = cxt->oops_pages - 1;
269 cxt->nextcount = 0;
270 }
271 else {
272 cxt->nextpage = maxpos;
273 cxt->nextcount = maxcount;
4b23aff0 274 }
4b23aff0 275
6ce0a856 276 mtdoops_inc_counter(cxt);
4b23aff0
RP
277}
278
2e386e4b 279static void mtdoops_do_dump(struct kmsg_dumper *dumper,
e2ae715d 280 enum kmsg_dump_reason reason)
2e386e4b
SK
281{
282 struct mtdoops_context *cxt = container_of(dumper,
283 struct mtdoops_context, dump);
f9f3f02d 284 struct kmsg_dump_iter iter;
fc2d557c 285
2e386e4b
SK
286 /* Only dump oopses if dump_oops is set */
287 if (reason == KMSG_DUMP_OOPS && !dump_oops)
288 return;
289
f9f3f02d
JO
290 kmsg_dump_rewind(&iter);
291
40ddbbac
JO
292 if (test_and_set_bit(0, &cxt->oops_buf_busy))
293 return;
0bd359ee
JME
294 kmsg_dump_get_buffer(&iter, true,
295 cxt->oops_buf + sizeof(struct mtdoops_hdr),
296 record_size - sizeof(struct mtdoops_hdr), NULL);
40ddbbac 297 clear_bit(0, &cxt->oops_buf_busy);
2e386e4b 298
c1cf1d57
MT
299 if (reason != KMSG_DUMP_OOPS) {
300 /* Panics must be written immediately */
016c1291 301 mtdoops_write(cxt, 1);
c1cf1d57
MT
302 } else {
303 /* For other cases, schedule work to write it "nicely" */
304 schedule_work(&cxt->work_write);
305 }
2e386e4b 306}
4b23aff0
RP
307
308static void mtdoops_notify_add(struct mtd_info *mtd)
309{
310 struct mtdoops_context *cxt = &oops_cxt;
2e386e4b
SK
311 u64 mtdoops_pages = div_u64(mtd->size, record_size);
312 int err;
4b23aff0 313
2e386e4b 314 if (!strcmp(mtd->name, mtddev))
e2a0f25b
AH
315 cxt->mtd_index = mtd->index;
316
a15b124f 317 if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
4b23aff0
RP
318 return;
319
a15b124f
AB
320 if (mtd->size < mtd->erasesize * 2) {
321 printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n",
322 mtd->index);
4b23aff0
RP
323 return;
324 }
9507b0c8 325 if (mtd->erasesize < record_size) {
a15b124f
AB
326 printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n",
327 mtd->index);
79dcd8e9
RP
328 return;
329 }
1114e3d0
SK
330 if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
331 printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n",
332 mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
333 return;
334 }
335
be95745f 336 /* oops_page_used is a bit field */
42bc47b3
KC
337 cxt->oops_page_used =
338 vmalloc(array_size(sizeof(unsigned long),
339 DIV_ROUND_UP(mtdoops_pages,
340 BITS_PER_LONG)));
be95745f 341 if (!cxt->oops_page_used) {
2e386e4b
SK
342 printk(KERN_ERR "mtdoops: could not allocate page array\n");
343 return;
344 }
345
e2ae715d 346 cxt->dump.max_reason = KMSG_DUMP_OOPS;
2e386e4b
SK
347 cxt->dump.dump = mtdoops_do_dump;
348 err = kmsg_dump_register(&cxt->dump);
349 if (err) {
350 printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
351 vfree(cxt->oops_page_used);
352 cxt->oops_page_used = NULL;
be95745f
SK
353 return;
354 }
4b23aff0 355
1114e3d0 356 cxt->mtd = mtd;
9507b0c8 357 cxt->oops_pages = (int)mtd->size / record_size;
6ce0a856 358 find_next_position(cxt);
79dcd8e9 359 printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
4b23aff0
RP
360}
361
362static void mtdoops_notify_remove(struct mtd_info *mtd)
363{
364 struct mtdoops_context *cxt = &oops_cxt;
365
a15b124f 366 if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
4b23aff0
RP
367 return;
368
2e386e4b
SK
369 if (kmsg_dump_unregister(&cxt->dump) < 0)
370 printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
371
4b23aff0 372 cxt->mtd = NULL;
43829731
TH
373 flush_work(&cxt->work_erase);
374 flush_work(&cxt->work_write);
4b23aff0
RP
375}
376
4b23aff0
RP
377
378static struct mtd_notifier mtdoops_notifier = {
379 .add = mtdoops_notify_add,
380 .remove = mtdoops_notify_remove,
381};
382
2e386e4b 383static int __init mtdoops_init(void)
4b23aff0
RP
384{
385 struct mtdoops_context *cxt = &oops_cxt;
2e386e4b
SK
386 int mtd_index;
387 char *endp;
4b23aff0 388
2e386e4b
SK
389 if (strlen(mtddev) == 0) {
390 printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
391 return -EINVAL;
392 }
9507b0c8
SK
393 if ((record_size & 4095) != 0) {
394 printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n");
395 return -EINVAL;
396 }
397 if (record_size < 4096) {
398 printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n");
399 return -EINVAL;
400 }
2e386e4b
SK
401
402 /* Setup the MTD device to use */
4b23aff0 403 cxt->mtd_index = -1;
2e386e4b
SK
404 mtd_index = simple_strtoul(mtddev, &endp, 0);
405 if (*endp == '\0')
406 cxt->mtd_index = mtd_index;
2e386e4b 407
9507b0c8 408 cxt->oops_buf = vmalloc(record_size);
313ea21a 409 if (!cxt->oops_buf)
4b23aff0 410 return -ENOMEM;
2e386e4b 411 memset(cxt->oops_buf, 0xff, record_size);
40ddbbac 412 cxt->oops_buf_busy = 0;
4b23aff0 413
6ce0a856
RP
414 INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
415 INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
4b23aff0 416
4b23aff0
RP
417 register_mtd_user(&mtdoops_notifier);
418 return 0;
419}
420
2e386e4b 421static void __exit mtdoops_exit(void)
4b23aff0
RP
422{
423 struct mtdoops_context *cxt = &oops_cxt;
424
425 unregister_mtd_user(&mtdoops_notifier);
4b23aff0 426 vfree(cxt->oops_buf);
be95745f 427 vfree(cxt->oops_page_used);
4b23aff0
RP
428}
429
430
2e386e4b
SK
431module_init(mtdoops_init);
432module_exit(mtdoops_exit);
4b23aff0
RP
433
434MODULE_LICENSE("GPL");
435MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
436MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");