1 jbd2: jbd2 stats through procfs
3 From: Johann Lombardi <johann.lombardi@bull.net>
5 The patch below updates the jbd stats patch to 2.6.20/jbd2.
6 The initial patch was posted by Alex Tomas in December 2005
7 (http://marc.info/?l=linux-ext4&m=113538565128617&w=2).
8 It provides statistics via procfs such as transaction lifetime and size.
10 Sometimes, investigating performance problems, i find useful to have
11 stats from jbd about transaction's lifetime, size, etc. here is a
12 patch for review and inclusion probably.
14 for example, stats after creation of 3M files in htree directory:
16 [root@bob ~]# cat /proc/fs/jbd/sda/history
17 R/C tid wait run lock flush log hndls block inlog ctime write drop close
18 R 261 8260 2720 0 0 750 9892 8170 8187
20 R 262 20 2200 10 0 770 9836 8170 8187
21 R 263 30 2200 10 0 3070 9812 8170 8187
22 R 264 0 5000 10 0 1340 0 0 0
23 C 261 8240 3212 4957 0
24 R 265 8260 1470 0 0 4640 9854 8170 8187
25 R 266 0 5000 10 0 1460 0 0 0
26 C 262 8210 2989 4868 0
27 R 267 8230 1490 10 0 4440 9875 8171 8188
28 R 268 0 5000 10 0 1260 0 0 0
29 C 263 7710 2937 4908 0
30 R 269 7730 1470 10 0 3330 9841 8170 8187
31 R 270 0 5000 10 0 830 0 0 0
32 C 265 8140 3234 4898 0
34 R 271 8630 2740 20 0 740 9819 8170 8187
36 R 272 40 2170 10 0 830 9716 8170 8187
37 R 273 40 2280 0 0 3530 9799 8170 8187
38 R 274 0 5000 10 0 990 0 0 0
43 R - line for transaction's life from T_RUNNING to T_FINISHED
44 C - line for transaction's checkpointing
45 tid - transaction's id
46 wait - for how long we were waiting for new transaction to start
47 (the longest period journal_start() took in this transaction)
48 run - real transaction's lifetime (from T_RUNNING to T_LOCKED
49 lock - how long we were waiting for all handles to close
50 (time the transaction was in T_LOCKED)
51 flush - how long it took to flush all data (data=ordered)
52 log - how long it took to write the transaction to the log
53 hndls - how many handles got to the transaction
54 block - how many blocks got to the transaction
55 inlog - how many blocks are written to the log (block + descriptors)
56 ctime - how long it took to checkpoint the transaction
57 write - how many blocks have been written during checkpointing
58 drop - how many blocks have been dropped during checkpointing
59 close - how many running transactions have been closed to checkpoint this one
61 all times are in msec.
64 [root@bob ~]# cat /proc/fs/jbd/sda/info
65 280 transaction, each upto 8192 blocks
67 1633ms waiting for transaction
68 3616ms running transaction
69 5ms transaction was being locked
70 1ms flushing data (in ordered mode)
71 1799ms logging transaction
72 11781 handles per transaction
73 5629 blocks per transaction
74 5641 logged blocks per transaction
76 Signed-off-by: Johann Lombardi <johann.lombardi@bull.net>
77 Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl>
78 Signed-off-by: Mingming Cao <cmm@us.ibm.com>
79 Signed-off-by: Eric Sandeen <sandeen@redhat.com>
82 fs/jbd2/checkpoint.c | 10 +
83 fs/jbd2/commit.c | 49 +++++++
84 fs/jbd2/journal.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++
85 fs/jbd2/transaction.c | 9 +
86 include/linux/jbd2.h | 77 +++++++++++
87 5 files changed, 481 insertions(+), 2 deletions(-)
90 diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
91 index 7e958c8..1b7f282 100644
92 --- a/fs/jbd2/checkpoint.c
93 +++ b/fs/jbd2/checkpoint.c
94 @@ -232,7 +232,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count)
95 * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it
97 static int __process_buffer(journal_t *journal, struct journal_head *jh,
98 - struct buffer_head **bhs, int *batch_count)
99 + struct buffer_head **bhs, int *batch_count,
100 + transaction_t *transaction)
102 struct buffer_head *bh = jh2bh(jh);
104 @@ -250,6 +251,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
105 transaction_t *t = jh->b_transaction;
106 tid_t tid = t->t_tid;
108 + transaction->t_chp_stats.cs_forced_to_close++;
109 spin_unlock(&journal->j_list_lock);
110 jbd_unlock_bh_state(bh);
111 jbd2_log_start_commit(journal, tid);
112 @@ -279,6 +281,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
113 bhs[*batch_count] = bh;
114 __buffer_relink_io(jh);
115 jbd_unlock_bh_state(bh);
116 + transaction->t_chp_stats.cs_written++;
118 if (*batch_count == NR_BATCH) {
119 spin_unlock(&journal->j_list_lock);
120 @@ -322,6 +325,8 @@ int jbd2_log_do_checkpoint(journal_t *journal)
121 if (!journal->j_checkpoint_transactions)
123 transaction = journal->j_checkpoint_transactions;
124 + if (transaction->t_chp_stats.cs_chp_time == 0)
125 + transaction->t_chp_stats.cs_chp_time = jiffies;
126 this_tid = transaction->t_tid;
129 @@ -346,7 +351,8 @@ restart:
133 - retry = __process_buffer(journal, jh, bhs,&batch_count);
134 + retry = __process_buffer(journal, jh, bhs, &batch_count,
136 if (!retry && lock_need_resched(&journal->j_list_lock)){
137 spin_unlock(&journal->j_list_lock);
139 diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
140 index 39b5cee..8749a86 100644
141 --- a/fs/jbd2/commit.c
142 +++ b/fs/jbd2/commit.c
144 #include <linux/slab.h>
145 #include <linux/mm.h>
146 #include <linux/pagemap.h>
147 +#include <linux/jiffies.h>
150 * Default IO end handler for temporary BJ_IO buffer_heads.
151 @@ -290,6 +291,7 @@ static inline void write_tag_block(int tag_bytes, journal_block_tag_t *tag,
153 void jbd2_journal_commit_transaction(journal_t *journal)
155 + struct transaction_stats_s stats;
156 transaction_t *commit_transaction;
157 struct journal_head *jh, *new_jh, *descriptor;
158 struct buffer_head **wbuf = journal->j_wbuf;
159 @@ -337,6 +339,11 @@ void jbd2_journal_commit_transaction(journal_t *journal)
160 spin_lock(&journal->j_state_lock);
161 commit_transaction->t_state = T_LOCKED;
163 + stats.u.run.rs_wait = commit_transaction->t_max_wait;
164 + stats.u.run.rs_locked = jiffies;
165 + stats.u.run.rs_running = jbd2_time_diff(commit_transaction->t_start,
166 + stats.u.run.rs_locked);
168 spin_lock(&commit_transaction->t_handle_lock);
169 while (commit_transaction->t_updates) {
171 @@ -407,6 +414,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
173 jbd2_journal_switch_revoke_table(journal);
175 + stats.u.run.rs_flushing = jiffies;
176 + stats.u.run.rs_locked = jbd2_time_diff(stats.u.run.rs_locked,
177 + stats.u.run.rs_flushing);
179 commit_transaction->t_state = T_FLUSH;
180 journal->j_committing_transaction = commit_transaction;
181 journal->j_running_transaction = NULL;
182 @@ -498,6 +509,12 @@ void jbd2_journal_commit_transaction(journal_t *journal)
184 commit_transaction->t_state = T_COMMIT;
186 + stats.u.run.rs_logging = jiffies;
187 + stats.u.run.rs_flushing = jbd2_time_diff(stats.u.run.rs_flushing,
188 + stats.u.run.rs_logging);
189 + stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits;
190 + stats.u.run.rs_blocks_logged = 0;
194 while (commit_transaction->t_buffers) {
195 @@ -646,6 +663,7 @@ start_journal_io:
196 submit_bh(WRITE, bh);
199 + stats.u.run.rs_blocks_logged += bufs;
201 /* Force a new descriptor to be generated next
202 time round the loop. */
203 @@ -816,6 +834,7 @@ restart_loop:
204 cp_transaction = jh->b_cp_transaction;
205 if (cp_transaction) {
206 JBUFFER_TRACE(jh, "remove from old cp transaction");
207 + cp_transaction->t_chp_stats.cs_dropped++;
208 __jbd2_journal_remove_checkpoint(jh);
211 @@ -890,6 +909,36 @@ restart_loop:
213 J_ASSERT(commit_transaction->t_state == T_COMMIT);
215 + commit_transaction->t_start = jiffies;
216 + stats.u.run.rs_logging = jbd2_time_diff(stats.u.run.rs_logging,
217 + commit_transaction->t_start);
220 + * File the transaction for history
222 + stats.ts_type = JBD2_STATS_RUN;
223 + stats.ts_tid = commit_transaction->t_tid;
224 + stats.u.run.rs_handle_count = commit_transaction->t_handle_count;
225 + spin_lock(&journal->j_history_lock);
226 + memcpy(journal->j_history + journal->j_history_cur, &stats,
228 + if (++journal->j_history_cur == journal->j_history_max)
229 + journal->j_history_cur = 0;
232 + * Calculate overall stats
234 + journal->j_stats.ts_tid++;
235 + journal->j_stats.u.run.rs_wait += stats.u.run.rs_wait;
236 + journal->j_stats.u.run.rs_running += stats.u.run.rs_running;
237 + journal->j_stats.u.run.rs_locked += stats.u.run.rs_locked;
238 + journal->j_stats.u.run.rs_flushing += stats.u.run.rs_flushing;
239 + journal->j_stats.u.run.rs_logging += stats.u.run.rs_logging;
240 + journal->j_stats.u.run.rs_handle_count += stats.u.run.rs_handle_count;
241 + journal->j_stats.u.run.rs_blocks += stats.u.run.rs_blocks;
242 + journal->j_stats.u.run.rs_blocks_logged += stats.u.run.rs_blocks_logged;
243 + spin_unlock(&journal->j_history_lock);
245 commit_transaction->t_state = T_FINISHED;
246 J_ASSERT(commit_transaction == journal->j_committing_transaction);
247 journal->j_commit_sequence = commit_transaction->t_tid;
248 diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
249 index 6ddc553..3667c91 100644
250 --- a/fs/jbd2/journal.c
251 +++ b/fs/jbd2/journal.c
253 #include <linux/poison.h>
254 #include <linux/proc_fs.h>
255 #include <linux/debugfs.h>
256 +#include <linux/seq_file.h>
258 #include <asm/uaccess.h>
259 #include <asm/page.h>
260 @@ -640,6 +641,312 @@ struct journal_head *jbd2_journal_get_descriptor_buffer(journal_t *journal)
261 return jbd2_journal_add_journal_head(bh);
264 +struct jbd2_stats_proc_session {
265 + journal_t *journal;
266 + struct transaction_stats_s *stats;
271 +static void *jbd2_history_skip_empty(struct jbd2_stats_proc_session *s,
272 + struct transaction_stats_s *ts,
275 + if (ts == s->stats + s->max)
277 + if (!first && ts == s->stats + s->start)
279 + while (ts->ts_type == 0) {
281 + if (ts == s->stats + s->max)
283 + if (ts == s->stats + s->start)
290 +static void *jbd2_seq_history_start(struct seq_file *seq, loff_t *pos)
292 + struct jbd2_stats_proc_session *s = seq->private;
293 + struct transaction_stats_s *ts;
297 + return SEQ_START_TOKEN;
298 + ts = jbd2_history_skip_empty(s, s->stats + s->start, 1);
303 + ts = jbd2_history_skip_empty(s, ++ts, 0);
311 +static void *jbd2_seq_history_next(struct seq_file *seq, void *v, loff_t *pos)
313 + struct jbd2_stats_proc_session *s = seq->private;
314 + struct transaction_stats_s *ts = v;
317 + if (v == SEQ_START_TOKEN)
318 + return jbd2_history_skip_empty(s, s->stats + s->start, 1);
320 + return jbd2_history_skip_empty(s, ++ts, 0);
323 +static int jbd2_seq_history_show(struct seq_file *seq, void *v)
325 + struct transaction_stats_s *ts = v;
326 + if (v == SEQ_START_TOKEN) {
327 + seq_printf(seq, "%-4s %-5s %-5s %-5s %-5s %-5s %-5s %-6s %-5s "
328 + "%-5s %-5s %-5s %-5s %-5s\n", "R/C", "tid",
329 + "wait", "run", "lock", "flush", "log", "hndls",
330 + "block", "inlog", "ctime", "write", "drop",
334 + if (ts->ts_type == JBD2_STATS_RUN)
335 + seq_printf(seq, "%-4s %-5lu %-5u %-5u %-5u %-5u %-5u "
336 + "%-6lu %-5lu %-5lu\n", "R", ts->ts_tid,
337 + jiffies_to_msecs(ts->u.run.rs_wait),
338 + jiffies_to_msecs(ts->u.run.rs_running),
339 + jiffies_to_msecs(ts->u.run.rs_locked),
340 + jiffies_to_msecs(ts->u.run.rs_flushing),
341 + jiffies_to_msecs(ts->u.run.rs_logging),
342 + ts->u.run.rs_handle_count,
343 + ts->u.run.rs_blocks,
344 + ts->u.run.rs_blocks_logged);
345 + else if (ts->ts_type == JBD2_STATS_CHECKPOINT)
346 + seq_printf(seq, "%-4s %-5lu %48s %-5u %-5lu %-5lu %-5lu\n",
347 + "C", ts->ts_tid, " ",
348 + jiffies_to_msecs(ts->u.chp.cs_chp_time),
349 + ts->u.chp.cs_written, ts->u.chp.cs_dropped,
350 + ts->u.chp.cs_forced_to_close);
356 +static void jbd2_seq_history_stop(struct seq_file *seq, void *v)
360 +static struct seq_operations jbd2_seq_history_ops = {
361 + .start = jbd2_seq_history_start,
362 + .next = jbd2_seq_history_next,
363 + .stop = jbd2_seq_history_stop,
364 + .show = jbd2_seq_history_show,
367 +static int jbd2_seq_history_open(struct inode *inode, struct file *file)
369 + journal_t *journal = PDE(inode)->data;
370 + struct jbd2_stats_proc_session *s;
373 + s = kmalloc(sizeof(*s), GFP_KERNEL);
376 + size = sizeof(struct transaction_stats_s) * journal->j_history_max;
377 + s->stats = kmalloc(size, GFP_KERNEL);
378 + if (s->stats == NULL) {
382 + spin_lock(&journal->j_history_lock);
383 + memcpy(s->stats, journal->j_history, size);
384 + s->max = journal->j_history_max;
385 + s->start = journal->j_history_cur % s->max;
386 + spin_unlock(&journal->j_history_lock);
388 + rc = seq_open(file, &jbd2_seq_history_ops);
390 + struct seq_file *m = file->private_data;
400 +static int jbd2_seq_history_release(struct inode *inode, struct file *file)
402 + struct seq_file *seq = file->private_data;
403 + struct jbd2_stats_proc_session *s = seq->private;
407 + return seq_release(inode, file);
410 +static struct file_operations jbd2_seq_history_fops = {
411 + .owner = THIS_MODULE,
412 + .open = jbd2_seq_history_open,
414 + .llseek = seq_lseek,
415 + .release = jbd2_seq_history_release,
418 +static void *jbd2_seq_info_start(struct seq_file *seq, loff_t *pos)
420 + return *pos ? NULL : SEQ_START_TOKEN;
423 +static void *jbd2_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
428 +static int jbd2_seq_info_show(struct seq_file *seq, void *v)
430 + struct jbd2_stats_proc_session *s = seq->private;
432 + if (v != SEQ_START_TOKEN)
434 + seq_printf(seq, "%lu transaction, each upto %u blocks\n",
436 + s->journal->j_max_transaction_buffers);
437 + if (s->stats->ts_tid == 0)
439 + seq_printf(seq, "average: \n %ums waiting for transaction\n",
440 + jiffies_to_msecs(s->stats->u.run.rs_wait / s->stats->ts_tid));
441 + seq_printf(seq, " %ums running transaction\n",
442 + jiffies_to_msecs(s->stats->u.run.rs_running / s->stats->ts_tid));
443 + seq_printf(seq, " %ums transaction was being locked\n",
444 + jiffies_to_msecs(s->stats->u.run.rs_locked / s->stats->ts_tid));
445 + seq_printf(seq, " %ums flushing data (in ordered mode)\n",
446 + jiffies_to_msecs(s->stats->u.run.rs_flushing / s->stats->ts_tid));
447 + seq_printf(seq, " %ums logging transaction\n",
448 + jiffies_to_msecs(s->stats->u.run.rs_logging / s->stats->ts_tid));
449 + seq_printf(seq, " %lu handles per transaction\n",
450 + s->stats->u.run.rs_handle_count / s->stats->ts_tid);
451 + seq_printf(seq, " %lu blocks per transaction\n",
452 + s->stats->u.run.rs_blocks / s->stats->ts_tid);
453 + seq_printf(seq, " %lu logged blocks per transaction\n",
454 + s->stats->u.run.rs_blocks_logged / s->stats->ts_tid);
458 +static void jbd2_seq_info_stop(struct seq_file *seq, void *v)
462 +static struct seq_operations jbd2_seq_info_ops = {
463 + .start = jbd2_seq_info_start,
464 + .next = jbd2_seq_info_next,
465 + .stop = jbd2_seq_info_stop,
466 + .show = jbd2_seq_info_show,
469 +static int jbd2_seq_info_open(struct inode *inode, struct file *file)
471 + journal_t *journal = PDE(inode)->data;
472 + struct jbd2_stats_proc_session *s;
475 + s = kmalloc(sizeof(*s), GFP_KERNEL);
478 + size = sizeof(struct transaction_stats_s);
479 + s->stats = kmalloc(size, GFP_KERNEL);
480 + if (s->stats == NULL) {
484 + spin_lock(&journal->j_history_lock);
485 + memcpy(s->stats, &journal->j_stats, size);
486 + s->journal = journal;
487 + spin_unlock(&journal->j_history_lock);
489 + rc = seq_open(file, &jbd2_seq_info_ops);
491 + struct seq_file *m = file->private_data;
501 +static int jbd2_seq_info_release(struct inode *inode, struct file *file)
503 + struct seq_file *seq = file->private_data;
504 + struct jbd2_stats_proc_session *s = seq->private;
507 + return seq_release(inode, file);
510 +static struct file_operations jbd2_seq_info_fops = {
511 + .owner = THIS_MODULE,
512 + .open = jbd2_seq_info_open,
514 + .llseek = seq_lseek,
515 + .release = jbd2_seq_info_release,
518 +static struct proc_dir_entry *proc_jbd2_stats;
520 +static void jbd2_stats_proc_init(journal_t *journal)
522 + char name[BDEVNAME_SIZE];
524 + snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
525 + journal->j_proc_entry = proc_mkdir(name, proc_jbd2_stats);
526 + if (journal->j_proc_entry) {
527 + struct proc_dir_entry *p;
528 + p = create_proc_entry("history", S_IRUGO,
529 + journal->j_proc_entry);
531 + p->proc_fops = &jbd2_seq_history_fops;
533 + p = create_proc_entry("info", S_IRUGO,
534 + journal->j_proc_entry);
536 + p->proc_fops = &jbd2_seq_info_fops;
543 +static void jbd2_stats_proc_exit(journal_t *journal)
545 + char name[BDEVNAME_SIZE];
547 + snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
548 + remove_proc_entry("info", journal->j_proc_entry);
549 + remove_proc_entry("history", journal->j_proc_entry);
550 + remove_proc_entry(name, proc_jbd2_stats);
553 +static void journal_init_stats(journal_t *journal)
557 + if (!proc_jbd2_stats)
560 + journal->j_history_max = 100;
561 + size = sizeof(struct transaction_stats_s) * journal->j_history_max;
562 + journal->j_history = kzalloc(size, GFP_KERNEL);
563 + if (!journal->j_history) {
564 + journal->j_history_max = 0;
567 + spin_lock_init(&journal->j_history_lock);
571 * Management for journal control blocks: functions to create and
572 * destroy journal_t structures, and to initialise and read existing
573 @@ -681,6 +988,9 @@ static journal_t * journal_init_common (void)
578 + journal_init_stats(journal);
583 @@ -735,6 +1045,7 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev,
584 journal->j_fs_dev = fs_dev;
585 journal->j_blk_offset = start;
586 journal->j_maxlen = len;
587 + jbd2_stats_proc_init(journal);
589 bh = __getblk(journal->j_dev, start, journal->j_blocksize);
590 J_ASSERT(bh != NULL);
591 @@ -773,6 +1084,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode)
593 journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
594 journal->j_blocksize = inode->i_sb->s_blocksize;
595 + jbd2_stats_proc_init(journal);
597 /* journal descriptor can store up to n blocks -bzzz */
598 n = journal->j_blocksize / sizeof(journal_block_tag_t);
599 @@ -1153,6 +1465,8 @@ void jbd2_journal_destroy(journal_t *journal)
600 brelse(journal->j_sb_buffer);
603 + if (journal->j_proc_entry)
604 + jbd2_stats_proc_exit(journal);
605 if (journal->j_inode)
606 iput(journal->j_inode);
607 if (journal->j_revoke)
608 @@ -1900,6 +2214,28 @@ static void __exit jbd2_remove_debugfs_entry(void)
612 +#ifdef CONFIG_PROC_FS
614 +#define JBD2_STATS_PROC_NAME "fs/jbd2"
616 +static void __init jbd2_create_jbd_stats_proc_entry(void)
618 + proc_jbd2_stats = proc_mkdir(JBD2_STATS_PROC_NAME, NULL);
621 +static void __exit jbd2_remove_jbd_stats_proc_entry(void)
623 + if (proc_jbd2_stats)
624 + remove_proc_entry(JBD2_STATS_PROC_NAME, NULL);
629 +#define jbd2_create_jbd_stats_proc_entry() do {} while (0)
630 +#define jbd2_remove_jbd_stats_proc_entry() do {} while (0)
634 struct kmem_cache *jbd2_handle_cache;
636 static int __init journal_init_handle_cache(void)
637 @@ -1955,6 +2291,7 @@ static int __init journal_init(void)
639 jbd2_journal_destroy_caches();
640 jbd2_create_debugfs_entry();
641 + jbd2_create_jbd_stats_proc_entry();
645 @@ -1966,6 +2303,7 @@ static void __exit journal_exit(void)
646 printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
648 jbd2_remove_debugfs_entry();
649 + jbd2_remove_jbd_stats_proc_entry();
650 jbd2_journal_destroy_caches();
653 diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
654 index b1fcf2b..f30802a 100644
655 --- a/fs/jbd2/transaction.c
656 +++ b/fs/jbd2/transaction.c
657 @@ -59,6 +59,8 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction)
659 J_ASSERT(journal->j_running_transaction == NULL);
660 journal->j_running_transaction = transaction;
661 + transaction->t_max_wait = 0;
662 + transaction->t_start = jiffies;
666 @@ -85,6 +87,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle)
667 int nblocks = handle->h_buffer_credits;
668 transaction_t *new_transaction = NULL;
670 + unsigned long ts = jiffies;
672 if (nblocks > journal->j_max_transaction_buffers) {
673 printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
674 @@ -217,6 +220,12 @@ repeat_locked:
675 /* OK, account for the buffers that this operation expects to
676 * use and add the handle to the running transaction. */
678 + if (time_after(transaction->t_start, ts)) {
679 + ts = jbd2_time_diff(ts, transaction->t_start);
680 + if (ts > transaction->t_max_wait)
681 + transaction->t_max_wait = ts;
684 handle->h_transaction = transaction;
685 transaction->t_outstanding_credits += nblocks;
686 transaction->t_updates++;
687 diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
688 index d861ffd..6856400 100644
689 --- a/include/linux/jbd2.h
690 +++ b/include/linux/jbd2.h
691 @@ -395,6 +395,16 @@ struct handle_s
696 + * Some stats for checkpoint phase
698 +struct transaction_chp_stats_s {
699 + unsigned long cs_chp_time;
700 + unsigned long cs_forced_to_close;
701 + unsigned long cs_written;
702 + unsigned long cs_dropped;
705 /* The transaction_t type is the guts of the journaling mechanism. It
706 * tracks a compound transaction through its various states:
708 @@ -532,6 +542,21 @@ struct transaction_s
709 spinlock_t t_handle_lock;
712 + * Longest time some handle had to wait for running transaction
714 + unsigned long t_max_wait;
717 + * When transaction started
719 + unsigned long t_start;
722 + * Checkpointing stats [j_checkpoint_sem]
724 + struct transaction_chp_stats_s t_chp_stats;
727 * Number of outstanding updates running on this transaction
730 @@ -562,6 +587,39 @@ struct transaction_s
734 +struct transaction_run_stats_s {
735 + unsigned long rs_wait;
736 + unsigned long rs_running;
737 + unsigned long rs_locked;
738 + unsigned long rs_flushing;
739 + unsigned long rs_logging;
741 + unsigned long rs_handle_count;
742 + unsigned long rs_blocks;
743 + unsigned long rs_blocks_logged;
746 +struct transaction_stats_s {
748 + unsigned long ts_tid;
750 + struct transaction_run_stats_s run;
751 + struct transaction_chp_stats_s chp;
755 +#define JBD2_STATS_RUN 1
756 +#define JBD2_STATS_CHECKPOINT 2
758 +static inline unsigned long
759 +jbd2_time_diff(unsigned long start, unsigned long end)
762 + return end - start;
764 + return end + (MAX_JIFFY_OFFSET - start);
768 * struct journal_s - The journal_s type is the concrete type associated with
770 @@ -623,6 +681,12 @@ struct transaction_s
771 * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the
772 * number that will fit in j_blocksize
773 * @j_last_sync_writer: most recent pid which did a synchronous write
774 + * @j_history: Buffer storing the transactions statistics history
775 + * @j_history_max: Maximum number of transactions in the statistics history
776 + * @j_history_cur: Current number of transactions in the statistics history
777 + * @j_history_lock: Protect the transactions statistics history
778 + * @j_proc_entry: procfs entry for the jbd statistics directory
779 + * @j_stats: Overall statistics
780 * @j_private: An opaque pointer to fs-private information.
783 @@ -815,6 +879,19 @@ struct journal_s
784 pid_t j_last_sync_writer;
787 + * Journal statistics
789 + struct transaction_stats_s *j_history;
793 + * Protect the transactions statistics history
795 + spinlock_t j_history_lock;
796 + struct proc_dir_entry *j_proc_entry;
797 + struct transaction_stats_s j_stats;
800 * An opaque pointer to fs-private information. ext3 puts its
801 * superblock pointer here