Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / lustre / kernel_patches / patches / jbd-stats-2.6.5.patch
1 Index: linux-2.6.5-7.201/include/linux/jbd.h
2 ===================================================================
3 --- linux-2.6.5-7.201.orig/include/linux/jbd.h  2005-10-11 00:12:48.000000000 +0400
4 +++ linux-2.6.5-7.201/include/linux/jbd.h       2006-07-28 02:40:14.000000000 +0400
5 @@ -411,6 +411,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 @@ -542,6 +552,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 @@ -581,6 +606,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 @@ -817,6 +893,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.5-7.201/fs/jbd/commit.c
120 ===================================================================
121 --- linux-2.6.5-7.201.orig/fs/jbd/commit.c      2005-10-11 00:12:45.000000000 +0400
122 +++ linux-2.6.5-7.201/fs/jbd/commit.c   2006-07-28 02:40:14.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 @@ -366,6 +376,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 = 0;
171         bufs = 0;
172         while (commit_transaction->t_buffers) {
173 @@ -514,6 +529,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 @@ -759,6 +775,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 @@ -805,6 +822,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.5-7.201/fs/jbd/checkpoint.c
227 ===================================================================
228 --- linux-2.6.5-7.201.orig/fs/jbd/checkpoint.c  2005-10-11 00:12:45.000000000 +0400
229 +++ linux-2.6.5-7.201/fs/jbd/checkpoint.c       2006-07-28 02:40:14.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.5-7.201/fs/jbd/transaction.c
310 ===================================================================
311 --- linux-2.6.5-7.201.orig/fs/jbd/transaction.c 2005-10-11 00:12:45.000000000 +0400
312 +++ linux-2.6.5-7.201/fs/jbd/transaction.c      2006-07-28 02:40:14.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.5-7.201/fs/jbd/journal.c
344 ===================================================================
345 --- linux-2.6.5-7.201.orig/fs/jbd/journal.c     2005-10-11 00:12:45.000000000 +0400
346 +++ linux-2.6.5-7.201/fs/jbd/journal.c  2006-08-02 01:20:09.000000000 +0400
347 @@ -35,6 +35,7 @@
348  #include <linux/pagemap.h>
349  #include <asm/uaccess.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 @@ -615,6 +616,337 @@ 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 ssize_t jbd_seq_history_write(struct file *file, const char __user *buf,
485 +                                       size_t len, loff_t * ppos)
486 +{
487 +       journal_t *journal = PDE(file->f_dentry->d_inode)->data;
488 +       int size;
489 +
490 +       if (!capable(CAP_SYS_ADMIN))
491 +               return -EPERM;
492 +
493 +       spin_lock(&journal->j_history_lock);
494 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
495 +       journal->j_history_cur = 0;
496 +       memset(journal->j_history, 0, size);
497 +       spin_unlock(&journal->j_history_lock);
498 +
499 +       return len;
500 +}
501 +
502 +static int jbd_seq_history_release(struct inode *inode, struct file *file)
503 +{
504 +       struct seq_file *seq = (struct seq_file *)file->private_data;
505 +       struct jbd_stats_proc_session *s = seq->private;
506 +       kfree(s->stats);
507 +       kfree(s);
508 +       return seq_release(inode, file);
509 +}
510 +
511 +static struct file_operations jbd_seq_history_fops = {
512 +       .owner          = THIS_MODULE,
513 +       .open           = jbd_seq_history_open,
514 +       .read           = seq_read,
515 +       .write          = jbd_seq_history_write,
516 +       .llseek         = seq_lseek,
517 +       .release        = jbd_seq_history_release,
518 +};
519 +
520 +static void *jbd_seq_info_start(struct seq_file *seq, loff_t *pos)
521 +{
522 +       return *pos ? NULL : SEQ_START_TOKEN;
523 +}
524 +
525 +static void *jbd_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
526 +{
527 +       return NULL;
528 +}
529 +
530 +static int jbd_seq_info_show(struct seq_file *seq, void *v)
531 +{
532 +       struct jbd_stats_proc_session *s = seq->private;
533 +       if (v != SEQ_START_TOKEN)
534 +               return 0;
535 +       seq_printf(seq, "%lu transaction, each upto %u blocks\n",
536 +                       s->stats->ts_tid,
537 +                       s->journal->j_max_transaction_buffers);
538 +       if (s->stats->ts_tid == 0)
539 +               return 0;
540 +       seq_printf(seq, "average: \n  %lums waiting for transaction\n",
541 +                       s->stats->ts_wait / s->stats->ts_tid);
542 +       seq_printf(seq, "  %lums running transaction\n",
543 +                       s->stats->ts_running / s->stats->ts_tid);
544 +       seq_printf(seq, "  %lums transaction was being locked\n",
545 +                       s->stats->ts_locked / s->stats->ts_tid);
546 +       seq_printf(seq, "  %lums flushing data (in ordered mode)\n",
547 +                       s->stats->ts_flushing / s->stats->ts_tid);
548 +       seq_printf(seq, "  %lums logging transaction\n",
549 +                       s->stats->ts_logging / s->stats->ts_tid);
550 +       seq_printf(seq, "  %lu handles per transaction\n",
551 +                       s->stats->ts_handle_count / s->stats->ts_tid);
552 +       seq_printf(seq, "  %lu blocks per transaction\n",
553 +                       s->stats->ts_blocks / s->stats->ts_tid);
554 +       seq_printf(seq, "  %lu logged blocks per transaction\n",
555 +                       s->stats->ts_blocks_logged / s->stats->ts_tid);
556 +       return 0;
557 +}
558 +
559 +static void jbd_seq_info_stop(struct seq_file *seq, void *v)
560 +{
561 +}
562 +
563 +static struct seq_operations jbd_seq_info_ops = {
564 +       .start  = jbd_seq_info_start,
565 +       .next   = jbd_seq_info_next,
566 +       .stop   = jbd_seq_info_stop,
567 +       .show   = jbd_seq_info_show,
568 +};
569 +
570 +static int jbd_seq_info_open(struct inode *inode, struct file *file)
571 +{
572 +       journal_t *journal = PDE(inode)->data;
573 +       struct jbd_stats_proc_session *s;
574 +       int rc, size;
575 +
576 +       s = kmalloc(sizeof(*s), GFP_KERNEL);
577 +       if (s == NULL)
578 +               return -EIO;
579 +       size = sizeof(struct transaction_stats_s);
580 +       s->stats = kmalloc(size, GFP_KERNEL);
581 +       if (s == NULL) {
582 +               kfree(s);
583 +               return -EIO;
584 +       }
585 +       spin_lock(&journal->j_history_lock);
586 +       memcpy(s->stats, &journal->j_stats, size);
587 +       s->journal = journal;
588 +       spin_unlock(&journal->j_history_lock);
589 +       
590 +       rc = seq_open(file, &jbd_seq_info_ops);
591 +       if (rc == 0) {
592 +               struct seq_file *m = (struct seq_file *)file->private_data;
593 +               m->private = s;
594 +       } else {
595 +               kfree(s->stats);
596 +               kfree(s);
597 +       }
598 +       return rc;
599 +
600 +}
601 +
602 +static ssize_t jbd_seq_info_write(struct file *file, const char __user *buf,
603 +                                       size_t len, loff_t * ppos)
604 +{
605 +       journal_t *journal = PDE(file->f_dentry->d_inode)->data;
606 +       int size;
607 +
608 +       if (!capable(CAP_SYS_ADMIN))
609 +               return -EPERM;
610 +
611 +       size = sizeof(struct transaction_stats_s);
612 +       spin_lock(&journal->j_history_lock);
613 +       memset(&journal->j_stats, 0, size);
614 +       spin_unlock(&journal->j_history_lock);
615 +
616 +       return len;
617 +}
618 +
619 +static int jbd_seq_info_release(struct inode *inode, struct file *file)
620 +{
621 +       struct seq_file *seq = (struct seq_file *)file->private_data;
622 +       struct jbd_stats_proc_session *s = seq->private;
623 +       kfree(s->stats);
624 +       kfree(s);
625 +       return seq_release(inode, file);
626 +}
627 +
628 +static struct file_operations jbd_seq_info_fops = {
629 +       .owner          = THIS_MODULE,
630 +       .open           = jbd_seq_info_open,
631 +       .read           = seq_read,
632 +       .write          = jbd_seq_info_write,
633 +       .llseek         = seq_lseek,
634 +       .release        = jbd_seq_info_release,
635 +};
636 +
637 +static struct proc_dir_entry *proc_jbd_stats = NULL;
638 +
639 +static void jbd_stats_proc_init(journal_t *journal)
640 +{
641 +       char name[64];
642 +
643 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
644 +       journal->j_proc_entry = proc_mkdir(name, proc_jbd_stats);
645 +       if (journal->j_proc_entry) {
646 +               struct proc_dir_entry *p;
647 +               p = create_proc_entry("history", S_IRUGO,
648 +                               journal->j_proc_entry);
649 +               if (p) {
650 +                       p->proc_fops = &jbd_seq_history_fops;
651 +                       p->data = journal;
652 +                       p = create_proc_entry("info", S_IRUGO,
653 +                                               journal->j_proc_entry);
654 +                       if (p) {
655 +                               p->proc_fops = &jbd_seq_info_fops;
656 +                               p->data = journal;
657 +                       }
658 +               }
659 +       }
660 +}
661 +
662 +static void jbd_stats_proc_exit(journal_t *journal)
663 +{
664 +       char name[64];
665 +
666 +       snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name));
667 +       remove_proc_entry("info", journal->j_proc_entry);
668 +       remove_proc_entry("history", journal->j_proc_entry);
669 +       remove_proc_entry(name, proc_jbd_stats);
670 +}
671 +
672 +static void journal_init_stats(journal_t *journal)
673 +{
674 +       int size;
675 +
676 +       if (proc_jbd_stats == NULL)
677 +               return;
678 +
679 +       journal->j_history_max = 1500;
680 +       size = sizeof(struct transaction_stats_s) * journal->j_history_max;
681 +       journal->j_history = kmalloc(size, GFP_KERNEL);
682 +       if (journal->j_history == NULL) {
683 +               journal->j_history_max = 0;
684 +               return;
685 +       }
686 +       memset(journal->j_history, 0, size);
687 +       spin_lock_init(&journal->j_history_lock);
688 +}
689 +
690  /*
691   * Management for journal control blocks: functions to create and
692   * destroy journal_t structures, and to initialise and read existing
693 @@ -657,6 +989,9 @@ static journal_t * journal_init_common (
694                 kfree(journal);
695                 goto fail;
696         }
697 +       
698 +       journal_init_stats(journal);
699 +
700         return journal;
701  fail:
702         return NULL;
703 @@ -699,6 +1034,7 @@ journal_t * journal_init_dev(struct bloc
704         journal->j_blk_offset = start;
705         journal->j_maxlen = len;
706         journal->j_blocksize = blocksize;
707 +       jbd_stats_proc_init(journal);
708  
709         bh = __getblk(journal->j_dev, start, journal->j_blocksize);
710         J_ASSERT(bh != NULL);
711 @@ -736,6 +1072,7 @@ journal_t * journal_init_inode (struct i
712  
713         journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
714         journal->j_blocksize = inode->i_sb->s_blocksize;
715 +       jbd_stats_proc_init(journal);
716  
717         err = journal_bmap(journal, 0, &blocknr);
718         /* If that failed, give up */
719 @@ -1106,6 +1443,8 @@ void journal_destroy(journal_t *journal)
720                 brelse(journal->j_sb_buffer);
721         }
722  
723 +       if (journal->j_proc_entry)
724 +               jbd_stats_proc_exit(journal);
725         if (journal->j_inode)
726                 iput(journal->j_inode);
727         if (journal->j_revoke)
728 @@ -1861,6 +2200,28 @@ static void __exit remove_jbd_proc_entry
729  
730  #endif
731  
732 +#if defined(CONFIG_PROC_FS)
733 +
734 +#define JBD_STATS_PROC_NAME "fs/jbd"
735 +
736 +static void __init create_jbd_stats_proc_entry(void)
737 +{
738 +       proc_jbd_stats = proc_mkdir(JBD_STATS_PROC_NAME, NULL);
739 +}
740 +
741 +static void __exit remove_jbd_stats_proc_entry(void)
742 +{
743 +       if (proc_jbd_stats)
744 +               remove_proc_entry(JBD_STATS_PROC_NAME, NULL);
745 +}
746 +
747 +#else
748 +
749 +#define create_jbd_stats_proc_entry() do {} while (0)
750 +#define remove_jbd_stats_proc_entry() do {} while (0)
751 +
752 +#endif
753 +
754  kmem_cache_t *jbd_handle_cache;
755  
756  static int __init journal_init_handle_cache(void)
757 @@ -1915,6 +2276,7 @@ static int __init journal_init(void)
758         if (ret != 0)
759                 journal_destroy_caches();
760         create_jbd_proc_entry();
761 +       create_jbd_stats_proc_entry();
762         return ret;
763  }
764  
765 @@ -1926,6 +2288,7 @@ static void __exit journal_exit(void)
766                 printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
767  #endif
768         remove_jbd_proc_entry();
769 +       remove_jbd_stats_proc_entry();
770         journal_destroy_caches();
771  }
772