diff options
-rw-r--r-- | fs/jbd2/checkpoint.c | 10 | ||||
-rw-r--r-- | fs/jbd2/commit.c | 49 | ||||
-rw-r--r-- | fs/jbd2/journal.c | 338 | ||||
-rw-r--r-- | fs/jbd2/transaction.c | 9 | ||||
-rw-r--r-- | include/linux/jbd2.h | 77 |
5 files changed, 481 insertions, 2 deletions
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 7e958c86242f..1b7f282c1ae9 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c | |||
@@ -232,7 +232,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count) | |||
232 | * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it | 232 | * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it |
233 | */ | 233 | */ |
234 | static int __process_buffer(journal_t *journal, struct journal_head *jh, | 234 | static int __process_buffer(journal_t *journal, struct journal_head *jh, |
235 | struct buffer_head **bhs, int *batch_count) | 235 | struct buffer_head **bhs, int *batch_count, |
236 | transaction_t *transaction) | ||
236 | { | 237 | { |
237 | struct buffer_head *bh = jh2bh(jh); | 238 | struct buffer_head *bh = jh2bh(jh); |
238 | int ret = 0; | 239 | int ret = 0; |
@@ -250,6 +251,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, | |||
250 | transaction_t *t = jh->b_transaction; | 251 | transaction_t *t = jh->b_transaction; |
251 | tid_t tid = t->t_tid; | 252 | tid_t tid = t->t_tid; |
252 | 253 | ||
254 | transaction->t_chp_stats.cs_forced_to_close++; | ||
253 | spin_unlock(&journal->j_list_lock); | 255 | spin_unlock(&journal->j_list_lock); |
254 | jbd_unlock_bh_state(bh); | 256 | jbd_unlock_bh_state(bh); |
255 | jbd2_log_start_commit(journal, tid); | 257 | jbd2_log_start_commit(journal, tid); |
@@ -279,6 +281,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, | |||
279 | bhs[*batch_count] = bh; | 281 | bhs[*batch_count] = bh; |
280 | __buffer_relink_io(jh); | 282 | __buffer_relink_io(jh); |
281 | jbd_unlock_bh_state(bh); | 283 | jbd_unlock_bh_state(bh); |
284 | transaction->t_chp_stats.cs_written++; | ||
282 | (*batch_count)++; | 285 | (*batch_count)++; |
283 | if (*batch_count == NR_BATCH) { | 286 | if (*batch_count == NR_BATCH) { |
284 | spin_unlock(&journal->j_list_lock); | 287 | spin_unlock(&journal->j_list_lock); |
@@ -322,6 +325,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) | |||
322 | if (!journal->j_checkpoint_transactions) | 325 | if (!journal->j_checkpoint_transactions) |
323 | goto out; | 326 | goto out; |
324 | transaction = journal->j_checkpoint_transactions; | 327 | transaction = journal->j_checkpoint_transactions; |
328 | if (transaction->t_chp_stats.cs_chp_time == 0) | ||
329 | transaction->t_chp_stats.cs_chp_time = jiffies; | ||
325 | this_tid = transaction->t_tid; | 330 | this_tid = transaction->t_tid; |
326 | restart: | 331 | restart: |
327 | /* | 332 | /* |
@@ -346,7 +351,8 @@ restart: | |||
346 | retry = 1; | 351 | retry = 1; |
347 | break; | 352 | break; |
348 | } | 353 | } |
349 | retry = __process_buffer(journal, jh, bhs,&batch_count); | 354 | retry = __process_buffer(journal, jh, bhs, &batch_count, |
355 | transaction); | ||
350 | if (!retry && lock_need_resched(&journal->j_list_lock)){ | 356 | if (!retry && lock_need_resched(&journal->j_list_lock)){ |
351 | spin_unlock(&journal->j_list_lock); | 357 | spin_unlock(&journal->j_list_lock); |
352 | retry = 1; | 358 | retry = 1; |
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 39b5cee3dd8a..8749a86f4175 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/mm.h> | 21 | #include <linux/mm.h> |
22 | #include <linux/pagemap.h> | 22 | #include <linux/pagemap.h> |
23 | #include <linux/jiffies.h> | ||
23 | 24 | ||
24 | /* | 25 | /* |
25 | * Default IO end handler for temporary BJ_IO buffer_heads. | 26 | * Default IO end handler for temporary BJ_IO buffer_heads. |
@@ -290,6 +291,7 @@ static inline void write_tag_block(int tag_bytes, journal_block_tag_t *tag, | |||
290 | */ | 291 | */ |
291 | void jbd2_journal_commit_transaction(journal_t *journal) | 292 | void jbd2_journal_commit_transaction(journal_t *journal) |
292 | { | 293 | { |
294 | struct transaction_stats_s stats; | ||
293 | transaction_t *commit_transaction; | 295 | transaction_t *commit_transaction; |
294 | struct journal_head *jh, *new_jh, *descriptor; | 296 | struct journal_head *jh, *new_jh, *descriptor; |
295 | struct buffer_head **wbuf = journal->j_wbuf; | 297 | struct buffer_head **wbuf = journal->j_wbuf; |
@@ -337,6 +339,11 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
337 | spin_lock(&journal->j_state_lock); | 339 | spin_lock(&journal->j_state_lock); |
338 | commit_transaction->t_state = T_LOCKED; | 340 | commit_transaction->t_state = T_LOCKED; |
339 | 341 | ||
342 | stats.u.run.rs_wait = commit_transaction->t_max_wait; | ||
343 | stats.u.run.rs_locked = jiffies; | ||
344 | stats.u.run.rs_running = jbd2_time_diff(commit_transaction->t_start, | ||
345 | stats.u.run.rs_locked); | ||
346 | |||
340 | spin_lock(&commit_transaction->t_handle_lock); | 347 | spin_lock(&commit_transaction->t_handle_lock); |
341 | while (commit_transaction->t_updates) { | 348 | while (commit_transaction->t_updates) { |
342 | DEFINE_WAIT(wait); | 349 | DEFINE_WAIT(wait); |
@@ -407,6 +414,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
407 | */ | 414 | */ |
408 | jbd2_journal_switch_revoke_table(journal); | 415 | jbd2_journal_switch_revoke_table(journal); |
409 | 416 | ||
417 | stats.u.run.rs_flushing = jiffies; | ||
418 | stats.u.run.rs_locked = jbd2_time_diff(stats.u.run.rs_locked, | ||
419 | stats.u.run.rs_flushing); | ||
420 | |||
410 | commit_transaction->t_state = T_FLUSH; | 421 | commit_transaction->t_state = T_FLUSH; |
411 | journal->j_committing_transaction = commit_transaction; | 422 | journal->j_committing_transaction = commit_transaction; |
412 | journal->j_running_transaction = NULL; | 423 | journal->j_running_transaction = NULL; |
@@ -498,6 +509,12 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
498 | */ | 509 | */ |
499 | commit_transaction->t_state = T_COMMIT; | 510 | commit_transaction->t_state = T_COMMIT; |
500 | 511 | ||
512 | stats.u.run.rs_logging = jiffies; | ||
513 | stats.u.run.rs_flushing = jbd2_time_diff(stats.u.run.rs_flushing, | ||
514 | stats.u.run.rs_logging); | ||
515 | stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits; | ||
516 | stats.u.run.rs_blocks_logged = 0; | ||
517 | |||
501 | descriptor = NULL; | 518 | descriptor = NULL; |
502 | bufs = 0; | 519 | bufs = 0; |
503 | while (commit_transaction->t_buffers) { | 520 | while (commit_transaction->t_buffers) { |
@@ -646,6 +663,7 @@ start_journal_io: | |||
646 | submit_bh(WRITE, bh); | 663 | submit_bh(WRITE, bh); |
647 | } | 664 | } |
648 | cond_resched(); | 665 | cond_resched(); |
666 | stats.u.run.rs_blocks_logged += bufs; | ||
649 | 667 | ||
650 | /* Force a new descriptor to be generated next | 668 | /* Force a new descriptor to be generated next |
651 | time round the loop. */ | 669 | time round the loop. */ |
@@ -816,6 +834,7 @@ restart_loop: | |||
816 | cp_transaction = jh->b_cp_transaction; | 834 | cp_transaction = jh->b_cp_transaction; |
817 | if (cp_transaction) { | 835 | if (cp_transaction) { |
818 | JBUFFER_TRACE(jh, "remove from old cp transaction"); | 836 | JBUFFER_TRACE(jh, "remove from old cp transaction"); |
837 | cp_transaction->t_chp_stats.cs_dropped++; | ||
819 | __jbd2_journal_remove_checkpoint(jh); | 838 | __jbd2_journal_remove_checkpoint(jh); |
820 | } | 839 | } |
821 | 840 | ||
@@ -890,6 +909,36 @@ restart_loop: | |||
890 | 909 | ||
891 | J_ASSERT(commit_transaction->t_state == T_COMMIT); | 910 | J_ASSERT(commit_transaction->t_state == T_COMMIT); |
892 | 911 | ||
912 | commit_transaction->t_start = jiffies; | ||
913 | stats.u.run.rs_logging = jbd2_time_diff(stats.u.run.rs_logging, | ||
914 | commit_transaction->t_start); | ||
915 | |||
916 | /* | ||
917 | * File the transaction for history | ||
918 | */ | ||
919 | stats.ts_type = JBD2_STATS_RUN; | ||
920 | stats.ts_tid = commit_transaction->t_tid; | ||
921 | stats.u.run.rs_handle_count = commit_transaction->t_handle_count; | ||
922 | spin_lock(&journal->j_history_lock); | ||
923 | memcpy(journal->j_history + journal->j_history_cur, &stats, | ||
924 | sizeof(stats)); | ||
925 | if (++journal->j_history_cur == journal->j_history_max) | ||
926 | journal->j_history_cur = 0; | ||
927 | |||
928 | /* | ||
929 | * Calculate overall stats | ||
930 | */ | ||
931 | journal->j_stats.ts_tid++; | ||
932 | journal->j_stats.u.run.rs_wait += stats.u.run.rs_wait; | ||
933 | journal->j_stats.u.run.rs_running += stats.u.run.rs_running; | ||
934 | journal->j_stats.u.run.rs_locked += stats.u.run.rs_locked; | ||
935 | journal->j_stats.u.run.rs_flushing += stats.u.run.rs_flushing; | ||
936 | journal->j_stats.u.run.rs_logging += stats.u.run.rs_logging; | ||
937 | journal->j_stats.u.run.rs_handle_count += stats.u.run.rs_handle_count; | ||
938 | journal->j_stats.u.run.rs_blocks += stats.u.run.rs_blocks; | ||
939 | journal->j_stats.u.run.rs_blocks_logged += stats.u.run.rs_blocks_logged; | ||
940 | spin_unlock(&journal->j_history_lock); | ||
941 | |||
893 | commit_transaction->t_state = T_FINISHED; | 942 | commit_transaction->t_state = T_FINISHED; |
894 | J_ASSERT(commit_transaction == journal->j_committing_transaction); | 943 | J_ASSERT(commit_transaction == journal->j_committing_transaction); |
895 | journal->j_commit_sequence = commit_transaction->t_tid; | 944 | journal->j_commit_sequence = commit_transaction->t_tid; |
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 6ddc5531587c..3667c91bc786 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/poison.h> | 36 | #include <linux/poison.h> |
37 | #include <linux/proc_fs.h> | 37 | #include <linux/proc_fs.h> |
38 | #include <linux/debugfs.h> | 38 | #include <linux/debugfs.h> |
39 | #include <linux/seq_file.h> | ||
39 | 40 | ||
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
41 | #include <asm/page.h> | 42 | #include <asm/page.h> |
@@ -640,6 +641,312 @@ struct journal_head *jbd2_journal_get_descriptor_buffer(journal_t *journal) | |||
640 | return jbd2_journal_add_journal_head(bh); | 641 | return jbd2_journal_add_journal_head(bh); |
641 | } | 642 | } |
642 | 643 | ||
644 | struct jbd2_stats_proc_session { | ||
645 | journal_t *journal; | ||
646 | struct transaction_stats_s *stats; | ||
647 | int start; | ||
648 | int max; | ||
649 | }; | ||
650 | |||
651 | static void *jbd2_history_skip_empty(struct jbd2_stats_proc_session *s, | ||
652 | struct transaction_stats_s *ts, | ||
653 | int first) | ||
654 | { | ||
655 | if (ts == s->stats + s->max) | ||
656 | ts = s->stats; | ||
657 | if (!first && ts == s->stats + s->start) | ||
658 | return NULL; | ||
659 | while (ts->ts_type == 0) { | ||
660 | ts++; | ||
661 | if (ts == s->stats + s->max) | ||
662 | ts = s->stats; | ||
663 | if (ts == s->stats + s->start) | ||
664 | return NULL; | ||
665 | } | ||
666 | return ts; | ||
667 | |||
668 | } | ||
669 | |||
670 | static void *jbd2_seq_history_start(struct seq_file *seq, loff_t *pos) | ||
671 | { | ||
672 | struct jbd2_stats_proc_session *s = seq->private; | ||
673 | struct transaction_stats_s *ts; | ||
674 | int l = *pos; | ||
675 | |||
676 | if (l == 0) | ||
677 | return SEQ_START_TOKEN; | ||
678 | ts = jbd2_history_skip_empty(s, s->stats + s->start, 1); | ||
679 | if (!ts) | ||
680 | return NULL; | ||
681 | l--; | ||
682 | while (l) { | ||
683 | ts = jbd2_history_skip_empty(s, ++ts, 0); | ||
684 | if (!ts) | ||
685 | break; | ||
686 | l--; | ||
687 | } | ||
688 | return ts; | ||
689 | } | ||
690 | |||
691 | static void *jbd2_seq_history_next(struct seq_file *seq, void *v, loff_t *pos) | ||
692 | { | ||
693 | struct jbd2_stats_proc_session *s = seq->private; | ||
694 | struct transaction_stats_s *ts = v; | ||
695 | |||
696 | ++*pos; | ||
697 | if (v == SEQ_START_TOKEN) | ||
698 | return jbd2_history_skip_empty(s, s->stats + s->start, 1); | ||
699 | else | ||
700 | return jbd2_history_skip_empty(s, ++ts, 0); | ||
701 | } | ||
702 | |||
703 | static int jbd2_seq_history_show(struct seq_file *seq, void *v) | ||
704 | { | ||
705 | struct transaction_stats_s *ts = v; | ||
706 | if (v == SEQ_START_TOKEN) { | ||
707 | seq_printf(seq, "%-4s %-5s %-5s %-5s %-5s %-5s %-5s %-6s %-5s " | ||
708 | "%-5s %-5s %-5s %-5s %-5s\n", "R/C", "tid", | ||
709 | "wait", "run", "lock", "flush", "log", "hndls", | ||
710 | "block", "inlog", "ctime", "write", "drop", | ||
711 | "close"); | ||
712 | return 0; | ||
713 | } | ||
714 | if (ts->ts_type == JBD2_STATS_RUN) | ||
715 | seq_printf(seq, "%-4s %-5lu %-5u %-5u %-5u %-5u %-5u " | ||
716 | "%-6lu %-5lu %-5lu\n", "R", ts->ts_tid, | ||
717 | jiffies_to_msecs(ts->u.run.rs_wait), | ||
718 | jiffies_to_msecs(ts->u.run.rs_running), | ||
719 | jiffies_to_msecs(ts->u.run.rs_locked), | ||
720 | jiffies_to_msecs(ts->u.run.rs_flushing), | ||
721 | jiffies_to_msecs(ts->u.run.rs_logging), | ||
722 | ts->u.run.rs_handle_count, | ||
723 | ts->u.run.rs_blocks, | ||
724 | ts->u.run.rs_blocks_logged); | ||
725 | else if (ts->ts_type == JBD2_STATS_CHECKPOINT) | ||
726 | seq_printf(seq, "%-4s %-5lu %48s %-5u %-5lu %-5lu %-5lu\n", | ||
727 | "C", ts->ts_tid, " ", | ||
728 | jiffies_to_msecs(ts->u.chp.cs_chp_time), | ||
729 | ts->u.chp.cs_written, ts->u.chp.cs_dropped, | ||
730 | ts->u.chp.cs_forced_to_close); | ||
731 | else | ||
732 | J_ASSERT(0); | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static void jbd2_seq_history_stop(struct seq_file *seq, void *v) | ||
737 | { | ||
738 | } | ||
739 | |||
740 | static struct seq_operations jbd2_seq_history_ops = { | ||
741 | .start = jbd2_seq_history_start, | ||
742 | .next = jbd2_seq_history_next, | ||
743 | .stop = jbd2_seq_history_stop, | ||
744 | .show = jbd2_seq_history_show, | ||
745 | }; | ||
746 | |||
747 | static int jbd2_seq_history_open(struct inode *inode, struct file *file) | ||
748 | { | ||
749 | journal_t *journal = PDE(inode)->data; | ||
750 | struct jbd2_stats_proc_session *s; | ||
751 | int rc, size; | ||
752 | |||
753 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
754 | if (s == NULL) | ||
755 | return -ENOMEM; | ||
756 | size = sizeof(struct transaction_stats_s) * journal->j_history_max; | ||
757 | s->stats = kmalloc(size, GFP_KERNEL); | ||
758 | if (s->stats == NULL) { | ||
759 | kfree(s); | ||
760 | return -ENOMEM; | ||
761 | } | ||
762 | spin_lock(&journal->j_history_lock); | ||
763 | memcpy(s->stats, journal->j_history, size); | ||
764 | s->max = journal->j_history_max; | ||
765 | s->start = journal->j_history_cur % s->max; | ||
766 | spin_unlock(&journal->j_history_lock); | ||
767 | |||
768 | rc = seq_open(file, &jbd2_seq_history_ops); | ||
769 | if (rc == 0) { | ||
770 | struct seq_file *m = file->private_data; | ||
771 | m->private = s; | ||
772 | } else { | ||
773 | kfree(s->stats); | ||
774 | kfree(s); | ||
775 | } | ||
776 | return rc; | ||
777 | |||
778 | } | ||
779 | |||
780 | static int jbd2_seq_history_release(struct inode *inode, struct file *file) | ||
781 | { | ||
782 | struct seq_file *seq = file->private_data; | ||
783 | struct jbd2_stats_proc_session *s = seq->private; | ||
784 | |||
785 | kfree(s->stats); | ||
786 | kfree(s); | ||
787 | return seq_release(inode, file); | ||
788 | } | ||
789 | |||
790 | static struct file_operations jbd2_seq_history_fops = { | ||
791 | .owner = THIS_MODULE, | ||
792 | .open = jbd2_seq_history_open, | ||
793 | .read = seq_read, | ||
794 | .llseek = seq_lseek, | ||
795 | .release = jbd2_seq_history_release, | ||
796 | }; | ||
797 | |||
798 | static void *jbd2_seq_info_start(struct seq_file *seq, loff_t *pos) | ||
799 | { | ||
800 | return *pos ? NULL : SEQ_START_TOKEN; | ||
801 | } | ||
802 | |||
803 | static void *jbd2_seq_info_next(struct seq_file *seq, void *v, loff_t *pos) | ||
804 | { | ||
805 | return NULL; | ||
806 | } | ||
807 | |||
808 | static int jbd2_seq_info_show(struct seq_file *seq, void *v) | ||
809 | { | ||
810 | struct jbd2_stats_proc_session *s = seq->private; | ||
811 | |||
812 | if (v != SEQ_START_TOKEN) | ||
813 | return 0; | ||
814 | seq_printf(seq, "%lu transaction, each upto %u blocks\n", | ||
815 | s->stats->ts_tid, | ||
816 | s->journal->j_max_transaction_buffers); | ||
817 | if (s->stats->ts_tid == 0) | ||
818 | return 0; | ||
819 | seq_printf(seq, "average: \n %ums waiting for transaction\n", | ||
820 | jiffies_to_msecs(s->stats->u.run.rs_wait / s->stats->ts_tid)); | ||
821 | seq_printf(seq, " %ums running transaction\n", | ||
822 | jiffies_to_msecs(s->stats->u.run.rs_running / s->stats->ts_tid)); | ||
823 | seq_printf(seq, " %ums transaction was being locked\n", | ||
824 | jiffies_to_msecs(s->stats->u.run.rs_locked / s->stats->ts_tid)); | ||
825 | seq_printf(seq, " %ums flushing data (in ordered mode)\n", | ||
826 | jiffies_to_msecs(s->stats->u.run.rs_flushing / s->stats->ts_tid)); | ||
827 | seq_printf(seq, " %ums logging transaction\n", | ||
828 | jiffies_to_msecs(s->stats->u.run.rs_logging / s->stats->ts_tid)); | ||
829 | seq_printf(seq, " %lu handles per transaction\n", | ||
830 | s->stats->u.run.rs_handle_count / s->stats->ts_tid); | ||
831 | seq_printf(seq, " %lu blocks per transaction\n", | ||
832 | s->stats->u.run.rs_blocks / s->stats->ts_tid); | ||
833 | seq_printf(seq, " %lu logged blocks per transaction\n", | ||
834 | s->stats->u.run.rs_blocks_logged / s->stats->ts_tid); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static void jbd2_seq_info_stop(struct seq_file *seq, void *v) | ||
839 | { | ||
840 | } | ||
841 | |||
842 | static struct seq_operations jbd2_seq_info_ops = { | ||
843 | .start = jbd2_seq_info_start, | ||
844 | .next = jbd2_seq_info_next, | ||
845 | .stop = jbd2_seq_info_stop, | ||
846 | .show = jbd2_seq_info_show, | ||
847 | }; | ||
848 | |||
849 | static int jbd2_seq_info_open(struct inode *inode, struct file *file) | ||
850 | { | ||
851 | journal_t *journal = PDE(inode)->data; | ||
852 | struct jbd2_stats_proc_session *s; | ||
853 | int rc, size; | ||
854 | |||
855 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
856 | if (s == NULL) | ||
857 | return -ENOMEM; | ||
858 | size = sizeof(struct transaction_stats_s); | ||
859 | s->stats = kmalloc(size, GFP_KERNEL); | ||
860 | if (s->stats == NULL) { | ||
861 | kfree(s); | ||
862 | return -ENOMEM; | ||
863 | } | ||
864 | spin_lock(&journal->j_history_lock); | ||
865 | memcpy(s->stats, &journal->j_stats, size); | ||
866 | s->journal = journal; | ||
867 | spin_unlock(&journal->j_history_lock); | ||
868 | |||
869 | rc = seq_open(file, &jbd2_seq_info_ops); | ||
870 | if (rc == 0) { | ||
871 | struct seq_file *m = file->private_data; | ||
872 | m->private = s; | ||
873 | } else { | ||
874 | kfree(s->stats); | ||
875 | kfree(s); | ||
876 | } | ||
877 | return rc; | ||
878 | |||
879 | } | ||
880 | |||
881 | static int jbd2_seq_info_release(struct inode *inode, struct file *file) | ||
882 | { | ||
883 | struct seq_file *seq = file->private_data; | ||
884 | struct jbd2_stats_proc_session *s = seq->private; | ||
885 | kfree(s->stats); | ||
886 | kfree(s); | ||
887 | return seq_release(inode, file); | ||
888 | } | ||
889 | |||
890 | static struct file_operations jbd2_seq_info_fops = { | ||
891 | .owner = THIS_MODULE, | ||
892 | .open = jbd2_seq_info_open, | ||
893 | .read = seq_read, | ||
894 | .llseek = seq_lseek, | ||
895 | .release = jbd2_seq_info_release, | ||
896 | }; | ||
897 | |||
898 | static struct proc_dir_entry *proc_jbd2_stats; | ||
899 | |||
900 | static void jbd2_stats_proc_init(journal_t *journal) | ||
901 | { | ||
902 | char name[BDEVNAME_SIZE]; | ||
903 | |||
904 | snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name)); | ||
905 | journal->j_proc_entry = proc_mkdir(name, proc_jbd2_stats); | ||
906 | if (journal->j_proc_entry) { | ||
907 | struct proc_dir_entry *p; | ||
908 | p = create_proc_entry("history", S_IRUGO, | ||
909 | journal->j_proc_entry); | ||
910 | if (p) { | ||
911 | p->proc_fops = &jbd2_seq_history_fops; | ||
912 | p->data = journal; | ||
913 | p = create_proc_entry("info", S_IRUGO, | ||
914 | journal->j_proc_entry); | ||
915 | if (p) { | ||
916 | p->proc_fops = &jbd2_seq_info_fops; | ||
917 | p->data = journal; | ||
918 | } | ||
919 | } | ||
920 | } | ||
921 | } | ||
922 | |||
923 | static void jbd2_stats_proc_exit(journal_t *journal) | ||
924 | { | ||
925 | char name[BDEVNAME_SIZE]; | ||
926 | |||
927 | snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name)); | ||
928 | remove_proc_entry("info", journal->j_proc_entry); | ||
929 | remove_proc_entry("history", journal->j_proc_entry); | ||
930 | remove_proc_entry(name, proc_jbd2_stats); | ||
931 | } | ||
932 | |||
933 | static void journal_init_stats(journal_t *journal) | ||
934 | { | ||
935 | int size; | ||
936 | |||
937 | if (!proc_jbd2_stats) | ||
938 | return; | ||
939 | |||
940 | journal->j_history_max = 100; | ||
941 | size = sizeof(struct transaction_stats_s) * journal->j_history_max; | ||
942 | journal->j_history = kzalloc(size, GFP_KERNEL); | ||
943 | if (!journal->j_history) { | ||
944 | journal->j_history_max = 0; | ||
945 | return; | ||
946 | } | ||
947 | spin_lock_init(&journal->j_history_lock); | ||
948 | } | ||
949 | |||
643 | /* | 950 | /* |
644 | * Management for journal control blocks: functions to create and | 951 | * Management for journal control blocks: functions to create and |
645 | * destroy journal_t structures, and to initialise and read existing | 952 | * destroy journal_t structures, and to initialise and read existing |
@@ -681,6 +988,9 @@ static journal_t * journal_init_common (void) | |||
681 | kfree(journal); | 988 | kfree(journal); |
682 | goto fail; | 989 | goto fail; |
683 | } | 990 | } |
991 | |||
992 | journal_init_stats(journal); | ||
993 | |||
684 | return journal; | 994 | return journal; |
685 | fail: | 995 | fail: |
686 | return NULL; | 996 | return NULL; |
@@ -735,6 +1045,7 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, | |||
735 | journal->j_fs_dev = fs_dev; | 1045 | journal->j_fs_dev = fs_dev; |
736 | journal->j_blk_offset = start; | 1046 | journal->j_blk_offset = start; |
737 | journal->j_maxlen = len; | 1047 | journal->j_maxlen = len; |
1048 | jbd2_stats_proc_init(journal); | ||
738 | 1049 | ||
739 | bh = __getblk(journal->j_dev, start, journal->j_blocksize); | 1050 | bh = __getblk(journal->j_dev, start, journal->j_blocksize); |
740 | J_ASSERT(bh != NULL); | 1051 | J_ASSERT(bh != NULL); |
@@ -773,6 +1084,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) | |||
773 | 1084 | ||
774 | journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits; | 1085 | journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits; |
775 | journal->j_blocksize = inode->i_sb->s_blocksize; | 1086 | journal->j_blocksize = inode->i_sb->s_blocksize; |
1087 | jbd2_stats_proc_init(journal); | ||
776 | 1088 | ||
777 | /* journal descriptor can store up to n blocks -bzzz */ | 1089 | /* journal descriptor can store up to n blocks -bzzz */ |
778 | n = journal->j_blocksize / sizeof(journal_block_tag_t); | 1090 | n = journal->j_blocksize / sizeof(journal_block_tag_t); |
@@ -1153,6 +1465,8 @@ void jbd2_journal_destroy(journal_t *journal) | |||
1153 | brelse(journal->j_sb_buffer); | 1465 | brelse(journal->j_sb_buffer); |
1154 | } | 1466 | } |
1155 | 1467 | ||
1468 | if (journal->j_proc_entry) | ||
1469 | jbd2_stats_proc_exit(journal); | ||
1156 | if (journal->j_inode) | 1470 | if (journal->j_inode) |
1157 | iput(journal->j_inode); | 1471 | iput(journal->j_inode); |
1158 | if (journal->j_revoke) | 1472 | if (journal->j_revoke) |
@@ -1900,6 +2214,28 @@ static void __exit jbd2_remove_debugfs_entry(void) | |||
1900 | 2214 | ||
1901 | #endif | 2215 | #endif |
1902 | 2216 | ||
2217 | #ifdef CONFIG_PROC_FS | ||
2218 | |||
2219 | #define JBD2_STATS_PROC_NAME "fs/jbd2" | ||
2220 | |||
2221 | static void __init jbd2_create_jbd_stats_proc_entry(void) | ||
2222 | { | ||
2223 | proc_jbd2_stats = proc_mkdir(JBD2_STATS_PROC_NAME, NULL); | ||
2224 | } | ||
2225 | |||
2226 | static void __exit jbd2_remove_jbd_stats_proc_entry(void) | ||
2227 | { | ||
2228 | if (proc_jbd2_stats) | ||
2229 | remove_proc_entry(JBD2_STATS_PROC_NAME, NULL); | ||
2230 | } | ||
2231 | |||
2232 | #else | ||
2233 | |||
2234 | #define jbd2_create_jbd_stats_proc_entry() do {} while (0) | ||
2235 | #define jbd2_remove_jbd_stats_proc_entry() do {} while (0) | ||
2236 | |||
2237 | #endif | ||
2238 | |||
1903 | struct kmem_cache *jbd2_handle_cache; | 2239 | struct kmem_cache *jbd2_handle_cache; |
1904 | 2240 | ||
1905 | static int __init journal_init_handle_cache(void) | 2241 | static int __init journal_init_handle_cache(void) |
@@ -1955,6 +2291,7 @@ static int __init journal_init(void) | |||
1955 | if (ret != 0) | 2291 | if (ret != 0) |
1956 | jbd2_journal_destroy_caches(); | 2292 | jbd2_journal_destroy_caches(); |
1957 | jbd2_create_debugfs_entry(); | 2293 | jbd2_create_debugfs_entry(); |
2294 | jbd2_create_jbd_stats_proc_entry(); | ||
1958 | return ret; | 2295 | return ret; |
1959 | } | 2296 | } |
1960 | 2297 | ||
@@ -1966,6 +2303,7 @@ static void __exit journal_exit(void) | |||
1966 | printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n); | 2303 | printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n); |
1967 | #endif | 2304 | #endif |
1968 | jbd2_remove_debugfs_entry(); | 2305 | jbd2_remove_debugfs_entry(); |
2306 | jbd2_remove_jbd_stats_proc_entry(); | ||
1969 | jbd2_journal_destroy_caches(); | 2307 | jbd2_journal_destroy_caches(); |
1970 | } | 2308 | } |
1971 | 2309 | ||
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index b1fcf2b3dca3..f30802aeefae 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c | |||
@@ -59,6 +59,8 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) | |||
59 | 59 | ||
60 | J_ASSERT(journal->j_running_transaction == NULL); | 60 | J_ASSERT(journal->j_running_transaction == NULL); |
61 | journal->j_running_transaction = transaction; | 61 | journal->j_running_transaction = transaction; |
62 | transaction->t_max_wait = 0; | ||
63 | transaction->t_start = jiffies; | ||
62 | 64 | ||
63 | return transaction; | 65 | return transaction; |
64 | } | 66 | } |
@@ -85,6 +87,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle) | |||
85 | int nblocks = handle->h_buffer_credits; | 87 | int nblocks = handle->h_buffer_credits; |
86 | transaction_t *new_transaction = NULL; | 88 | transaction_t *new_transaction = NULL; |
87 | int ret = 0; | 89 | int ret = 0; |
90 | unsigned long ts = jiffies; | ||
88 | 91 | ||
89 | if (nblocks > journal->j_max_transaction_buffers) { | 92 | if (nblocks > journal->j_max_transaction_buffers) { |
90 | printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", | 93 | printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", |
@@ -217,6 +220,12 @@ repeat_locked: | |||
217 | /* OK, account for the buffers that this operation expects to | 220 | /* OK, account for the buffers that this operation expects to |
218 | * use and add the handle to the running transaction. */ | 221 | * use and add the handle to the running transaction. */ |
219 | 222 | ||
223 | if (time_after(transaction->t_start, ts)) { | ||
224 | ts = jbd2_time_diff(ts, transaction->t_start); | ||
225 | if (ts > transaction->t_max_wait) | ||
226 | transaction->t_max_wait = ts; | ||
227 | } | ||
228 | |||
220 | handle->h_transaction = transaction; | 229 | handle->h_transaction = transaction; |
221 | transaction->t_outstanding_credits += nblocks; | 230 | transaction->t_outstanding_credits += nblocks; |
222 | transaction->t_updates++; | 231 | transaction->t_updates++; |
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index d861ffd49821..685640036e81 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h | |||
@@ -395,6 +395,16 @@ struct handle_s | |||
395 | }; | 395 | }; |
396 | 396 | ||
397 | 397 | ||
398 | /* | ||
399 | * Some stats for checkpoint phase | ||
400 | */ | ||
401 | struct transaction_chp_stats_s { | ||
402 | unsigned long cs_chp_time; | ||
403 | unsigned long cs_forced_to_close; | ||
404 | unsigned long cs_written; | ||
405 | unsigned long cs_dropped; | ||
406 | }; | ||
407 | |||
398 | /* The transaction_t type is the guts of the journaling mechanism. It | 408 | /* The transaction_t type is the guts of the journaling mechanism. It |
399 | * tracks a compound transaction through its various states: | 409 | * tracks a compound transaction through its various states: |
400 | * | 410 | * |
@@ -532,6 +542,21 @@ struct transaction_s | |||
532 | spinlock_t t_handle_lock; | 542 | spinlock_t t_handle_lock; |
533 | 543 | ||
534 | /* | 544 | /* |
545 | * Longest time some handle had to wait for running transaction | ||
546 | */ | ||
547 | unsigned long t_max_wait; | ||
548 | |||
549 | /* | ||
550 | * When transaction started | ||
551 | */ | ||
552 | unsigned long t_start; | ||
553 | |||
554 | /* | ||
555 | * Checkpointing stats [j_checkpoint_sem] | ||
556 | */ | ||
557 | struct transaction_chp_stats_s t_chp_stats; | ||
558 | |||
559 | /* | ||
535 | * Number of outstanding updates running on this transaction | 560 | * Number of outstanding updates running on this transaction |
536 | * [t_handle_lock] | 561 | * [t_handle_lock] |
537 | */ | 562 | */ |
@@ -562,6 +587,39 @@ struct transaction_s | |||
562 | 587 | ||
563 | }; | 588 | }; |
564 | 589 | ||
590 | struct transaction_run_stats_s { | ||
591 | unsigned long rs_wait; | ||
592 | unsigned long rs_running; | ||
593 | unsigned long rs_locked; | ||
594 | unsigned long rs_flushing; | ||
595 | unsigned long rs_logging; | ||
596 | |||
597 | unsigned long rs_handle_count; | ||
598 | unsigned long rs_blocks; | ||
599 | unsigned long rs_blocks_logged; | ||
600 | }; | ||
601 | |||
602 | struct transaction_stats_s { | ||
603 | int ts_type; | ||
604 | unsigned long ts_tid; | ||
605 | union { | ||
606 | struct transaction_run_stats_s run; | ||
607 | struct transaction_chp_stats_s chp; | ||
608 | } u; | ||
609 | }; | ||
610 | |||
611 | #define JBD2_STATS_RUN 1 | ||
612 | #define JBD2_STATS_CHECKPOINT 2 | ||
613 | |||
614 | static inline unsigned long | ||
615 | jbd2_time_diff(unsigned long start, unsigned long end) | ||
616 | { | ||
617 | if (end >= start) | ||
618 | return end - start; | ||
619 | |||
620 | return end + (MAX_JIFFY_OFFSET - start); | ||
621 | } | ||
622 | |||
565 | /** | 623 | /** |
566 | * struct journal_s - The journal_s type is the concrete type associated with | 624 | * struct journal_s - The journal_s type is the concrete type associated with |
567 | * journal_t. | 625 | * journal_t. |
@@ -623,6 +681,12 @@ struct transaction_s | |||
623 | * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the | 681 | * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the |
624 | * number that will fit in j_blocksize | 682 | * number that will fit in j_blocksize |
625 | * @j_last_sync_writer: most recent pid which did a synchronous write | 683 | * @j_last_sync_writer: most recent pid which did a synchronous write |
684 | * @j_history: Buffer storing the transactions statistics history | ||
685 | * @j_history_max: Maximum number of transactions in the statistics history | ||
686 | * @j_history_cur: Current number of transactions in the statistics history | ||
687 | * @j_history_lock: Protect the transactions statistics history | ||
688 | * @j_proc_entry: procfs entry for the jbd statistics directory | ||
689 | * @j_stats: Overall statistics | ||
626 | * @j_private: An opaque pointer to fs-private information. | 690 | * @j_private: An opaque pointer to fs-private information. |
627 | */ | 691 | */ |
628 | 692 | ||
@@ -815,6 +879,19 @@ struct journal_s | |||
815 | pid_t j_last_sync_writer; | 879 | pid_t j_last_sync_writer; |
816 | 880 | ||
817 | /* | 881 | /* |
882 | * Journal statistics | ||
883 | */ | ||
884 | struct transaction_stats_s *j_history; | ||
885 | int j_history_max; | ||
886 | int j_history_cur; | ||
887 | /* | ||
888 | * Protect the transactions statistics history | ||
889 | */ | ||
890 | spinlock_t j_history_lock; | ||
891 | struct proc_dir_entry *j_proc_entry; | ||
892 | struct transaction_stats_s j_stats; | ||
893 | |||
894 | /* | ||
818 | * An opaque pointer to fs-private information. ext3 puts its | 895 | * An opaque pointer to fs-private information. ext3 puts its |
819 | * superblock pointer here | 896 | * superblock pointer here |
820 | */ | 897 | */ |