diff options
author | Theodore Ts'o <tytso@mit.edu> | 2013-10-17 21:11:01 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2013-10-17 21:11:01 -0400 |
commit | efbed4dc5857f845d787e406ce85097d1ccc5c4f (patch) | |
tree | 4cfd6a4c20e52b21c3f26632c849dd084df6f632 /fs | |
parent | aeac589a74b91c4c07458272767e089810fbd23d (diff) |
ext4: add ratelimiting to ext4 messages
In the case of a storage device that suddenly disappears, or in the
case of significant file system corruption, this can result in a huge
flood of messages being sent to the console. This can overflow the
file system containing /var/log/messages, or if a serial console is
configured, this can slow down the system so much that a hardware
watchdog can end up triggering forcing a system reboot.
Google-Bug-Id: 7258357
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 6 | ||||
-rw-r--r-- | fs/ext4/super.c | 152 |
2 files changed, 100 insertions, 58 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index af815ea9d7cc..65485ab7a889 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/wait.h> | 29 | #include <linux/wait.h> |
30 | #include <linux/blockgroup_lock.h> | 30 | #include <linux/blockgroup_lock.h> |
31 | #include <linux/percpu_counter.h> | 31 | #include <linux/percpu_counter.h> |
32 | #include <linux/ratelimit.h> | ||
32 | #include <crypto/hash.h> | 33 | #include <crypto/hash.h> |
33 | #ifdef __KERNEL__ | 34 | #ifdef __KERNEL__ |
34 | #include <linux/compat.h> | 35 | #include <linux/compat.h> |
@@ -1314,6 +1315,11 @@ struct ext4_sb_info { | |||
1314 | unsigned long s_es_last_sorted; | 1315 | unsigned long s_es_last_sorted; |
1315 | struct percpu_counter s_extent_cache_cnt; | 1316 | struct percpu_counter s_extent_cache_cnt; |
1316 | spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp; | 1317 | spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp; |
1318 | |||
1319 | /* Ratelimit ext4 messages. */ | ||
1320 | struct ratelimit_state s_err_ratelimit_state; | ||
1321 | struct ratelimit_state s_warning_ratelimit_state; | ||
1322 | struct ratelimit_state s_msg_ratelimit_state; | ||
1317 | }; | 1323 | }; |
1318 | 1324 | ||
1319 | static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) | 1325 | static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 2c2e6cbc6bed..d3a857bfae47 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -411,20 +411,26 @@ static void ext4_handle_error(struct super_block *sb) | |||
411 | sb->s_id); | 411 | sb->s_id); |
412 | } | 412 | } |
413 | 413 | ||
414 | #define ext4_error_ratelimit(sb) \ | ||
415 | ___ratelimit(&(EXT4_SB(sb)->s_err_ratelimit_state), \ | ||
416 | "EXT4-fs error") | ||
417 | |||
414 | void __ext4_error(struct super_block *sb, const char *function, | 418 | void __ext4_error(struct super_block *sb, const char *function, |
415 | unsigned int line, const char *fmt, ...) | 419 | unsigned int line, const char *fmt, ...) |
416 | { | 420 | { |
417 | struct va_format vaf; | 421 | struct va_format vaf; |
418 | va_list args; | 422 | va_list args; |
419 | 423 | ||
420 | va_start(args, fmt); | 424 | if (ext4_error_ratelimit(sb)) { |
421 | vaf.fmt = fmt; | 425 | va_start(args, fmt); |
422 | vaf.va = &args; | 426 | vaf.fmt = fmt; |
423 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n", | 427 | vaf.va = &args; |
424 | sb->s_id, function, line, current->comm, &vaf); | 428 | printk(KERN_CRIT |
425 | va_end(args); | 429 | "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n", |
430 | sb->s_id, function, line, current->comm, &vaf); | ||
431 | va_end(args); | ||
432 | } | ||
426 | save_error_info(sb, function, line); | 433 | save_error_info(sb, function, line); |
427 | |||
428 | ext4_handle_error(sb); | 434 | ext4_handle_error(sb); |
429 | } | 435 | } |
430 | 436 | ||
@@ -438,22 +444,23 @@ void __ext4_error_inode(struct inode *inode, const char *function, | |||
438 | 444 | ||
439 | es->s_last_error_ino = cpu_to_le32(inode->i_ino); | 445 | es->s_last_error_ino = cpu_to_le32(inode->i_ino); |
440 | es->s_last_error_block = cpu_to_le64(block); | 446 | es->s_last_error_block = cpu_to_le64(block); |
447 | if (ext4_error_ratelimit(inode->i_sb)) { | ||
448 | va_start(args, fmt); | ||
449 | vaf.fmt = fmt; | ||
450 | vaf.va = &args; | ||
451 | if (block) | ||
452 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: " | ||
453 | "inode #%lu: block %llu: comm %s: %pV\n", | ||
454 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
455 | block, current->comm, &vaf); | ||
456 | else | ||
457 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: " | ||
458 | "inode #%lu: comm %s: %pV\n", | ||
459 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
460 | current->comm, &vaf); | ||
461 | va_end(args); | ||
462 | } | ||
441 | save_error_info(inode->i_sb, function, line); | 463 | save_error_info(inode->i_sb, function, line); |
442 | va_start(args, fmt); | ||
443 | vaf.fmt = fmt; | ||
444 | vaf.va = &args; | ||
445 | if (block) | ||
446 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: " | ||
447 | "inode #%lu: block %llu: comm %s: %pV\n", | ||
448 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
449 | block, current->comm, &vaf); | ||
450 | else | ||
451 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: " | ||
452 | "inode #%lu: comm %s: %pV\n", | ||
453 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
454 | current->comm, &vaf); | ||
455 | va_end(args); | ||
456 | |||
457 | ext4_handle_error(inode->i_sb); | 464 | ext4_handle_error(inode->i_sb); |
458 | } | 465 | } |
459 | 466 | ||
@@ -469,27 +476,28 @@ void __ext4_error_file(struct file *file, const char *function, | |||
469 | 476 | ||
470 | es = EXT4_SB(inode->i_sb)->s_es; | 477 | es = EXT4_SB(inode->i_sb)->s_es; |
471 | es->s_last_error_ino = cpu_to_le32(inode->i_ino); | 478 | es->s_last_error_ino = cpu_to_le32(inode->i_ino); |
479 | if (ext4_error_ratelimit(inode->i_sb)) { | ||
480 | path = d_path(&(file->f_path), pathname, sizeof(pathname)); | ||
481 | if (IS_ERR(path)) | ||
482 | path = "(unknown)"; | ||
483 | va_start(args, fmt); | ||
484 | vaf.fmt = fmt; | ||
485 | vaf.va = &args; | ||
486 | if (block) | ||
487 | printk(KERN_CRIT | ||
488 | "EXT4-fs error (device %s): %s:%d: inode #%lu: " | ||
489 | "block %llu: comm %s: path %s: %pV\n", | ||
490 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
491 | block, current->comm, path, &vaf); | ||
492 | else | ||
493 | printk(KERN_CRIT | ||
494 | "EXT4-fs error (device %s): %s:%d: inode #%lu: " | ||
495 | "comm %s: path %s: %pV\n", | ||
496 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
497 | current->comm, path, &vaf); | ||
498 | va_end(args); | ||
499 | } | ||
472 | save_error_info(inode->i_sb, function, line); | 500 | save_error_info(inode->i_sb, function, line); |
473 | path = d_path(&(file->f_path), pathname, sizeof(pathname)); | ||
474 | if (IS_ERR(path)) | ||
475 | path = "(unknown)"; | ||
476 | va_start(args, fmt); | ||
477 | vaf.fmt = fmt; | ||
478 | vaf.va = &args; | ||
479 | if (block) | ||
480 | printk(KERN_CRIT | ||
481 | "EXT4-fs error (device %s): %s:%d: inode #%lu: " | ||
482 | "block %llu: comm %s: path %s: %pV\n", | ||
483 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
484 | block, current->comm, path, &vaf); | ||
485 | else | ||
486 | printk(KERN_CRIT | ||
487 | "EXT4-fs error (device %s): %s:%d: inode #%lu: " | ||
488 | "comm %s: path %s: %pV\n", | ||
489 | inode->i_sb->s_id, function, line, inode->i_ino, | ||
490 | current->comm, path, &vaf); | ||
491 | va_end(args); | ||
492 | |||
493 | ext4_handle_error(inode->i_sb); | 501 | ext4_handle_error(inode->i_sb); |
494 | } | 502 | } |
495 | 503 | ||
@@ -543,11 +551,13 @@ void __ext4_std_error(struct super_block *sb, const char *function, | |||
543 | (sb->s_flags & MS_RDONLY)) | 551 | (sb->s_flags & MS_RDONLY)) |
544 | return; | 552 | return; |
545 | 553 | ||
546 | errstr = ext4_decode_error(sb, errno, nbuf); | 554 | if (ext4_error_ratelimit(sb)) { |
547 | printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", | 555 | errstr = ext4_decode_error(sb, errno, nbuf); |
548 | sb->s_id, function, line, errstr); | 556 | printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", |
549 | save_error_info(sb, function, line); | 557 | sb->s_id, function, line, errstr); |
558 | } | ||
550 | 559 | ||
560 | save_error_info(sb, function, line); | ||
551 | ext4_handle_error(sb); | 561 | ext4_handle_error(sb); |
552 | } | 562 | } |
553 | 563 | ||
@@ -597,6 +607,9 @@ void __ext4_msg(struct super_block *sb, | |||
597 | struct va_format vaf; | 607 | struct va_format vaf; |
598 | va_list args; | 608 | va_list args; |
599 | 609 | ||
610 | if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs")) | ||
611 | return; | ||
612 | |||
600 | va_start(args, fmt); | 613 | va_start(args, fmt); |
601 | vaf.fmt = fmt; | 614 | vaf.fmt = fmt; |
602 | vaf.va = &args; | 615 | vaf.va = &args; |
@@ -610,6 +623,10 @@ void __ext4_warning(struct super_block *sb, const char *function, | |||
610 | struct va_format vaf; | 623 | struct va_format vaf; |
611 | va_list args; | 624 | va_list args; |
612 | 625 | ||
626 | if (!___ratelimit(&(EXT4_SB(sb)->s_warning_ratelimit_state), | ||
627 | "EXT4-fs warning")) | ||
628 | return; | ||
629 | |||
613 | va_start(args, fmt); | 630 | va_start(args, fmt); |
614 | vaf.fmt = fmt; | 631 | vaf.fmt = fmt; |
615 | vaf.va = &args; | 632 | vaf.va = &args; |
@@ -633,18 +650,20 @@ __acquires(bitlock) | |||
633 | es->s_last_error_block = cpu_to_le64(block); | 650 | es->s_last_error_block = cpu_to_le64(block); |
634 | __save_error_info(sb, function, line); | 651 | __save_error_info(sb, function, line); |
635 | 652 | ||
636 | va_start(args, fmt); | 653 | if (ext4_error_ratelimit(sb)) { |
637 | 654 | va_start(args, fmt); | |
638 | vaf.fmt = fmt; | 655 | vaf.fmt = fmt; |
639 | vaf.va = &args; | 656 | vaf.va = &args; |
640 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u, ", | 657 | printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u, ", |
641 | sb->s_id, function, line, grp); | 658 | sb->s_id, function, line, grp); |
642 | if (ino) | 659 | if (ino) |
643 | printk(KERN_CONT "inode %lu: ", ino); | 660 | printk(KERN_CONT "inode %lu: ", ino); |
644 | if (block) | 661 | if (block) |
645 | printk(KERN_CONT "block %llu:", (unsigned long long) block); | 662 | printk(KERN_CONT "block %llu:", |
646 | printk(KERN_CONT "%pV\n", &vaf); | 663 | (unsigned long long) block); |
647 | va_end(args); | 664 | printk(KERN_CONT "%pV\n", &vaf); |
665 | va_end(args); | ||
666 | } | ||
648 | 667 | ||
649 | if (test_opt(sb, ERRORS_CONT)) { | 668 | if (test_opt(sb, ERRORS_CONT)) { |
650 | ext4_commit_super(sb, 0); | 669 | ext4_commit_super(sb, 0); |
@@ -2606,6 +2625,12 @@ EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc); | |||
2606 | EXT4_DEPRECATED_ATTR(max_writeback_mb_bump, 128); | 2625 | EXT4_DEPRECATED_ATTR(max_writeback_mb_bump, 128); |
2607 | EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb); | 2626 | EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb); |
2608 | EXT4_ATTR(trigger_fs_error, 0200, NULL, trigger_test_error); | 2627 | EXT4_ATTR(trigger_fs_error, 0200, NULL, trigger_test_error); |
2628 | EXT4_RW_ATTR_SBI_UI(err_ratelimit_interval_ms, s_err_ratelimit_state.interval); | ||
2629 | EXT4_RW_ATTR_SBI_UI(err_ratelimit_burst, s_err_ratelimit_state.burst); | ||
2630 | EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.interval); | ||
2631 | EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst); | ||
2632 | EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval); | ||
2633 | EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst); | ||
2609 | 2634 | ||
2610 | static struct attribute *ext4_attrs[] = { | 2635 | static struct attribute *ext4_attrs[] = { |
2611 | ATTR_LIST(delayed_allocation_blocks), | 2636 | ATTR_LIST(delayed_allocation_blocks), |
@@ -2623,6 +2648,12 @@ static struct attribute *ext4_attrs[] = { | |||
2623 | ATTR_LIST(max_writeback_mb_bump), | 2648 | ATTR_LIST(max_writeback_mb_bump), |
2624 | ATTR_LIST(extent_max_zeroout_kb), | 2649 | ATTR_LIST(extent_max_zeroout_kb), |
2625 | ATTR_LIST(trigger_fs_error), | 2650 | ATTR_LIST(trigger_fs_error), |
2651 | ATTR_LIST(err_ratelimit_interval_ms), | ||
2652 | ATTR_LIST(err_ratelimit_burst), | ||
2653 | ATTR_LIST(warning_ratelimit_interval_ms), | ||
2654 | ATTR_LIST(warning_ratelimit_burst), | ||
2655 | ATTR_LIST(msg_ratelimit_interval_ms), | ||
2656 | ATTR_LIST(msg_ratelimit_burst), | ||
2626 | NULL, | 2657 | NULL, |
2627 | }; | 2658 | }; |
2628 | 2659 | ||
@@ -4118,6 +4149,11 @@ no_journal: | |||
4118 | if (es->s_error_count) | 4149 | if (es->s_error_count) |
4119 | mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */ | 4150 | mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */ |
4120 | 4151 | ||
4152 | /* Enable message ratelimiting. Default is 10 messages per 5 secs. */ | ||
4153 | ratelimit_state_init(&sbi->s_err_ratelimit_state, 5 * HZ, 10); | ||
4154 | ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10); | ||
4155 | ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10); | ||
4156 | |||
4121 | kfree(orig_data); | 4157 | kfree(orig_data); |
4122 | return 0; | 4158 | return 0; |
4123 | 4159 | ||