diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/block_validity.c | 8 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 17 | ||||
-rw-r--r-- | fs/ext4/ext4_jbd2.c | 5 | ||||
-rw-r--r-- | fs/ext4/inode.c | 2 | ||||
-rw-r--r-- | fs/ext4/super.c | 80 |
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 | ||
310 | static 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 | |||
331 | static 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 | ||
325 | static void ext4_handle_error(struct super_block *sb) | 354 | static 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 | ||
502 | void ext4_msg (struct super_block * sb, const char *prefix, | 533 | void 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"); |