aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2010-07-27 11:56:03 -0400
committerTheodore Ts'o <tytso@mit.edu>2010-07-27 11:56:03 -0400
commit1c13d5c0872870cca3e612aa045d492ead9ab004 (patch)
tree6e3dd0d3f49ff56dda9fc6cd72c233759bc24e09
parentc398eda0e43a791be0fca6f197a1e2bbb9f16070 (diff)
ext4: Save error information to the superblock for analysis
Save number of file system errors, and the time function name, line number, block number, and inode number of the first and most recent errors reported on the file system in the superblock. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/block_validity.c8
-rw-r--r--fs/ext4/ext4.h17
-rw-r--r--fs/ext4/ext4_jbd2.c5
-rw-r--r--fs/ext4/inode.c2
-rw-r--r--fs/ext4/super.c80
5 files changed, 90 insertions, 22 deletions
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index 5b6973fbf1bd..3db5084db9bd 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -229,16 +229,20 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
229 229
230 if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || 230 if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
231 (start_blk + count < start_blk) || 231 (start_blk + count < start_blk) ||
232 (start_blk + count > ext4_blocks_count(sbi->s_es))) 232 (start_blk + count > ext4_blocks_count(sbi->s_es))) {
233 sbi->s_es->s_last_error_block = cpu_to_le64(start_blk);
233 return 0; 234 return 0;
235 }
234 while (n) { 236 while (n) {
235 entry = rb_entry(n, struct ext4_system_zone, node); 237 entry = rb_entry(n, struct ext4_system_zone, node);
236 if (start_blk + count - 1 < entry->start_blk) 238 if (start_blk + count - 1 < entry->start_blk)
237 n = n->rb_left; 239 n = n->rb_left;
238 else if (start_blk >= (entry->start_blk + entry->count)) 240 else if (start_blk >= (entry->start_blk + entry->count))
239 n = n->rb_right; 241 n = n->rb_right;
240 else 242 else {
243 sbi->s_es->s_last_error_block = cpu_to_le64(start_blk);
241 return 0; 244 return 0;
245 }
242 } 246 }
243 return 1; 247 return 1;
244} 248}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 088938148f5c..6b96125e7255 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1011,9 +1011,24 @@ struct ext4_super_block {
1011 snapshot's future use */ 1011 snapshot's future use */
1012 __le32 s_snapshot_list; /* inode number of the head of the 1012 __le32 s_snapshot_list; /* inode number of the head of the
1013 on-disk snapshot list */ 1013 on-disk snapshot list */
1014 __u32 s_reserved[155]; /* Padding to the end of the block */ 1014#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count)
1015 __le32 s_error_count; /* number of fs errors */
1016 __le32 s_first_error_time; /* first time an error happened */
1017 __le32 s_first_error_ino; /* inode involved in first error */
1018 __le64 s_first_error_block; /* block involved of first error */
1019 __u8 s_first_error_func[32]; /* function where the error happened */
1020 __le32 s_first_error_line; /* line number where error happened */
1021 __le32 s_last_error_time; /* most recent time of an error */
1022 __le32 s_last_error_ino; /* inode involved in last error */
1023 __le32 s_last_error_line; /* line number where error happened */
1024 __le64 s_last_error_block; /* block involved of last error */
1025 __u8 s_last_error_func[32]; /* function where the error happened */
1026#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_reserved)
1027 __le32 s_reserved[128]; /* Padding to the end of the block */
1015}; 1028};
1016 1029
1030#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START)
1031
1017#ifdef __KERNEL__ 1032#ifdef __KERNEL__
1018 1033
1019/* 1034/*
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 23425cd68daa..6e272ef6ba96 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -134,6 +134,11 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
134 if (inode && inode_needs_sync(inode)) { 134 if (inode && inode_needs_sync(inode)) {
135 sync_dirty_buffer(bh); 135 sync_dirty_buffer(bh);
136 if (buffer_req(bh) && !buffer_uptodate(bh)) { 136 if (buffer_req(bh) && !buffer_uptodate(bh)) {
137 struct ext4_super_block *es;
138
139 es = EXT4_SB(inode->i_sb)->s_es;
140 es->s_last_error_block =
141 cpu_to_le64(bh->b_blocknr);
137 ext4_error_inode(inode, where, line, 142 ext4_error_inode(inode, where, line,
138 bh->b_blocknr, 143 bh->b_blocknr,
139 "IO error syncing itable block"); 144 "IO error syncing itable block");
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 69ea663ef03e..755ba8682233 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -341,6 +341,7 @@ static int __ext4_check_blockref(const char *function, unsigned int line,
341 struct inode *inode, 341 struct inode *inode,
342 __le32 *p, unsigned int max) 342 __le32 *p, unsigned int max)
343{ 343{
344 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
344 __le32 *bref = p; 345 __le32 *bref = p;
345 unsigned int blk; 346 unsigned int blk;
346 347
@@ -349,6 +350,7 @@ static int __ext4_check_blockref(const char *function, unsigned int line,
349 if (blk && 350 if (blk &&
350 unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb), 351 unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
351 blk, 1))) { 352 blk, 1))) {
353 es->s_last_error_block = cpu_to_le64(blk);
352 ext4_error_inode(inode, function, line, blk, 354 ext4_error_inode(inode, function, line, blk,
353 "invalid block"); 355 "invalid block");
354 return -EIO; 356 return -EIO;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bcf74b31d014..a94d3f56898f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -307,6 +307,35 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line,
307 jbd2_journal_abort_handle(handle); 307 jbd2_journal_abort_handle(handle);
308} 308}
309 309
310static void __save_error_info(struct super_block *sb, const char *func,
311 unsigned int line)
312{
313 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
314
315 EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
316 es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
317 es->s_last_error_time = cpu_to_le32(get_seconds());
318 strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func));
319 es->s_last_error_line = cpu_to_le32(line);
320 if (!es->s_first_error_time) {
321 es->s_first_error_time = es->s_last_error_time;
322 strncpy(es->s_first_error_func, func,
323 sizeof(es->s_first_error_func));
324 es->s_first_error_line = cpu_to_le32(line);
325 es->s_first_error_ino = es->s_last_error_ino;
326 es->s_first_error_block = es->s_last_error_block;
327 }
328 es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);
329}
330
331static void save_error_info(struct super_block *sb, const char *func,
332 unsigned int line)
333{
334 __save_error_info(sb, func, line);
335 ext4_commit_super(sb, 1);
336}
337
338
310/* Deal with the reporting of failure conditions on a filesystem such as 339/* Deal with the reporting of failure conditions on a filesystem such as
311 * inconsistencies detected or read IO failures. 340 * inconsistencies detected or read IO failures.
312 * 341 *
@@ -324,11 +353,6 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line,
324 353
325static void ext4_handle_error(struct super_block *sb) 354static void ext4_handle_error(struct super_block *sb)
326{ 355{
327 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
328
329 EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
330 es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
331
332 if (sb->s_flags & MS_RDONLY) 356 if (sb->s_flags & MS_RDONLY)
333 return; 357 return;
334 358
@@ -343,7 +367,6 @@ static void ext4_handle_error(struct super_block *sb)
343 ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only"); 367 ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
344 sb->s_flags |= MS_RDONLY; 368 sb->s_flags |= MS_RDONLY;
345 } 369 }
346 ext4_commit_super(sb, 1);
347 if (test_opt(sb, ERRORS_PANIC)) 370 if (test_opt(sb, ERRORS_PANIC))
348 panic("EXT4-fs (device %s): panic forced after error\n", 371 panic("EXT4-fs (device %s): panic forced after error\n",
349 sb->s_id); 372 sb->s_id);
@@ -369,7 +392,11 @@ void ext4_error_inode(struct inode *inode, const char *function,
369 const char *fmt, ...) 392 const char *fmt, ...)
370{ 393{
371 va_list args; 394 va_list args;
395 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
372 396
397 es->s_last_error_ino = cpu_to_le32(inode->i_ino);
398 es->s_last_error_block = cpu_to_le64(block);
399 save_error_info(inode->i_sb, function, line);
373 va_start(args, fmt); 400 va_start(args, fmt);
374 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: ", 401 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: ",
375 inode->i_sb->s_id, function, line, inode->i_ino); 402 inode->i_sb->s_id, function, line, inode->i_ino);
@@ -387,9 +414,13 @@ void ext4_error_file(struct file *file, const char *function,
387 unsigned int line, const char *fmt, ...) 414 unsigned int line, const char *fmt, ...)
388{ 415{
389 va_list args; 416 va_list args;
417 struct ext4_super_block *es;
390 struct inode *inode = file->f_dentry->d_inode; 418 struct inode *inode = file->f_dentry->d_inode;
391 char pathname[80], *path; 419 char pathname[80], *path;
392 420
421 es = EXT4_SB(inode->i_sb)->s_es;
422 es->s_last_error_ino = cpu_to_le32(inode->i_ino);
423 save_error_info(inode->i_sb, function, line);
393 va_start(args, fmt); 424 va_start(args, fmt);
394 path = d_path(&(file->f_path), pathname, sizeof(pathname)); 425 path = d_path(&(file->f_path), pathname, sizeof(pathname));
395 if (!path) 426 if (!path)
@@ -459,6 +490,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
459 errstr = ext4_decode_error(sb, errno, nbuf); 490 errstr = ext4_decode_error(sb, errno, nbuf);
460 printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", 491 printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
461 sb->s_id, function, line, errstr); 492 sb->s_id, function, line, errstr);
493 save_error_info(sb, function, line);
462 494
463 ext4_handle_error(sb); 495 ext4_handle_error(sb);
464} 496}
@@ -478,6 +510,7 @@ void __ext4_abort(struct super_block *sb, const char *function,
478{ 510{
479 va_list args; 511 va_list args;
480 512
513 save_error_info(sb, function, line);
481 va_start(args, fmt); 514 va_start(args, fmt);
482 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id, 515 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
483 function, line); 516 function, line);
@@ -485,18 +518,16 @@ void __ext4_abort(struct super_block *sb, const char *function,
485 printk("\n"); 518 printk("\n");
486 va_end(args); 519 va_end(args);
487 520
521 if ((sb->s_flags & MS_RDONLY) == 0) {
522 ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
523 sb->s_flags |= MS_RDONLY;
524 EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
525 if (EXT4_SB(sb)->s_journal)
526 jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
527 save_error_info(sb, function, line);
528 }
488 if (test_opt(sb, ERRORS_PANIC)) 529 if (test_opt(sb, ERRORS_PANIC))
489 panic("EXT4-fs panic from previous error\n"); 530 panic("EXT4-fs panic from previous error\n");
490
491 if (sb->s_flags & MS_RDONLY)
492 return;
493
494 ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
495 EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
496 sb->s_flags |= MS_RDONLY;
497 EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
498 if (EXT4_SB(sb)->s_journal)
499 jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
500} 531}
501 532
502void ext4_msg (struct super_block * sb, const char *prefix, 533void ext4_msg (struct super_block * sb, const char *prefix,
@@ -534,6 +565,9 @@ __acquires(bitlock)
534 va_list args; 565 va_list args;
535 struct ext4_super_block *es = EXT4_SB(sb)->s_es; 566 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
536 567
568 es->s_last_error_ino = cpu_to_le32(ino);
569 es->s_last_error_block = cpu_to_le64(block);
570 __save_error_info(sb, function, line);
537 va_start(args, fmt); 571 va_start(args, fmt);
538 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u", 572 printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u",
539 sb->s_id, function, line, grp); 573 sb->s_id, function, line, grp);
@@ -546,11 +580,10 @@ __acquires(bitlock)
546 va_end(args); 580 va_end(args);
547 581
548 if (test_opt(sb, ERRORS_CONT)) { 582 if (test_opt(sb, ERRORS_CONT)) {
549 EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
550 es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
551 ext4_commit_super(sb, 0); 583 ext4_commit_super(sb, 0);
552 return; 584 return;
553 } 585 }
586
554 ext4_unlock_group(sb, grp); 587 ext4_unlock_group(sb, grp);
555 ext4_handle_error(sb); 588 ext4_handle_error(sb);
556 /* 589 /*
@@ -3332,8 +3365,17 @@ static int ext4_load_journal(struct super_block *sb,
3332 3365
3333 if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) 3366 if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER))
3334 err = jbd2_journal_wipe(journal, !really_read_only); 3367 err = jbd2_journal_wipe(journal, !really_read_only);
3335 if (!err) 3368 if (!err) {
3369 char *save = kmalloc(EXT4_S_ERR_LEN, GFP_KERNEL);
3370 if (save)
3371 memcpy(save, ((char *) es) +
3372 EXT4_S_ERR_START, EXT4_S_ERR_LEN);
3336 err = jbd2_journal_load(journal); 3373 err = jbd2_journal_load(journal);
3374 if (save)
3375 memcpy(((char *) es) + EXT4_S_ERR_START,
3376 save, EXT4_S_ERR_LEN);
3377 kfree(save);
3378 }
3337 3379
3338 if (err) { 3380 if (err) {
3339 ext4_msg(sb, KERN_ERR, "error loading journal"); 3381 ext4_msg(sb, KERN_ERR, "error loading journal");