Whamcloud - gitweb
b=17671
[fs/lustre-release.git] / lustre / kernel_patches / patches / jbd-stats-2.6.9.patch
1 Index: linux-2.6.9/include/linux/jbd.h
2 ===================================================================
3 --- linux-2.6.9.orig/include/linux/jbd.h
4 +++ linux-2.6.9/include/linux/jbd.h
5 @@ -422,6 +422,16 @@ struct handle_s 
6  };
7  
8  
9 +/*
10 + * Some stats for checkpoint phase
11 + */
12 +struct transaction_chp_stats_s {
13 +       unsigned long           cs_chp_time;
14 +       unsigned long           cs_forced_to_close;
15 +       unsigned long           cs_written;
16 +       unsigned long           cs_dropped;
17 +};
18 +
19  /* The transaction_t type is the guts of the journaling mechanism.  It
20   * tracks a compound transaction through its various states:
21   *
22 @@ -553,6 +563,21 @@ struct transaction_s 
23         spinlock_t              t_handle_lock;
24
25         /*
26 +        * Longest time some handle had to wait for running transaction
27 +        */
28 +       unsigned long           t_max_wait;
29 +
30 +       /*
31 +        * When transaction started
32 +        */
33 +       unsigned long           t_start;
34 +
35 +       /*
36 +        * Checkpointing stats [j_checkpoint_sem]
37 +        */
38 +       struct transaction_chp_stats_s t_chp_stats;
39 +
40 +       /*
41          * Number of outstanding updates running on this transaction
42          * [t_handle_lock]
43          */
44 @@ -592,6 +617,57 @@ struct transaction_s 
45  #endif
46  };
47
48 +struct transaction_run_stats_s {
49 +       unsigned long           rs_wait;
50 +       unsigned long           rs_running;
51 +       unsigned long           rs_locked;
52 +       unsigned long           rs_flushing;
53 +       unsigned long           rs_logging;
54 +
55 +       unsigned long           rs_handle_count;
56 +       unsigned long           rs_blocks;
57 +       unsigned long           rs_blocks_logged;
58 +};
59 +
60 +struct transaction_stats_s
61 +{
62 +       int                     ts_type;
63 +       unsigned long           ts_tid;
64 +       union {
65 +               struct transaction_run_stats_s run;
66 +               struct transaction_chp_stats_s chp;
67 +       } u;
68 +};
69 +
70 +#define JBD_STATS_RUN          1
71 +#define JBD_STATS_CHECKPOINT   2
72 +
73 +#define ts_wait                        u.run.rs_wait
74 +#define ts_running             u.run.rs_running
75 +#define ts_locked              u.run.rs_locked
76 +#define ts_flushing            u.run.rs_flushing
77 +#define ts_logging             u.run.rs_logging
78 +#define ts_handle_count                u.run.rs_handle_count
79 +#define ts_blocks              u.run.rs_blocks
80 +#define ts_blocks_logged       u.run.rs_blocks_logged
81 +
82 +#define ts_chp_time            u.chp.cs_chp_time
83 +#define ts_forced_to_close     u.chp.cs_forced_to_close
84 +#define ts_written             u.chp.cs_written
85 +#define ts_dropped             u.chp.cs_dropped
86 +
87 +#define CURRENT_MSECS          (jiffies_to_msecs(jiffies))
88 +
89 +static inline unsigned int
90 +jbd_time_diff(unsigned int start, unsigned int end)
91 +{
92 +       if (unlikely(start > end))
93 +               end = end + (~0UL - start);
94 +       else
95 +               end -= start;
96 +       return end;
97 +}
98 +
99  /**
100   * struct journal_s - The journal_s type is the concrete type associated with
101   *     journal_t.
102 @@ -828,6 +904,16 @@ struct journal_s
103         struct jbd_revoke_table_s *j_revoke_table[2];
104  
105         /*
106 +        *
107 +        */
108 +       struct transaction_stats_s *j_history;
109 +       int                     j_history_max;
110 +       int                     j_history_cur;
111 +       spinlock_t              j_history_lock;
112 +       struct proc_dir_entry   *j_proc_entry;
113 +       struct transaction_stats_s j_stats;
114 +       
115 +       /*
116          * An opaque pointer to fs-private information.  ext3 puts its
117          * superblock pointer here
118          */
119 Index: linux-2.6.9/fs/jbd/commit.c
120 ===================================================================
121 --- linux-2.6.9.orig/fs/jbd/commit.c
122 +++ linux-2.6.9/fs/jbd/commit.c
123 @@ -21,6 +21,7 @@
124  #include <linux/mm.h>
125  #include <linux/pagemap.h>
126  #include <linux/smp_lock.h>
127 +#include <linux/jiffies.h>
128
129  /*
130   * Default IO end handler for temporary BJ_IO buffer_heads.
131 @@ -101,6 +102,7 @@ static int inverted_lock(journal_t *jour
132   */
133  void journal_commit_transaction(journal_t *journal)
134  {
135 +       struct transaction_stats_s stats;
136         transaction_t *commit_transaction;
137         struct journal_head *jh, *new_jh, *descriptor;
138         struct buffer_head *wbuf[64];
139 @@ -147,6 +149,11 @@ void journal_commit_transaction(journal_
140         spin_lock(&journal->j_state_lock);
141         commit_transaction->t_state = T_LOCKED;
142
143 +       stats.ts_wait = commit_transaction->t_max_wait;
144 +       stats.ts_locked = CURRENT_MSECS;
145 +       stats.ts_running = jbd_time_diff(commit_transaction->t_start,
146 +                                               stats.ts_locked);
147 +       
148         spin_lock(&commit_transaction->t_handle_lock);
149         while (commit_transaction->t_updates) {
150                 DEFINE_WAIT(wait);
151 @@ -219,6 +226,9 @@ void journal_commit_transaction(journal_
152          */
153         journal_switch_revoke_table(journal);
154
155 +       stats.ts_flushing = CURRENT_MSECS;
156 +       stats.ts_locked = jbd_time_diff(stats.ts_locked, stats.ts_flushing);
157 +
158         commit_transaction->t_state = T_FLUSH;
159         journal->j_committing_transaction = commit_transaction;
160         journal->j_running_transaction = NULL;
161 @@ -365,6 +375,11 @@ write_out_data:
162          */
163         commit_transaction->t_state = T_COMMIT;
164
165 +       stats.ts_logging = CURRENT_MSECS;
166 +       stats.ts_flushing = jbd_time_diff(stats.ts_flushing, stats.ts_logging);
167 +       stats.ts_blocks = commit_transaction->t_outstanding_credits;
168 +       stats.ts_blocks_logged = 0;
169 +
170         descriptor = NULL;
171         bufs = 0;
172         while (commit_transaction->t_buffers) {
173 @@ -513,6 +528,7 @@ start_journal_io:
174                                 submit_bh(WRITE, bh);
175                         }
176                         cond_resched();
177 +                       stats.ts_blocks_logged += bufs;
178
179                         /* Force a new descriptor to be generated next
180                             time round the loop. */
181 @@ -767,6 +783,7 @@ restart_loop:
182                 cp_transaction = jh->b_cp_transaction;
183                 if (cp_transaction) {
184                         JBUFFER_TRACE(jh, "remove from old cp transaction");
185 +                       cp_transaction->t_chp_stats.cs_dropped++;
186                         __journal_remove_checkpoint(jh);
187                 }
188
189 @@ -816,6 +833,37 @@ restart_loop:
190                 }
191                 cond_resched_lock(&journal->j_list_lock);
192         }
193 +
194 +       commit_transaction->t_start = CURRENT_MSECS;
195 +       stats.ts_logging = jbd_time_diff(stats.ts_logging,
196 +                                       commit_transaction->t_start);
197 +
198 +       /*
199 +        * File the transaction for history
200 +        */
201 +       stats.ts_type = JBD_STATS_RUN;
202 +       stats.ts_tid = commit_transaction->t_tid;
203 +       stats.ts_handle_count = commit_transaction->t_handle_count;
204 +       spin_lock(&journal->j_history_lock);
205 +       memcpy(journal->j_history + journal->j_history_cur, &stats,
206 +               sizeof(stats));
207 +       if (++journal->j_history_cur == journal->j_history_max)
208 +               journal->j_history_cur = 0;
209 +
210 +       /*
211 +        * Calculate overall stats
212 +        */
213 +       journal->j_stats.ts_tid++;
214 +       journal->j_stats.ts_wait += stats.ts_wait;
215 +       journal->j_stats.ts_running += stats.ts_running;
216 +       journal->j_stats.ts_locked += stats.ts_locked;
217 +       journal->j_stats.ts_flushing += stats.ts_flushing;
218 +       journal->j_stats.ts_logging += stats.ts_logging;
219 +       journal->j_stats.ts_handle_count += stats.ts_handle_count;
220 +       journal->j_stats.ts_blocks += stats.ts_blocks;
221 +       journal->j_stats.ts_blocks_logged += stats.ts_blocks_logged;
222 +       spin_unlock(&journal->j_history_lock);
223 +
224         spin_unlock(&journal->j_list_lock);
225         /*
226          * This is a bit sleazy.  We borrow j_list_lock to protect
227 Index: linux-2.6.9/fs/jbd/checkpoint.c
228 ===================================================================
229 --- linux-2.6.9.orig/fs/jbd/checkpoint.c
230 +++ linux-2.6.9/fs/jbd/checkpoint.c
231 @@ -126,7 +127,8 @@
232   * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it
233   */
234  static int __process_buffer(journal_t *journal, struct journal_head *jh,
235 -                       struct buffer_head **bhs, int *batch_count)
236 +                       struct buffer_head **bhs, int *batch_count,
237 +                       transaction_t *transaction)
238  {
239         struct buffer_head *bh = jh2bh(jh);
240         int ret = 0;
241 @@ -166,6 +166,7 @@
242                 transaction_t *t = jh->b_transaction;
243                 tid_t tid = t->t_tid;
244
245 +               transaction->t_chp_stats.cs_forced_to_close++;
246                 spin_unlock(&journal->j_list_lock);
247                 jbd_unlock_bh_state(bh);
248                 log_start_commit(journal, tid);
249 @@ -247,6 +248,7 @@
250                 bhs[*batch_count] = bh;
251                 __buffer_relink_io(jh);
252                 jbd_unlock_bh_state(bh);
253 +               transaction->t_chp_stats.cs_written++;
254                 (*batch_count)++;
255                 if (*batch_count == NR_BATCH) {
256                         spin_unlock(&journal->j_list_lock);
257 @@ -315,6 +317,8 @@ int log_do_checkpoint(journal_t *journal
258         if (!journal->j_checkpoint_transactions)
259                 goto out;
260         transaction = journal->j_checkpoint_transactions;
261 +       if (transaction->t_chp_stats.cs_chp_time == 0)
262 +               transaction->t_chp_stats.cs_chp_time = CURRENT_MSECS;
263         this_tid = transaction->t_tid;
264  restart:
265         /*
266 @@ -331,7 +335,8 @@ int log_do_checkpoint(journal_t *journal
267                                 retry = 1;
268                                 break;
269                         }
270 -                       retry = __process_buffer(journal, jh, bhs,&batch_count);
271 +                       retry = __process_buffer(journal, jh, bhs,&batch_count,
272 +                                       transaction);
273                 }
274
275                 if (batch_count) {
276 @@ -597,6 +602,8 @@ void __journal_insert_checkpoint(struct 
277
278  void __journal_drop_transaction(journal_t *journal, transaction_t *transaction)
279  {
280 +       struct transaction_stats_s stats;
281 +
282         assert_spin_locked(&journal->j_list_lock);
283         if (transaction->t_cpnext) {
284                 transaction->t_cpnext->t_cpprev = transaction->t_cpprev;
285 @@ -622,5 +629,25 @@ void __journal_drop_transaction(journal_
286         J_ASSERT(journal->j_running_transaction != transaction);
287
288         jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid);
289 +
290 +       /*
291 +        * File the transaction for history
292 +        */
293 +       if (transaction->t_chp_stats.cs_written != 0 ||
294 +                       transaction->t_chp_stats.cs_chp_time != 0) {
295 +               stats.ts_type = JBD_STATS_CHECKPOINT;
296 +               stats.ts_tid = transaction->t_tid;
297 +               stats.u.chp = transaction->t_chp_stats;
298 +               if (stats.ts_chp_time)
299 +                       stats.ts_chp_time = 
300 +                               jbd_time_diff(stats.ts_chp_time, CURRENT_MSECS);
301 +               spin_lock(&journal->j_history_lock);
302 +               memcpy(journal->j_history + journal->j_history_cur, &stats,
303 +                               sizeof(stats));
304 +               if (++journal->j_history_cur == journal->j_history_max)
305 +                       journal->j_history_cur = 0;
306 +               spin_unlock(&journal->j_history_lock);
307 +       }
308 +
309         kfree(transaction);
310  }
311 Index: linux-2.6.9/fs/jbd/transaction.c
312 ===================================================================
313 --- linux-2.6.9.orig/fs/jbd/transaction.c
314 +++ linux-2.6.9/fs/jbd/transaction.c
315 @@ -60,6 +60,8 @@ get_transaction(journal_t *journal, tran
316  
317         J_ASSERT(journal->j_running_transaction == NULL);
318         journal->j_running_transaction = transaction;
319 +       transaction->t_max_wait = 0;
320 +       transaction->t_start = CURRENT_MSECS;
321  
322         return transaction;
323  }
324 @@ -86,6 +88,7 @@ static int start_this_handle(journal_t *
325         int nblocks = handle->h_buffer_credits;
326         transaction_t *new_transaction = NULL;
327         int ret = 0;
328 +       unsigned long ts = CURRENT_MSECS;
329  
330         if (nblocks > journal->j_max_transaction_buffers) {
331                 printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
332 @@ -219,6 +222,12 @@ repeat_locked:
333         /* OK, account for the buffers that this operation expects to
334          * use and add the handle to the running transaction. */
335  
336 +       if (time_after(transaction->t_start, ts)) {
337 +               ts = jbd_time_diff(ts, transaction->t_start);
338 +               if (ts > transaction->t_max_wait)
339 +                       transaction->t_max_wait= ts;
340 +       }
341 +
342         handle->h_transaction = transaction;
343         transaction->t_outstanding_credits += nblocks;
344         transaction->t_updates++;
345 Index: linux-2.6.9/fs/jbd/journal.c
346 ===================================================================
347 --- linux-2.6.9.orig/fs/jbd/journal.c
348 +++ linux-2.6.9/fs/jbd/journal.c
349 @@ -36,6 +36,7 @@
350  #include <asm/uaccess.h>
351  #include <asm/page.h>
352  #include <linux/proc_fs.h>
353 +#include <linux/seq_file.h>
354  
355  EXPORT_SYMBOL(journal_start);
356  EXPORT_SYMBOL(journal_restart);
357 @@ -649,6 +650,300 @@ struct journal_head *journal_get_descrip
358         return journal_add_journal_head(bh);
359  }
360  
361 +struct jbd_stats_proc_session {
362 +       journal_t *journal;
363 +       struct transaction_stats_s *stats;
364 +       int start;
365 +       int max;
366 +};
367 +
368 +static void *jbd_history_skip_empty(struct jbd_stats_proc_session *s,
369 +                                       struct transaction_stats_s *ts,
370 +                                       int first)
371 +{
372 +       if (ts == s->stats + s->max)
373 +               ts = s->stats;
374 +       if (!first && ts == s->stats + s->start)
375 +               return NULL;
376 +       while (ts->ts_type == 0) {
377 +               ts++;
378 +               if (ts == s->stats + s->max)
379 +                       ts = s->stats;
380 +               if (ts == s->stats + s->start)
381 +                       return NULL;
382 +       }
383 +       return ts;
384 +
385 +}
386 +
387 +static void *jbd_seq_history_start(struct seq_file *seq, loff_t *pos)
388 +{
389 +       struct jbd_stats_proc_session *s = seq->private;
390 +       struct transaction_stats_s *ts;
391 +       int l = *pos;
392 +
393 +       if (l == 0)
394 +               return SEQ_START_TOKEN;
395 +       ts = jbd_history_skip_empty(s, s->stats + s->start, 1);
396 +       if (!ts)
397 +               return NULL;
398 +       while (--l && (ts = jbd_history_skip_empty(s, ++ts, 0)) != NULL);
399 +       return ts;
400 +}
401 +
402 +static void *jbd_seq_history_next(struct seq_file *seq, void *v, loff_t *pos)
403 +{
404 +       struct jbd_stats_proc_session *s = seq->private;
405 +       struct transaction_stats_s *ts = v;
406 +
407 +       ++*pos;
408 +       if (v == SEQ_START_TOKEN)
409 +               return jbd_history_skip_empty(s, s->stats + s->start, 1);
410 +       else
411 +               return jbd_history_skip_empty(s, ++ts, 0);
412 +}
413 +
414 +static int jbd_seq_history_show(struct seq_file *seq, void *v)
415 +{
416 +       struct transaction_stats_s *ts = v;
417 +       if (v == SEQ_START_TOKEN) {
418 +               seq_printf(seq, "%-4s %-5s %-5s %-5s %-5s %-5s %-5s %-6s %-5s "
419 +                               "%-5s %-5s %-5s %-5s %-5s\n", "R/C", "tid",
420 +                               "wait", "run", "lock", "flush", "log", "hndls",
421 +                               "block", "inlog", "ctime", "write", "drop",
422 +                               "close");
423 +               return 0;
424 +       }
425 +       if (ts->ts_type == JBD_STATS_RUN)
426 +               seq_printf(seq, "%-4s %-5lu %-5lu %-5lu %-5lu %-5lu %-5lu "
427 +                               "%-6lu %-5lu %-5lu\n", "R", ts->ts_tid,
428 +                               ts->ts_wait, ts->ts_running, ts->ts_locked,
429 +                               ts->ts_flushing, ts->ts_logging,
430 +                               ts->ts_handle_count, ts->ts_blocks,
431 +                               ts->ts_blocks_logged);
432 +       else if (ts->ts_type == JBD_STATS_CHECKPOINT)
433 +               seq_printf(seq, "%-4s %-5lu %48s %-5lu %-5lu %-5lu %-5lu\n",
434 +                               "C", ts->ts_tid, " ", ts->ts_chp_time,
435 +                               ts->ts_written, ts->ts_dropped,
436 +                               ts->ts_forced_to_close);
437 +       else
438 +               J_ASSERT(0);
439 +       return 0;
440 +}
441 +
442 +static void jbd_seq_history_stop(struct seq_file *seq, void *v)
443 +{
444 +}
445 +
446 +static struct seq_operations jbd_seq_history_ops = {
447 +       .start  = jbd_seq_history_start,
448 +       .next   = jbd_seq_history_next,
449 +       .stop   = jbd_seq_history_stop,
450 +       .show   = jbd_seq_history_show,
451 +};
452 +
453 +static int jbd_seq_history_open(struct inode *inode, struct file *file)
454 +{
455 +       journal_t *journal = PDE(inode)->data;
456 +       struct jbd_stats_proc_session *s;
457 +       int rc, size;
458 +
459 +       s = kmalloc(sizeof(*s), GFP_KERNEL);
460 +       if (s == NULL)
461 +               return -EIO;
462 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
463 +       s->stats = kmalloc(size, GFP_KERNEL);
464 +       if (s->stats == NULL) {
465 +               kfree(s);
466 +               return -EIO;
467 +       }
468 +       spin_lock(&journal->j_history_lock);
469 +       memcpy(s->stats, journal->j_history, size);
470 +       s->max = journal->j_history_max;
471 +       s->start = journal->j_history_cur % s->max;
472 +       spin_unlock(&journal->j_history_lock);
473 +       
474 +       rc = seq_open(file, &jbd_seq_history_ops);
475 +       if (rc == 0) {
476 +               struct seq_file *m = (struct seq_file *)file->private_data;
477 +               m->private = s;
478 +       } else {
479 +               kfree(s->stats);
480 +               kfree(s);
481 +       }
482 +       return rc;
483 +
484 +}
485 +
486 +static int jbd_seq_history_release(struct inode *inode, struct file *file)
487 +{
488 +       struct seq_file *seq = (struct seq_file *)file->private_data;
489 +       struct jbd_stats_proc_session *s = seq->private;
490 +       kfree(s->stats);
491 +       kfree(s);
492 +       return seq_release(inode, file);
493 +}
494 +
495 +static struct file_operations jbd_seq_history_fops = {
496 +       .owner          = THIS_MODULE,
497 +       .open           = jbd_seq_history_open,
498 +       .read           = seq_read,
499 +       .llseek         = seq_lseek,
500 +       .release        = jbd_seq_history_release,
501 +};
502 +
503 +static void *jbd_seq_info_start(struct seq_file *seq, loff_t *pos)
504 +{
505 +       return *pos ? NULL : SEQ_START_TOKEN;
506 +}
507 +
508 +static void *jbd_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
509 +{
510 +       return NULL;
511 +}
512 +
513 +static int jbd_seq_info_show(struct seq_file *seq, void *v)
514 +{
515 +       struct jbd_stats_proc_session *s = seq->private;
516 +       if (v != SEQ_START_TOKEN)
517 +               return 0;
518 +       seq_printf(seq, "%lu transaction, each upto %u blocks\n",
519 +                       s->stats->ts_tid,
520 +                       s->journal->j_max_transaction_buffers);
521 +       if (s->stats->ts_tid == 0)
522 +               return 0;
523 +       seq_printf(seq, "average: \n  %lums waiting for transaction\n",
524 +                       s->stats->ts_wait / s->stats->ts_tid);
525 +       seq_printf(seq, "  %lums running transaction\n",
526 +                       s->stats->ts_running / s->stats->ts_tid);
527 +       seq_printf(seq, "  %lums transaction was being locked\n",
528 +                       s->stats->ts_locked / s->stats->ts_tid);
529 +       seq_printf(seq, "  %lums flushing data (in ordered mode)\n",
530 +                       s->stats->ts_flushing / s->stats->ts_tid);
531 +       seq_printf(seq, "  %lums logging transaction\n",
532 +                       s->stats->ts_logging / s->stats->ts_tid);
533 +       seq_printf(seq, "  %lu handles per transaction\n",
534 +                       s->stats->ts_handle_count / s->stats->ts_tid);
535 +       seq_printf(seq, "  %lu blocks per transaction\n",
536 +                       s->stats->ts_blocks / s->stats->ts_tid);
537 +       seq_printf(seq, "  %lu logged blocks per transaction\n",
538 +                       s->stats->ts_blocks_logged / s->stats->ts_tid);
539 +       return 0;
540 +}
541 +
542 +static void jbd_seq_info_stop(struct seq_file *seq, void *v)
543 +{
544 +}
545 +
546 +static struct seq_operations jbd_seq_info_ops = {
547 +       .start  = jbd_seq_info_start,
548 +       .next   = jbd_seq_info_next,
549 +       .stop   = jbd_seq_info_stop,
550 +       .show   = jbd_seq_info_show,
551 +};
552 +
553 +static int jbd_seq_info_open(struct inode *inode, struct file *file)
554 +{
555 +       journal_t *journal = PDE(inode)->data;
556 +       struct jbd_stats_proc_session *s;
557 +       int rc, size;
558 +
559 +       s = kmalloc(sizeof(*s), GFP_KERNEL);
560 +       if (s == NULL)
561 +               return -EIO;
562 +       size = sizeof(struct transaction_stats_s);
563 +       s->stats = kmalloc(size, GFP_KERNEL);
564 +       if (s->stats == NULL) {
565 +               kfree(s);
566 +               return -EIO;
567 +       }
568 +       spin_lock(&journal->j_history_lock);
569 +       memcpy(s->stats, &journal->j_stats, size);
570 +       s->journal = journal;
571 +       spin_unlock(&journal->j_history_lock);
572 +       
573 +       rc = seq_open(file, &jbd_seq_info_ops);
574 +       if (rc == 0) {
575 +               struct seq_file *m = (struct seq_file *)file->private_data;
576 +               m->private = s;
577 +       } else {
578 +               kfree(s->stats);
579 +               kfree(s);
580 +       }
581 +       return rc;
582 +
583 +}
584 +
585 +static int jbd_seq_info_release(struct inode *inode, struct file *file)
586 +{
587 +       struct seq_file *seq = (struct seq_file *)file->private_data;
588 +       struct jbd_stats_proc_session *s = seq->private;
589 +       kfree(s->stats);
590 +       kfree(s);
591 +       return seq_release(inode, file);
592 +}
593 +
594 +static struct file_operations jbd_seq_info_fops = {
595 +       .owner          = THIS_MODULE,
596 +       .open           = jbd_seq_info_open,
597 +       .read           = seq_read,
598 +       .llseek         = seq_lseek,
599 +       .release        = jbd_seq_info_release,
600 +};
601 +
602 +static struct proc_dir_entry *proc_jbd_stats = NULL;
603 +
604 +static void jbd_stats_proc_init(journal_t *journal)
605 +{
606 +       char name[64];
607 +
608 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
609 +       journal->j_proc_entry = proc_mkdir(name, proc_jbd_stats);
610 +       if (journal->j_proc_entry) {
611 +               struct proc_dir_entry *p;
612 +               p = create_proc_entry("history", S_IRUGO,
613 +                               journal->j_proc_entry);
614 +               if (p) {
615 +                       p->proc_fops = &jbd_seq_history_fops;
616 +                       p->data = journal;
617 +                       p = create_proc_entry("info", S_IRUGO,
618 +                                               journal->j_proc_entry);
619 +                       if (p) {
620 +                               p->proc_fops = &jbd_seq_info_fops;
621 +                               p->data = journal;
622 +                       }
623 +               }
624 +       }
625 +}
626 +
627 +static void jbd_stats_proc_exit(journal_t *journal)
628 +{
629 +       char name[64];
630 +
631 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
632 +       remove_proc_entry("info", journal->j_proc_entry);
633 +       remove_proc_entry("history", journal->j_proc_entry);
634 +       remove_proc_entry(name, proc_jbd_stats);
635 +}
636 +
637 +static void journal_init_stats(journal_t *journal)
638 +{
639 +       int size;
640 +
641 +       if (proc_jbd_stats == NULL)
642 +               return;
643 +
644 +       journal->j_history_max = 100;
645 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
646 +       journal->j_history = kmalloc(size, GFP_KERNEL);
647 +       if (journal->j_history == NULL) {
648 +               journal->j_history_max = 0;
649 +               return;
650 +       }
651 +       memset(journal->j_history, 0, size);
652 +       spin_lock_init(&journal->j_history_lock);
653 +}
654 +
655  /*
656   * Management for journal control blocks: functions to create and
657   * destroy journal_t structures, and to initialise and read existing
658 @@ -691,6 +986,9 @@ static journal_t * journal_init_common (
659                 kfree(journal);
660                 goto fail;
661         }
662 +       
663 +       journal_init_stats(journal);
664 +
665         return journal;
666  fail:
667         return NULL;
668 @@ -733,6 +1031,7 @@ journal_t * journal_init_dev(struct bloc
669         journal->j_blk_offset = start;
670         journal->j_maxlen = len;
671         journal->j_blocksize = blocksize;
672 +       jbd_stats_proc_init(journal);
673  
674         bh = __getblk(journal->j_dev, start, journal->j_blocksize);
675         J_ASSERT(bh != NULL);
676 @@ -770,6 +1069,7 @@ journal_t * journal_init_inode (struct i
677  
678         journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
679         journal->j_blocksize = inode->i_sb->s_blocksize;
680 +       jbd_stats_proc_init(journal);
681  
682         err = journal_bmap(journal, 0, &blocknr);
683         /* If that failed, give up */
684 @@ -1140,6 +1440,8 @@ void journal_destroy(journal_t *journal)
685                 brelse(journal->j_sb_buffer);
686         }
687  
688 +       if (journal->j_proc_entry)
689 +               jbd_stats_proc_exit(journal);
690         if (journal->j_inode)
691                 iput(journal->j_inode);
692         if (journal->j_revoke)
693 @@ -1896,6 +2198,28 @@ static void __exit remove_jbd_proc_entry
694  
695  #endif
696  
697 +#if defined(CONFIG_PROC_FS)
698 +
699 +#define JBD_STATS_PROC_NAME "fs/jbd"
700 +
701 +static void __init create_jbd_stats_proc_entry(void)
702 +{
703 +       proc_jbd_stats = proc_mkdir(JBD_STATS_PROC_NAME, NULL);
704 +}
705 +
706 +static void __exit remove_jbd_stats_proc_entry(void)
707 +{
708 +       if (proc_jbd_stats)
709 +               remove_proc_entry(JBD_STATS_PROC_NAME, NULL);
710 +}
711 +
712 +#else
713 +
714 +#define create_jbd_stats_proc_entry() do {} while (0)
715 +#define remove_jbd_stats_proc_entry() do {} while (0)
716 +
717 +#endif
718 +
719  kmem_cache_t *jbd_handle_cache;
720  
721  static int __init journal_init_handle_cache(void)
722 @@ -1950,6 +2274,7 @@ static int __init journal_init(void)
723         if (ret != 0)
724                 journal_destroy_caches();
725         create_jbd_proc_entry();
726 +       create_jbd_stats_proc_entry();
727         return ret;
728  }
729  
730 @@ -1961,6 +2286,7 @@ static void __exit journal_exit(void)
731                 printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
732  #endif
733         remove_jbd_proc_entry();
734 +       remove_jbd_stats_proc_entry();
735         journal_destroy_caches();
736  }
737