Whamcloud - gitweb
Add journal checksum feature.
[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         struct list_head        t_jcb;
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 @@ -166,6 +166,7 @@ static int __cleanup_transaction(journal
232                         transaction_t *t = jh->b_transaction;
233                         tid_t tid = t->t_tid;
234
235 +                       transaction->t_chp_stats.cs_forced_to_close++;
236                         spin_unlock(&journal->j_list_lock);
237                         jbd_unlock_bh_state(bh);
238                         log_start_commit(journal, tid);
239 @@ -226,7 +227,7 @@ __flush_batch(journal_t *journal, struct
240   */
241  static int __flush_buffer(journal_t *journal, struct journal_head *jh,
242                         struct buffer_head **bhs, int *batch_count,
243 -                       int *drop_count)
244 +                       int *drop_count, transaction_t *transaction)
245  {
246         struct buffer_head *bh = jh2bh(jh);
247         int ret = 0;
248 @@ -247,6 +248,7 @@ static int __flush_buffer(journal_t *jou
249                 set_buffer_jwrite(bh);
250                 bhs[*batch_count] = bh;
251                 jbd_unlock_bh_state(bh);
252 +               transaction->t_chp_stats.cs_written++;
253                 (*batch_count)++;
254                 if (*batch_count == NR_BATCH) {
255                         __flush_batch(journal, bhs, batch_count);
256 @@ -315,6 +317,8 @@ int log_do_checkpoint(journal_t *journal
257                 tid_t this_tid;
258
259                 transaction = journal->j_checkpoint_transactions;
260 +               if (transaction->t_chp_stats.cs_chp_time == 0)
261 +                       transaction->t_chp_stats.cs_chp_time = CURRENT_MSECS;
262                 this_tid = transaction->t_tid;
263                 jh = transaction->t_checkpoint_list;
264                 last_jh = jh->b_cpprev;
265 @@ -331,7 +335,8 @@ int log_do_checkpoint(journal_t *journal
266                                 retry = 1;
267                                 break;
268                         }
269 -                       retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count);
270 +                       retry = __flush_buffer(journal, jh, bhs, &batch_count, 
271 +                                       &drop_count, transaction);
272                 } while (jh != last_jh && !retry);
273
274                 if (batch_count) {
275 @@ -597,6 +602,8 @@ void __journal_insert_checkpoint(struct 
276
277  void __journal_drop_transaction(journal_t *journal, transaction_t *transaction)
278  {
279 +       struct transaction_stats_s stats;
280 +
281         assert_spin_locked(&journal->j_list_lock);
282         if (transaction->t_cpnext) {
283                 transaction->t_cpnext->t_cpprev = transaction->t_cpprev;
284 @@ -622,5 +629,25 @@ void __journal_drop_transaction(journal_
285         J_ASSERT(journal->j_running_transaction != transaction);
286
287         jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid);
288 +
289 +       /*
290 +        * File the transaction for history
291 +        */
292 +       if (transaction->t_chp_stats.cs_written != 0 ||
293 +                       transaction->t_chp_stats.cs_chp_time != 0) {
294 +               stats.ts_type = JBD_STATS_CHECKPOINT;
295 +               stats.ts_tid = transaction->t_tid;
296 +               stats.u.chp = transaction->t_chp_stats;
297 +               if (stats.ts_chp_time)
298 +                       stats.ts_chp_time = 
299 +                               jbd_time_diff(stats.ts_chp_time, CURRENT_MSECS);
300 +               spin_lock(&journal->j_history_lock);
301 +               memcpy(journal->j_history + journal->j_history_cur, &stats,
302 +                               sizeof(stats));
303 +               if (++journal->j_history_cur == journal->j_history_max)
304 +                       journal->j_history_cur = 0;
305 +               spin_unlock(&journal->j_history_lock);
306 +       }
307 +
308         kfree(transaction);
309  }
310 Index: linux-2.6.9/fs/jbd/transaction.c
311 ===================================================================
312 --- linux-2.6.9.orig/fs/jbd/transaction.c
313 +++ linux-2.6.9/fs/jbd/transaction.c
314 @@ -60,6 +60,8 @@ get_transaction(journal_t *journal, tran
315  
316         J_ASSERT(journal->j_running_transaction == NULL);
317         journal->j_running_transaction = transaction;
318 +       transaction->t_max_wait = 0;
319 +       transaction->t_start = CURRENT_MSECS;
320  
321         return transaction;
322  }
323 @@ -86,6 +88,7 @@ static int start_this_handle(journal_t *
324         int nblocks = handle->h_buffer_credits;
325         transaction_t *new_transaction = NULL;
326         int ret = 0;
327 +       unsigned long ts = CURRENT_MSECS;
328  
329         if (nblocks > journal->j_max_transaction_buffers) {
330                 printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
331 @@ -219,6 +222,12 @@ repeat_locked:
332         /* OK, account for the buffers that this operation expects to
333          * use and add the handle to the running transaction. */
334  
335 +       if (time_after(transaction->t_start, ts)) {
336 +               ts = jbd_time_diff(ts, transaction->t_start);
337 +               if (ts > transaction->t_max_wait)
338 +                       transaction->t_max_wait= ts;
339 +       }
340 +
341         handle->h_transaction = transaction;
342         transaction->t_outstanding_credits += nblocks;
343         transaction->t_updates++;
344 Index: linux-2.6.9/fs/jbd/journal.c
345 ===================================================================
346 --- linux-2.6.9.orig/fs/jbd/journal.c
347 +++ linux-2.6.9/fs/jbd/journal.c
348 @@ -36,6 +36,7 @@
349  #include <asm/uaccess.h>
350  #include <asm/page.h>
351  #include <linux/proc_fs.h>
352 +#include <linux/seq_file.h>
353  
354  EXPORT_SYMBOL(journal_start);
355  EXPORT_SYMBOL(journal_restart);
356 @@ -649,6 +650,300 @@ struct journal_head *journal_get_descrip
357         return journal_add_journal_head(bh);
358  }
359  
360 +struct jbd_stats_proc_session {
361 +       journal_t *journal;
362 +       struct transaction_stats_s *stats;
363 +       int start;
364 +       int max;
365 +};
366 +
367 +static void *jbd_history_skip_empty(struct jbd_stats_proc_session *s,
368 +                                       struct transaction_stats_s *ts,
369 +                                       int first)
370 +{
371 +       if (ts == s->stats + s->max)
372 +               ts = s->stats;
373 +       if (!first && ts == s->stats + s->start)
374 +               return NULL;
375 +       while (ts->ts_type == 0) {
376 +               ts++;
377 +               if (ts == s->stats + s->max)
378 +                       ts = s->stats;
379 +               if (ts == s->stats + s->start)
380 +                       return NULL;
381 +       }
382 +       return ts;
383 +
384 +}
385 +
386 +static void *jbd_seq_history_start(struct seq_file *seq, loff_t *pos)
387 +{
388 +       struct jbd_stats_proc_session *s = seq->private;
389 +       struct transaction_stats_s *ts;
390 +       int l = *pos;
391 +
392 +       if (l == 0)
393 +               return SEQ_START_TOKEN;
394 +       ts = jbd_history_skip_empty(s, s->stats + s->start, 1);
395 +       if (!ts)
396 +               return NULL;
397 +       while (--l && (ts = jbd_history_skip_empty(s, ++ts, 0)) != NULL);
398 +       return ts;
399 +}
400 +
401 +static void *jbd_seq_history_next(struct seq_file *seq, void *v, loff_t *pos)
402 +{
403 +       struct jbd_stats_proc_session *s = seq->private;
404 +       struct transaction_stats_s *ts = v;
405 +
406 +       ++*pos;
407 +       if (v == SEQ_START_TOKEN)
408 +               return jbd_history_skip_empty(s, s->stats + s->start, 1);
409 +       else
410 +               return jbd_history_skip_empty(s, ++ts, 0);
411 +}
412 +
413 +static int jbd_seq_history_show(struct seq_file *seq, void *v)
414 +{
415 +       struct transaction_stats_s *ts = v;
416 +       if (v == SEQ_START_TOKEN) {
417 +               seq_printf(seq, "%-4s %-5s %-5s %-5s %-5s %-5s %-5s %-6s %-5s "
418 +                               "%-5s %-5s %-5s %-5s %-5s\n", "R/C", "tid",
419 +                               "wait", "run", "lock", "flush", "log", "hndls",
420 +                               "block", "inlog", "ctime", "write", "drop",
421 +                               "close");
422 +               return 0;
423 +       }
424 +       if (ts->ts_type == JBD_STATS_RUN)
425 +               seq_printf(seq, "%-4s %-5lu %-5lu %-5lu %-5lu %-5lu %-5lu "
426 +                               "%-6lu %-5lu %-5lu\n", "R", ts->ts_tid,
427 +                               ts->ts_wait, ts->ts_running, ts->ts_locked,
428 +                               ts->ts_flushing, ts->ts_logging,
429 +                               ts->ts_handle_count, ts->ts_blocks,
430 +                               ts->ts_blocks_logged);
431 +       else if (ts->ts_type == JBD_STATS_CHECKPOINT)
432 +               seq_printf(seq, "%-4s %-5lu %48s %-5lu %-5lu %-5lu %-5lu\n",
433 +                               "C", ts->ts_tid, " ", ts->ts_chp_time,
434 +                               ts->ts_written, ts->ts_dropped,
435 +                               ts->ts_forced_to_close);
436 +       else
437 +               J_ASSERT(0);
438 +       return 0;
439 +}
440 +
441 +static void jbd_seq_history_stop(struct seq_file *seq, void *v)
442 +{
443 +}
444 +
445 +static struct seq_operations jbd_seq_history_ops = {
446 +       .start  = jbd_seq_history_start,
447 +       .next   = jbd_seq_history_next,
448 +       .stop   = jbd_seq_history_stop,
449 +       .show   = jbd_seq_history_show,
450 +};
451 +
452 +static int jbd_seq_history_open(struct inode *inode, struct file *file)
453 +{
454 +       journal_t *journal = PDE(inode)->data;
455 +       struct jbd_stats_proc_session *s;
456 +       int rc, size;
457 +
458 +       s = kmalloc(sizeof(*s), GFP_KERNEL);
459 +       if (s == NULL)
460 +               return -EIO;
461 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
462 +       s->stats = kmalloc(size, GFP_KERNEL);
463 +       if (s == NULL) {
464 +               kfree(s);
465 +               return -EIO;
466 +       }
467 +       spin_lock(&journal->j_history_lock);
468 +       memcpy(s->stats, journal->j_history, size);
469 +       s->max = journal->j_history_max;
470 +       s->start = journal->j_history_cur % s->max;
471 +       spin_unlock(&journal->j_history_lock);
472 +       
473 +       rc = seq_open(file, &jbd_seq_history_ops);
474 +       if (rc == 0) {
475 +               struct seq_file *m = (struct seq_file *)file->private_data;
476 +               m->private = s;
477 +       } else {
478 +               kfree(s->stats);
479 +               kfree(s);
480 +       }
481 +       return rc;
482 +
483 +}
484 +
485 +static int jbd_seq_history_release(struct inode *inode, struct file *file)
486 +{
487 +       struct seq_file *seq = (struct seq_file *)file->private_data;
488 +       struct jbd_stats_proc_session *s = seq->private;
489 +       kfree(s->stats);
490 +       kfree(s);
491 +       return seq_release(inode, file);
492 +}
493 +
494 +static struct file_operations jbd_seq_history_fops = {
495 +       .owner          = THIS_MODULE,
496 +       .open           = jbd_seq_history_open,
497 +       .read           = seq_read,
498 +       .llseek         = seq_lseek,
499 +       .release        = jbd_seq_history_release,
500 +};
501 +
502 +static void *jbd_seq_info_start(struct seq_file *seq, loff_t *pos)
503 +{
504 +       return *pos ? NULL : SEQ_START_TOKEN;
505 +}
506 +
507 +static void *jbd_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
508 +{
509 +       return NULL;
510 +}
511 +
512 +static int jbd_seq_info_show(struct seq_file *seq, void *v)
513 +{
514 +       struct jbd_stats_proc_session *s = seq->private;
515 +       if (v != SEQ_START_TOKEN)
516 +               return 0;
517 +       seq_printf(seq, "%lu transaction, each upto %u blocks\n",
518 +                       s->stats->ts_tid,
519 +                       s->journal->j_max_transaction_buffers);
520 +       if (s->stats->ts_tid == 0)
521 +               return 0;
522 +       seq_printf(seq, "average: \n  %lums waiting for transaction\n",
523 +                       s->stats->ts_wait / s->stats->ts_tid);
524 +       seq_printf(seq, "  %lums running transaction\n",
525 +                       s->stats->ts_running / s->stats->ts_tid);
526 +       seq_printf(seq, "  %lums transaction was being locked\n",
527 +                       s->stats->ts_locked / s->stats->ts_tid);
528 +       seq_printf(seq, "  %lums flushing data (in ordered mode)\n",
529 +                       s->stats->ts_flushing / s->stats->ts_tid);
530 +       seq_printf(seq, "  %lums logging transaction\n",
531 +                       s->stats->ts_logging / s->stats->ts_tid);
532 +       seq_printf(seq, "  %lu handles per transaction\n",
533 +                       s->stats->ts_handle_count / s->stats->ts_tid);
534 +       seq_printf(seq, "  %lu blocks per transaction\n",
535 +                       s->stats->ts_blocks / s->stats->ts_tid);
536 +       seq_printf(seq, "  %lu logged blocks per transaction\n",
537 +                       s->stats->ts_blocks_logged / s->stats->ts_tid);
538 +       return 0;
539 +}
540 +
541 +static void jbd_seq_info_stop(struct seq_file *seq, void *v)
542 +{
543 +}
544 +
545 +static struct seq_operations jbd_seq_info_ops = {
546 +       .start  = jbd_seq_info_start,
547 +       .next   = jbd_seq_info_next,
548 +       .stop   = jbd_seq_info_stop,
549 +       .show   = jbd_seq_info_show,
550 +};
551 +
552 +static int jbd_seq_info_open(struct inode *inode, struct file *file)
553 +{
554 +       journal_t *journal = PDE(inode)->data;
555 +       struct jbd_stats_proc_session *s;
556 +       int rc, size;
557 +
558 +       s = kmalloc(sizeof(*s), GFP_KERNEL);
559 +       if (s == NULL)
560 +               return -EIO;
561 +       size = sizeof(struct transaction_stats_s);
562 +       s->stats = kmalloc(size, GFP_KERNEL);
563 +       if (s == NULL) {
564 +               kfree(s);
565 +               return -EIO;
566 +       }
567 +       spin_lock(&journal->j_history_lock);
568 +       memcpy(s->stats, &journal->j_stats, size);
569 +       s->journal = journal;
570 +       spin_unlock(&journal->j_history_lock);
571 +       
572 +       rc = seq_open(file, &jbd_seq_info_ops);
573 +       if (rc == 0) {
574 +               struct seq_file *m = (struct seq_file *)file->private_data;
575 +               m->private = s;
576 +       } else {
577 +               kfree(s->stats);
578 +               kfree(s);
579 +       }
580 +       return rc;
581 +
582 +}
583 +
584 +static int jbd_seq_info_release(struct inode *inode, struct file *file)
585 +{
586 +       struct seq_file *seq = (struct seq_file *)file->private_data;
587 +       struct jbd_stats_proc_session *s = seq->private;
588 +       kfree(s->stats);
589 +       kfree(s);
590 +       return seq_release(inode, file);
591 +}
592 +
593 +static struct file_operations jbd_seq_info_fops = {
594 +       .owner          = THIS_MODULE,
595 +       .open           = jbd_seq_info_open,
596 +       .read           = seq_read,
597 +       .llseek         = seq_lseek,
598 +       .release        = jbd_seq_info_release,
599 +};
600 +
601 +static struct proc_dir_entry *proc_jbd_stats = NULL;
602 +
603 +static void jbd_stats_proc_init(journal_t *journal)
604 +{
605 +       char name[64];
606 +
607 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
608 +       journal->j_proc_entry = proc_mkdir(name, proc_jbd_stats);
609 +       if (journal->j_proc_entry) {
610 +               struct proc_dir_entry *p;
611 +               p = create_proc_entry("history", S_IRUGO,
612 +                               journal->j_proc_entry);
613 +               if (p) {
614 +                       p->proc_fops = &jbd_seq_history_fops;
615 +                       p->data = journal;
616 +                       p = create_proc_entry("info", S_IRUGO,
617 +                                               journal->j_proc_entry);
618 +                       if (p) {
619 +                               p->proc_fops = &jbd_seq_info_fops;
620 +                               p->data = journal;
621 +                       }
622 +               }
623 +       }
624 +}
625 +
626 +static void jbd_stats_proc_exit(journal_t *journal)
627 +{
628 +       char name[64];
629 +
630 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
631 +       remove_proc_entry("info", journal->j_proc_entry);
632 +       remove_proc_entry("history", journal->j_proc_entry);
633 +       remove_proc_entry(name, proc_jbd_stats);
634 +}
635 +
636 +static void journal_init_stats(journal_t *journal)
637 +{
638 +       int size;
639 +
640 +       if (proc_jbd_stats == NULL)
641 +               return;
642 +
643 +       journal->j_history_max = 100;
644 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
645 +       journal->j_history = kmalloc(size, GFP_KERNEL);
646 +       if (journal->j_history == NULL) {
647 +               journal->j_history_max = 0;
648 +               return;
649 +       }
650 +       memset(journal->j_history, 0, size);
651 +       spin_lock_init(&journal->j_history_lock);
652 +}
653 +
654  /*
655   * Management for journal control blocks: functions to create and
656   * destroy journal_t structures, and to initialise and read existing
657 @@ -691,6 +986,9 @@ static journal_t * journal_init_common (
658                 kfree(journal);
659                 goto fail;
660         }
661 +       
662 +       journal_init_stats(journal);
663 +
664         return journal;
665  fail:
666         return NULL;
667 @@ -733,6 +1031,7 @@ journal_t * journal_init_dev(struct bloc
668         journal->j_blk_offset = start;
669         journal->j_maxlen = len;
670         journal->j_blocksize = blocksize;
671 +       jbd_stats_proc_init(journal);
672  
673         bh = __getblk(journal->j_dev, start, journal->j_blocksize);
674         J_ASSERT(bh != NULL);
675 @@ -770,6 +1069,7 @@ journal_t * journal_init_inode (struct i
676  
677         journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
678         journal->j_blocksize = inode->i_sb->s_blocksize;
679 +       jbd_stats_proc_init(journal);
680  
681         err = journal_bmap(journal, 0, &blocknr);
682         /* If that failed, give up */
683 @@ -1140,6 +1440,8 @@ void journal_destroy(journal_t *journal)
684                 brelse(journal->j_sb_buffer);
685         }
686  
687 +       if (journal->j_proc_entry)
688 +               jbd_stats_proc_exit(journal);
689         if (journal->j_inode)
690                 iput(journal->j_inode);
691         if (journal->j_revoke)
692 @@ -1896,6 +2198,28 @@ static void __exit remove_jbd_proc_entry
693  
694  #endif
695  
696 +#if defined(CONFIG_PROC_FS)
697 +
698 +#define JBD_STATS_PROC_NAME "fs/jbd"
699 +
700 +static void __init create_jbd_stats_proc_entry(void)
701 +{
702 +       proc_jbd_stats = proc_mkdir(JBD_STATS_PROC_NAME, NULL);
703 +}
704 +
705 +static void __exit remove_jbd_stats_proc_entry(void)
706 +{
707 +       if (proc_jbd_stats)
708 +               remove_proc_entry(JBD_STATS_PROC_NAME, NULL);
709 +}
710 +
711 +#else
712 +
713 +#define create_jbd_stats_proc_entry() do {} while (0)
714 +#define remove_jbd_stats_proc_entry() do {} while (0)
715 +
716 +#endif
717 +
718  kmem_cache_t *jbd_handle_cache;
719  
720  static int __init journal_init_handle_cache(void)
721 @@ -1950,6 +2274,7 @@ static int __init journal_init(void)
722         if (ret != 0)
723                 journal_destroy_caches();
724         create_jbd_proc_entry();
725 +       create_jbd_stats_proc_entry();
726         return ret;
727  }
728  
729 @@ -1961,6 +2286,7 @@ static void __exit journal_exit(void)
730                 printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
731  #endif
732         remove_jbd_proc_entry();
733 +       remove_jbd_stats_proc_entry();
734         journal_destroy_caches();
735  }
736