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