aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/jbd2/checkpoint.c10
-rw-r--r--fs/jbd2/commit.c49
-rw-r--r--fs/jbd2/journal.c338
-rw-r--r--fs/jbd2/transaction.c9
-rw-r--r--include/linux/jbd2.h77
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 */
234static int __process_buffer(journal_t *journal, struct journal_head *jh, 234static 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;
326restart: 331restart:
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 */
291void jbd2_journal_commit_transaction(journal_t *journal) 292void 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
644struct jbd2_stats_proc_session {
645 journal_t *journal;
646 struct transaction_stats_s *stats;
647 int start;
648 int max;
649};
650
651static 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
670static 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
691static 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
703static 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
736static void jbd2_seq_history_stop(struct seq_file *seq, void *v)
737{
738}
739
740static 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
747static 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
780static 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
790static 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
798static void *jbd2_seq_info_start(struct seq_file *seq, loff_t *pos)
799{
800 return *pos ? NULL : SEQ_START_TOKEN;
801}
802
803static void *jbd2_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
804{
805 return NULL;
806}
807
808static 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
838static void jbd2_seq_info_stop(struct seq_file *seq, void *v)
839{
840}
841
842static 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
849static 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
881static 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
890static 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
898static struct proc_dir_entry *proc_jbd2_stats;
899
900static 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
923static 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
933static 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;
685fail: 995fail:
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
2221static void __init jbd2_create_jbd_stats_proc_entry(void)
2222{
2223 proc_jbd2_stats = proc_mkdir(JBD2_STATS_PROC_NAME, NULL);
2224}
2225
2226static 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
1903struct kmem_cache *jbd2_handle_cache; 2239struct kmem_cache *jbd2_handle_cache;
1904 2240
1905static int __init journal_init_handle_cache(void) 2241static 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 */
401struct 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
590struct 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
602struct 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
614static inline unsigned long
615jbd2_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 */