From: Johann Lombardi Subject: [PATCH] jbd2 stats through procfs Date: Mon, 2 Apr 2007 19:47:41 +0200 Message-ID: <20070402174741.GA13225@lombardij> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii To: linux-ext4@vger.kernel.org Return-path: Received: from ecfrec.frec.bull.fr ([129.183.4.8]:44271 "EHLO ecfrec.frec.bull.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965441AbXDBRro (ORCPT ); Mon, 2 Apr 2007 13:47:44 -0400 Received: from localhost (localhost [127.0.0.1]) by ecfrec.frec.bull.fr (Postfix) with ESMTP id C6FCC19D916 for ; Mon, 2 Apr 2007 19:47:37 +0200 (CEST) Received: from ecfrec.frec.bull.fr ([127.0.0.1]) by localhost (ecfrec.frec.bull.fr [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 18658-02 for ; Mon, 2 Apr 2007 19:47:34 +0200 (CEST) Received: from ecn002.frec.bull.fr (ecn002.frec.bull.fr [129.183.4.6]) by ecfrec.frec.bull.fr (Postfix) with ESMTP id BABC119D908 for ; Mon, 2 Apr 2007 19:47:33 +0200 (CEST) Received: from johann by lombardij with local (Exim 3.36 #1 (Debian)) id 1HYQd3-0003SK-00 for ; Mon, 02 Apr 2007 19:47:41 +0200 Content-Disposition: inline Sender: linux-ext4-owner@vger.kernel.org List-Id: linux-ext4.vger.kernel.org Hi all, The patch below updates the jbd stats patch to 2.6.20/jbd2. The initial patch was posted by Alex Tomas in December 2005 (http://marc.info/?l=linux-ext4&m=113538565128617&w=2). It provides statistics via procfs such as transaction lifetime and size. Johann Signed-off-by: Johann Lombardi -- Index: linux-2.6.20.4/include/linux/jbd2.h =================================================================== --- linux-2.6.20.4.orig/include/linux/jbd2.h 2007-04-02 13:34:44.000000000 +0200 +++ linux-2.6.20.4/include/linux/jbd2.h 2007-04-02 18:33:17.000000000 +0200 @@ -408,6 +408,16 @@ struct handle_s }; +/* + * Some stats for checkpoint phase + */ +struct transaction_chp_stats_s { + unsigned long cs_chp_time; + unsigned long cs_forced_to_close; + unsigned long cs_written; + unsigned long cs_dropped; +}; + /* The transaction_t type is the guts of the journaling mechanism. It * tracks a compound transaction through its various states: * @@ -543,6 +553,21 @@ struct transaction_s spinlock_t t_handle_lock; /* + * Longest time some handle had to wait for running transaction + */ + unsigned long t_max_wait; + + /* + * When transaction started + */ + unsigned long t_start; + + /* + * Checkpointing stats [j_checkpoint_sem] + */ + struct transaction_chp_stats_s t_chp_stats; + + /* * Number of outstanding updates running on this transaction * [t_handle_lock] */ @@ -573,6 +598,57 @@ struct transaction_s }; +struct transaction_run_stats_s { + unsigned long rs_wait; + unsigned long rs_running; + unsigned long rs_locked; + unsigned long rs_flushing; + unsigned long rs_logging; + + unsigned long rs_handle_count; + unsigned long rs_blocks; + unsigned long rs_blocks_logged; +}; + +struct transaction_stats_s +{ + int ts_type; + unsigned long ts_tid; + union { + struct transaction_run_stats_s run; + struct transaction_chp_stats_s chp; + } u; +}; + +#define JBD2_STATS_RUN 1 +#define JBD2_STATS_CHECKPOINT 2 + +#define ts_wait u.run.rs_wait +#define ts_running u.run.rs_running +#define ts_locked u.run.rs_locked +#define ts_flushing u.run.rs_flushing +#define ts_logging u.run.rs_logging +#define ts_handle_count u.run.rs_handle_count +#define ts_blocks u.run.rs_blocks +#define ts_blocks_logged u.run.rs_blocks_logged + +#define ts_chp_time u.chp.cs_chp_time +#define ts_forced_to_close u.chp.cs_forced_to_close +#define ts_written u.chp.cs_written +#define ts_dropped u.chp.cs_dropped + +#define CURRENT_MSECS (jiffies_to_msecs(jiffies)) + +static inline unsigned int +jbd2_time_diff(unsigned int start, unsigned int end) +{ + if (unlikely(start > end)) + end = end + (~0UL - start); + else + end -= start; + return end; +} + /** * struct journal_s - The journal_s type is the concrete type associated with * journal_t. @@ -634,6 +710,12 @@ struct transaction_s * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the * number that will fit in j_blocksize * @j_last_sync_writer: most recent pid which did a synchronous write + * @j_history: Buffer storing the transactions statistics history + * @j_history_max: Maximum number of transactions in the statistics history + * @j_history_cur: Current number of transactions in the statistics history + * @j_history_lock: Protect the transactions statistics history + * @j_proc_entry: procfs entry for the jbd statistics directory + * @j_stats: Overall statistics * @j_private: An opaque pointer to fs-private information. */ @@ -826,6 +908,16 @@ struct journal_s pid_t j_last_sync_writer; /* + * Journal statistics + */ + struct transaction_stats_s *j_history; + int j_history_max; + int j_history_cur; + spinlock_t j_history_lock; + struct proc_dir_entry *j_proc_entry; + struct transaction_stats_s j_stats; + + /* * An opaque pointer to fs-private information. ext3 puts its * superblock pointer here */ Index: linux-2.6.20.4/fs/jbd2/transaction.c =================================================================== --- linux-2.6.20.4.orig/fs/jbd2/transaction.c 2007-04-02 13:34:44.000000000 +0200 +++ linux-2.6.20.4/fs/jbd2/transaction.c 2007-04-02 13:35:49.000000000 +0200 @@ -60,6 +60,8 @@ jbd2_get_transaction(journal_t *journal, J_ASSERT(journal->j_running_transaction == NULL); journal->j_running_transaction = transaction; + transaction->t_max_wait = 0; + transaction->t_start = CURRENT_MSECS; return transaction; } @@ -86,6 +88,7 @@ static int start_this_handle(journal_t * int nblocks = handle->h_buffer_credits; transaction_t *new_transaction = NULL; int ret = 0; + unsigned long ts = CURRENT_MSECS; if (nblocks > journal->j_max_transaction_buffers) { printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", @@ -219,6 +222,12 @@ repeat_locked: /* OK, account for the buffers that this operation expects to * use and add the handle to the running transaction. */ + if (time_after(transaction->t_start, ts)) { + ts = jbd2_time_diff(ts, transaction->t_start); + if (ts > transaction->t_max_wait) + transaction->t_max_wait= ts; + } + handle->h_transaction = transaction; transaction->t_outstanding_credits += nblocks; transaction->t_updates++; Index: linux-2.6.20.4/fs/jbd2/checkpoint.c =================================================================== --- linux-2.6.20.4.orig/fs/jbd2/checkpoint.c 2007-04-02 13:34:44.000000000 +0200 +++ linux-2.6.20.4/fs/jbd2/checkpoint.c 2007-04-02 13:35:49.000000000 +0200 @@ -232,7 +232,8 @@ __flush_batch(journal_t *journal, struct * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it */ static int __process_buffer(journal_t *journal, struct journal_head *jh, - struct buffer_head **bhs, int *batch_count) + struct buffer_head **bhs, int *batch_count, + transaction_t *transaction) { struct buffer_head *bh = jh2bh(jh); int ret = 0; @@ -250,6 +251,7 @@ static int __process_buffer(journal_t *j transaction_t *t = jh->b_transaction; tid_t tid = t->t_tid; + transaction->t_chp_stats.cs_forced_to_close++; spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); jbd2_log_start_commit(journal, tid); @@ -279,6 +281,7 @@ static int __process_buffer(journal_t *j bhs[*batch_count] = bh; __buffer_relink_io(jh); jbd_unlock_bh_state(bh); + transaction->t_chp_stats.cs_written++; (*batch_count)++; if (*batch_count == NR_BATCH) { spin_unlock(&journal->j_list_lock); @@ -322,6 +325,8 @@ int jbd2_log_do_checkpoint(journal_t *jo if (!journal->j_checkpoint_transactions) goto out; transaction = journal->j_checkpoint_transactions; + if (transaction->t_chp_stats.cs_chp_time == 0) + transaction->t_chp_stats.cs_chp_time = CURRENT_MSECS; this_tid = transaction->t_tid; restart: /* @@ -346,7 +351,8 @@ restart: retry = 1; break; } - retry = __process_buffer(journal, jh, bhs,&batch_count); + retry = __process_buffer(journal, jh, bhs, &batch_count, + transaction); if (!retry && lock_need_resched(&journal->j_list_lock)){ spin_unlock(&journal->j_list_lock); retry = 1; @@ -668,6 +674,8 @@ void __jbd2_journal_insert_checkpoint(st void __jbd2_journal_drop_transaction(journal_t *journal, transaction_t *transaction) { + struct transaction_stats_s stats; + assert_spin_locked(&journal->j_list_lock); if (transaction->t_cpnext) { transaction->t_cpnext->t_cpprev = transaction->t_cpprev; @@ -693,5 +701,25 @@ void __jbd2_journal_drop_transaction(jou J_ASSERT(journal->j_running_transaction != transaction); jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid); + + /* + * File the transaction for history + */ + if (transaction->t_chp_stats.cs_written != 0 || + transaction->t_chp_stats.cs_chp_time != 0) { + stats.ts_type = JBD2_STATS_CHECKPOINT; + stats.ts_tid = transaction->t_tid; + stats.u.chp = transaction->t_chp_stats; + if (stats.ts_chp_time) + stats.ts_chp_time = + jbd2_time_diff(stats.ts_chp_time, CURRENT_MSECS); + spin_lock(&journal->j_history_lock); + memcpy(journal->j_history + journal->j_history_cur, &stats, + sizeof(stats)); + if (++journal->j_history_cur == journal->j_history_max) + journal->j_history_cur = 0; + spin_unlock(&journal->j_history_lock); + } + kfree(transaction); } Index: linux-2.6.20.4/fs/jbd2/commit.c =================================================================== --- linux-2.6.20.4.orig/fs/jbd2/commit.c 2007-04-02 13:34:44.000000000 +0200 +++ linux-2.6.20.4/fs/jbd2/commit.c 2007-04-02 13:35:49.000000000 +0200 @@ -21,6 +21,7 @@ #include #include #include +#include /* * Default IO end handler for temporary BJ_IO buffer_heads. @@ -291,6 +292,7 @@ static inline void write_tag_block(int t */ void jbd2_journal_commit_transaction(journal_t *journal) { + struct transaction_stats_s stats; transaction_t *commit_transaction; struct journal_head *jh, *new_jh, *descriptor; struct buffer_head **wbuf = journal->j_wbuf; @@ -338,6 +340,11 @@ void jbd2_journal_commit_transaction(jou spin_lock(&journal->j_state_lock); commit_transaction->t_state = T_LOCKED; + stats.ts_wait = commit_transaction->t_max_wait; + stats.ts_locked = CURRENT_MSECS; + stats.ts_running = jbd2_time_diff(commit_transaction->t_start, + stats.ts_locked); + spin_lock(&commit_transaction->t_handle_lock); while (commit_transaction->t_updates) { DEFINE_WAIT(wait); @@ -408,6 +415,9 @@ void jbd2_journal_commit_transaction(jou */ jbd2_journal_switch_revoke_table(journal); + stats.ts_flushing = CURRENT_MSECS; + stats.ts_locked = jbd2_time_diff(stats.ts_locked, stats.ts_flushing); + commit_transaction->t_state = T_FLUSH; journal->j_committing_transaction = commit_transaction; journal->j_running_transaction = NULL; @@ -499,6 +509,11 @@ void jbd2_journal_commit_transaction(jou */ commit_transaction->t_state = T_COMMIT; + stats.ts_logging = CURRENT_MSECS; + stats.ts_flushing = jbd2_time_diff(stats.ts_flushing, stats.ts_logging); + stats.ts_blocks = commit_transaction->t_outstanding_credits; + stats.ts_blocks_logged = 0; + descriptor = NULL; bufs = 0; while (commit_transaction->t_buffers) { @@ -647,6 +662,7 @@ start_journal_io: submit_bh(WRITE, bh); } cond_resched(); + stats.ts_blocks_logged += bufs; /* Force a new descriptor to be generated next time round the loop. */ @@ -817,6 +833,7 @@ restart_loop: cp_transaction = jh->b_cp_transaction; if (cp_transaction) { JBUFFER_TRACE(jh, "remove from old cp transaction"); + cp_transaction->t_chp_stats.cs_dropped++; __jbd2_journal_remove_checkpoint(jh); } @@ -891,6 +908,36 @@ restart_loop: J_ASSERT(commit_transaction->t_state == T_COMMIT); + commit_transaction->t_start = CURRENT_MSECS; + stats.ts_logging = jbd2_time_diff(stats.ts_logging, + commit_transaction->t_start); + + /* + * File the transaction for history + */ + stats.ts_type = JBD2_STATS_RUN; + stats.ts_tid = commit_transaction->t_tid; + stats.ts_handle_count = commit_transaction->t_handle_count; + spin_lock(&journal->j_history_lock); + memcpy(journal->j_history + journal->j_history_cur, &stats, + sizeof(stats)); + if (++journal->j_history_cur == journal->j_history_max) + journal->j_history_cur = 0; + + /* + * Calculate overall stats + */ + journal->j_stats.ts_tid++; + journal->j_stats.ts_wait += stats.ts_wait; + journal->j_stats.ts_running += stats.ts_running; + journal->j_stats.ts_locked += stats.ts_locked; + journal->j_stats.ts_flushing += stats.ts_flushing; + journal->j_stats.ts_logging += stats.ts_logging; + journal->j_stats.ts_handle_count += stats.ts_handle_count; + journal->j_stats.ts_blocks += stats.ts_blocks; + journal->j_stats.ts_blocks_logged += stats.ts_blocks_logged; + spin_unlock(&journal->j_history_lock); + commit_transaction->t_state = T_FINISHED; J_ASSERT(commit_transaction == journal->j_committing_transaction); journal->j_commit_sequence = commit_transaction->t_tid; Index: linux-2.6.20.4/fs/jbd2/journal.c =================================================================== --- linux-2.6.20.4.orig/fs/jbd2/journal.c 2007-04-02 13:34:44.000000000 +0200 +++ linux-2.6.20.4/fs/jbd2/journal.c 2007-04-02 13:35:49.000000000 +0200 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -635,6 +636,300 @@ struct journal_head *jbd2_journal_get_de return jbd2_journal_add_journal_head(bh); } +struct jbd2_stats_proc_session { + journal_t *journal; + struct transaction_stats_s *stats; + int start; + int max; +}; + +static void *jbd2_history_skip_empty(struct jbd2_stats_proc_session *s, + struct transaction_stats_s *ts, + int first) +{ + if (ts == s->stats + s->max) + ts = s->stats; + if (!first && ts == s->stats + s->start) + return NULL; + while (ts->ts_type == 0) { + ts++; + if (ts == s->stats + s->max) + ts = s->stats; + if (ts == s->stats + s->start) + return NULL; + } + return ts; + +} + +static void *jbd2_seq_history_start(struct seq_file *seq, loff_t *pos) +{ + struct jbd2_stats_proc_session *s = seq->private; + struct transaction_stats_s *ts; + int l = *pos; + + if (l == 0) + return SEQ_START_TOKEN; + ts = jbd2_history_skip_empty(s, s->stats + s->start, 1); + if (!ts) + return NULL; + while (--l && (ts = jbd2_history_skip_empty(s, ++ts, 0)) != NULL); + return ts; +} + +static void *jbd2_seq_history_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct jbd2_stats_proc_session *s = seq->private; + struct transaction_stats_s *ts = v; + + ++*pos; + if (v == SEQ_START_TOKEN) + return jbd2_history_skip_empty(s, s->stats + s->start, 1); + else + return jbd2_history_skip_empty(s, ++ts, 0); +} + +static int jbd2_seq_history_show(struct seq_file *seq, void *v) +{ + struct transaction_stats_s *ts = v; + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "%-4s %-5s %-5s %-5s %-5s %-5s %-5s %-6s %-5s " + "%-5s %-5s %-5s %-5s %-5s\n", "R/C", "tid", + "wait", "run", "lock", "flush", "log", "hndls", + "block", "inlog", "ctime", "write", "drop", + "close"); + return 0; + } + if (ts->ts_type == JBD2_STATS_RUN) + seq_printf(seq, "%-4s %-5lu %-5lu %-5lu %-5lu %-5lu %-5lu " + "%-6lu %-5lu %-5lu\n", "R", ts->ts_tid, + ts->ts_wait, ts->ts_running, ts->ts_locked, + ts->ts_flushing, ts->ts_logging, + ts->ts_handle_count, ts->ts_blocks, + ts->ts_blocks_logged); + else if (ts->ts_type == JBD2_STATS_CHECKPOINT) + seq_printf(seq, "%-4s %-5lu %48s %-5lu %-5lu %-5lu %-5lu\n", + "C", ts->ts_tid, " ", ts->ts_chp_time, + ts->ts_written, ts->ts_dropped, + ts->ts_forced_to_close); + else + J_ASSERT(0); + return 0; +} + +static void jbd2_seq_history_stop(struct seq_file *seq, void *v) +{ +} + +static struct seq_operations jbd2_seq_history_ops = { + .start = jbd2_seq_history_start, + .next = jbd2_seq_history_next, + .stop = jbd2_seq_history_stop, + .show = jbd2_seq_history_show, +}; + +static int jbd2_seq_history_open(struct inode *inode, struct file *file) +{ + journal_t *journal = PDE(inode)->data; + struct jbd2_stats_proc_session *s; + int rc, size; + + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return -EIO; + size = sizeof(struct transaction_stats_s) * journal->j_history_max; + s->stats = kmalloc(size, GFP_KERNEL); + if (s == NULL) { + kfree(s); + return -EIO; + } + spin_lock(&journal->j_history_lock); + memcpy(s->stats, journal->j_history, size); + s->max = journal->j_history_max; + s->start = journal->j_history_cur % s->max; + spin_unlock(&journal->j_history_lock); + + rc = seq_open(file, &jbd2_seq_history_ops); + if (rc == 0) { + struct seq_file *m = (struct seq_file *)file->private_data; + m->private = s; + } else { + kfree(s->stats); + kfree(s); + } + return rc; + +} + +static int jbd2_seq_history_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = (struct seq_file *)file->private_data; + struct jbd2_stats_proc_session *s = seq->private; + kfree(s->stats); + kfree(s); + return seq_release(inode, file); +} + +static struct file_operations jbd2_seq_history_fops = { + .owner = THIS_MODULE, + .open = jbd2_seq_history_open, + .read = seq_read, + .llseek = seq_lseek, + .release = jbd2_seq_history_release, +}; + +static void *jbd2_seq_info_start(struct seq_file *seq, loff_t *pos) +{ + return *pos ? NULL : SEQ_START_TOKEN; +} + +static void *jbd2_seq_info_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return NULL; +} + +static int jbd2_seq_info_show(struct seq_file *seq, void *v) +{ + struct jbd2_stats_proc_session *s = seq->private; + if (v != SEQ_START_TOKEN) + return 0; + seq_printf(seq, "%lu transaction, each upto %u blocks\n", + s->stats->ts_tid, + s->journal->j_max_transaction_buffers); + if (s->stats->ts_tid == 0) + return 0; + seq_printf(seq, "average: \n %lums waiting for transaction\n", + s->stats->ts_wait / s->stats->ts_tid); + seq_printf(seq, " %lums running transaction\n", + s->stats->ts_running / s->stats->ts_tid); + seq_printf(seq, " %lums transaction was being locked\n", + s->stats->ts_locked / s->stats->ts_tid); + seq_printf(seq, " %lums flushing data (in ordered mode)\n", + s->stats->ts_flushing / s->stats->ts_tid); + seq_printf(seq, " %lums logging transaction\n", + s->stats->ts_logging / s->stats->ts_tid); + seq_printf(seq, " %lu handles per transaction\n", + s->stats->ts_handle_count / s->stats->ts_tid); + seq_printf(seq, " %lu blocks per transaction\n", + s->stats->ts_blocks / s->stats->ts_tid); + seq_printf(seq, " %lu logged blocks per transaction\n", + s->stats->ts_blocks_logged / s->stats->ts_tid); + return 0; +} + +static void jbd2_seq_info_stop(struct seq_file *seq, void *v) +{ +} + +static struct seq_operations jbd2_seq_info_ops = { + .start = jbd2_seq_info_start, + .next = jbd2_seq_info_next, + .stop = jbd2_seq_info_stop, + .show = jbd2_seq_info_show, +}; + +static int jbd2_seq_info_open(struct inode *inode, struct file *file) +{ + journal_t *journal = PDE(inode)->data; + struct jbd2_stats_proc_session *s; + int rc, size; + + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return -EIO; + size = sizeof(struct transaction_stats_s); + s->stats = kmalloc(size, GFP_KERNEL); + if (s == NULL) { + kfree(s); + return -EIO; + } + spin_lock(&journal->j_history_lock); + memcpy(s->stats, &journal->j_stats, size); + s->journal = journal; + spin_unlock(&journal->j_history_lock); + + rc = seq_open(file, &jbd2_seq_info_ops); + if (rc == 0) { + struct seq_file *m = (struct seq_file *)file->private_data; + m->private = s; + } else { + kfree(s->stats); + kfree(s); + } + return rc; + +} + +static int jbd2_seq_info_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = (struct seq_file *)file->private_data; + struct jbd2_stats_proc_session *s = seq->private; + kfree(s->stats); + kfree(s); + return seq_release(inode, file); +} + +static struct file_operations jbd2_seq_info_fops = { + .owner = THIS_MODULE, + .open = jbd2_seq_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = jbd2_seq_info_release, +}; + +static struct proc_dir_entry *proc_jbd2_stats = NULL; + +static void jbd2_stats_proc_init(journal_t *journal) +{ + char name[64]; + + snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name)); + journal->j_proc_entry = proc_mkdir(name, proc_jbd2_stats); + if (journal->j_proc_entry) { + struct proc_dir_entry *p; + p = create_proc_entry("history", S_IRUGO, + journal->j_proc_entry); + if (p) { + p->proc_fops = &jbd2_seq_history_fops; + p->data = journal; + p = create_proc_entry("info", S_IRUGO, + journal->j_proc_entry); + if (p) { + p->proc_fops = &jbd2_seq_info_fops; + p->data = journal; + } + } + } +} + +static void jbd2_stats_proc_exit(journal_t *journal) +{ + char name[64]; + + snprintf(name, sizeof(name) - 1, "%s", bdevname(journal->j_dev, name)); + remove_proc_entry("info", journal->j_proc_entry); + remove_proc_entry("history", journal->j_proc_entry); + remove_proc_entry(name, proc_jbd2_stats); +} + +static void journal_init_stats(journal_t *journal) +{ + int size; + + if (proc_jbd2_stats == NULL) + return; + + journal->j_history_max = 100; + size = sizeof(struct transaction_stats_s) * journal->j_history_max; + journal->j_history = kmalloc(size, GFP_KERNEL); + if (journal->j_history == NULL) { + journal->j_history_max = 0; + return; + } + memset(journal->j_history, 0, size); + spin_lock_init(&journal->j_history_lock); +} + /* * Management for journal control blocks: functions to create and * destroy journal_t structures, and to initialise and read existing @@ -677,6 +972,9 @@ static journal_t * journal_init_common ( kfree(journal); goto fail; } + + journal_init_stats(journal); + return journal; fail: return NULL; @@ -727,6 +1025,7 @@ journal_t * jbd2_journal_init_dev(struct journal = NULL; goto out; } + jbd2_stats_proc_init(journal); journal->j_dev = bdev; journal->j_fs_dev = fs_dev; journal->j_blk_offset = start; @@ -769,6 +1068,7 @@ journal_t * jbd2_journal_init_inode (str journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits; journal->j_blocksize = inode->i_sb->s_blocksize; + jbd2_stats_proc_init(journal); /* journal descriptor can store up to n blocks -bzzz */ n = journal->j_blocksize / sizeof(journal_block_tag_t); @@ -1157,6 +1457,8 @@ void jbd2_journal_destroy(journal_t *jou brelse(journal->j_sb_buffer); } + if (journal->j_proc_entry) + jbd2_stats_proc_exit(journal); if (journal->j_inode) iput(journal->j_inode); if (journal->j_revoke) @@ -2007,6 +2309,28 @@ static void __exit jbd2_remove_jbd_proc_ #endif +#if defined(CONFIG_PROC_FS) + +#define JBD2_STATS_PROC_NAME "fs/jbd2" + +static void __init jbd2_create_jbd_stats_proc_entry(void) +{ + proc_jbd2_stats = proc_mkdir(JBD2_STATS_PROC_NAME, NULL); +} + +static void __exit jbd2_remove_jbd_stats_proc_entry(void) +{ + if (proc_jbd2_stats) + remove_proc_entry(JBD2_STATS_PROC_NAME, NULL); +} + +#else + +#define jbd2_create_jbd_stats_proc_entry() do {} while (0) +#define jbd2_remove_jbd_stats_proc_entry() do {} while (0) + +#endif + struct kmem_cache *jbd2_handle_cache; static int __init journal_init_handle_cache(void) @@ -2064,6 +2388,7 @@ static int __init journal_init(void) if (ret != 0) jbd2_journal_destroy_caches(); create_jbd_proc_entry(); + jbd2_create_jbd_stats_proc_entry(); return ret; } @@ -2075,6 +2400,7 @@ static void __exit journal_exit(void) printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n); #endif jbd2_remove_jbd_proc_entry(); + jbd2_remove_jbd_stats_proc_entry(); jbd2_journal_destroy_caches(); }