diff --git a/lustre/ChangeLog b/lustre/ChangeLog
index 41fb85f3e1ebff04e1b4a9d83b7819c50c932dcf..479eb5b4d9b26748486ab216d940ced21cfc9668 100644
--- a/lustre/ChangeLog
+++ b/lustre/ChangeLog
@@ -385,6 +385,10 @@ Details    : When running with O_DIRECT I/O, neither the client rpc_stats nor
              read_ahead_stats were updated. copied the stats section from
              osc_send_oap_rpc() into async_internal().
 
+Severity   : normal
+Bugzilla   : 13454
+Description: Add jbd statistics patch for RHEL5 and 2.6.18-vanilla
+
 
 --------------------------------------------------------------------------------
 
diff --git a/lustre/kernel_patches/patches/jbd-stats-2.6-rhel5.patch b/lustre/kernel_patches/patches/jbd-stats-2.6-rhel5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..50fb6f5a8701df00466202fac5d17f79cc94ef48
--- /dev/null
+++ b/lustre/kernel_patches/patches/jbd-stats-2.6-rhel5.patch
@@ -0,0 +1,744 @@
+Index: linux-2.6.18-8.1.8/include/linux/jbd.h
+===================================================================
+--- linux-2.6.18-8.1.8.orig/include/linux/jbd.h	2007-08-28 22:22:10.000000000 +0200
++++ linux-2.6.18-8.1.8/include/linux/jbd.h	2007-08-28 22:22:29.000000000 +0200
+@@ -455,6 +455,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:
+  *
+@@ -592,6 +602,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]
+ 	 */
+@@ -631,6 +656,57 @@ struct transaction_s 
+ 	struct list_head	t_jcb;
+ };
+ 
++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 JBD_STATS_RUN		1
++#define JBD_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
++jbd_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.
+@@ -884,6 +960,16 @@ struct journal_s
+ 	pid_t			j_last_sync_writer;
+ 
+ 	/*
++	 *
++	 */
++	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.18-8.1.8/fs/jbd/transaction.c
+===================================================================
+--- linux-2.6.18-8.1.8.orig/fs/jbd/transaction.c	2007-08-28 22:22:10.000000000 +0200
++++ linux-2.6.18-8.1.8/fs/jbd/transaction.c	2007-08-28 22:22:29.000000000 +0200
+@@ -60,6 +60,8 @@ get_transaction(journal_t *journal, tran
+ 
+ 	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 = jbd_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.18-8.1.8/fs/jbd/journal.c
+===================================================================
+--- linux-2.6.18-8.1.8.orig/fs/jbd/journal.c	2007-08-28 22:22:10.000000000 +0200
++++ linux-2.6.18-8.1.8/fs/jbd/journal.c	2007-08-28 22:22:29.000000000 +0200
+@@ -36,6 +36,7 @@
+ #include <linux/kthread.h>
+ #include <linux/poison.h>
+ #include <linux/proc_fs.h>
++#include <linux/seq_file.h>
+ 
+ #include <asm/uaccess.h>
+ #include <asm/page.h>
+@@ -639,6 +640,300 @@ struct journal_head *journal_get_descrip
+ 	return journal_add_journal_head(bh);
+ }
+ 
++struct jbd_stats_proc_session {
++	journal_t *journal;
++	struct transaction_stats_s *stats;
++	int start;
++	int max;
++};
++
++static void *jbd_history_skip_empty(struct jbd_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 *jbd_seq_history_start(struct seq_file *seq, loff_t *pos)
++{
++	struct jbd_stats_proc_session *s = seq->private;
++	struct transaction_stats_s *ts;
++	int l = *pos;
++
++	if (l == 0)
++		return SEQ_START_TOKEN;
++	ts = jbd_history_skip_empty(s, s->stats + s->start, 1);
++	if (!ts)
++		return NULL;
++	while (--l && (ts = jbd_history_skip_empty(s, ++ts, 0)) != NULL);
++	return ts;
++}
++
++static void *jbd_seq_history_next(struct seq_file *seq, void *v, loff_t *pos)
++{
++	struct jbd_stats_proc_session *s = seq->private;
++	struct transaction_stats_s *ts = v;
++
++	++*pos;
++	if (v == SEQ_START_TOKEN)
++		return jbd_history_skip_empty(s, s->stats + s->start, 1);
++	else
++		return jbd_history_skip_empty(s, ++ts, 0);
++}
++
++static int jbd_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 == JBD_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 == JBD_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 jbd_seq_history_stop(struct seq_file *seq, void *v)
++{
++}
++
++static struct seq_operations jbd_seq_history_ops = {
++	.start  = jbd_seq_history_start,
++	.next   = jbd_seq_history_next,
++	.stop   = jbd_seq_history_stop,
++	.show   = jbd_seq_history_show,
++};
++
++static int jbd_seq_history_open(struct inode *inode, struct file *file)
++{
++	journal_t *journal = PDE(inode)->data;
++	struct jbd_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, &jbd_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 jbd_seq_history_release(struct inode *inode, struct file *file)
++{
++	struct seq_file *seq = (struct seq_file *)file->private_data;
++	struct jbd_stats_proc_session *s = seq->private;
++	kfree(s->stats);
++	kfree(s);
++	return seq_release(inode, file);
++}
++
++static struct file_operations jbd_seq_history_fops = {
++	.owner		= THIS_MODULE,
++	.open           = jbd_seq_history_open,
++	.read           = seq_read,
++	.llseek         = seq_lseek,
++	.release        = jbd_seq_history_release,
++};
++
++static void *jbd_seq_info_start(struct seq_file *seq, loff_t *pos)
++{
++	return *pos ? NULL : SEQ_START_TOKEN;
++}
++
++static void *jbd_seq_info_next(struct seq_file *seq, void *v, loff_t *pos)
++{
++	return NULL;
++}
++
++static int jbd_seq_info_show(struct seq_file *seq, void *v)
++{
++	struct jbd_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 jbd_seq_info_stop(struct seq_file *seq, void *v)
++{
++}
++
++static struct seq_operations jbd_seq_info_ops = {
++	.start  = jbd_seq_info_start,
++	.next   = jbd_seq_info_next,
++	.stop   = jbd_seq_info_stop,
++	.show   = jbd_seq_info_show,
++};
++
++static int jbd_seq_info_open(struct inode *inode, struct file *file)
++{
++	journal_t *journal = PDE(inode)->data;
++	struct jbd_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, &jbd_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 jbd_seq_info_release(struct inode *inode, struct file *file)
++{
++	struct seq_file *seq = (struct seq_file *)file->private_data;
++	struct jbd_stats_proc_session *s = seq->private;
++	kfree(s->stats);
++	kfree(s);
++	return seq_release(inode, file);
++}
++
++static struct file_operations jbd_seq_info_fops = {
++	.owner		= THIS_MODULE,
++	.open           = jbd_seq_info_open,
++	.read           = seq_read,
++	.llseek         = seq_lseek,
++	.release        = jbd_seq_info_release,
++};
++
++static struct proc_dir_entry *proc_jbd_stats = NULL;
++
++static void jbd_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_jbd_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 = &jbd_seq_history_fops;
++			p->data = journal;
++			p = create_proc_entry("info", S_IRUGO,
++						journal->j_proc_entry);
++			if (p) {
++				p->proc_fops = &jbd_seq_info_fops;
++				p->data = journal;
++			}
++		}
++	}
++}
++
++static void jbd_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_jbd_stats);
++}
++
++static void journal_init_stats(journal_t *journal)
++{
++	int size;
++
++	if (proc_jbd_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
+@@ -681,6 +976,9 @@ static journal_t * journal_init_common (
+ 		kfree(journal);
+ 		goto fail;
+ 	}
++
++	journal_init_stats(journal);
++
+ 	return journal;
+ fail:
+ 	return NULL;
+@@ -724,6 +1022,7 @@ journal_t * journal_init_dev(struct bloc
+ 	journal->j_blk_offset = start;
+ 	journal->j_maxlen = len;
+ 	journal->j_blocksize = blocksize;
++	jbd_stats_proc_init(journal);
+ 
+ 	bh = __getblk(journal->j_dev, start, journal->j_blocksize);
+ 	J_ASSERT(bh != NULL);
+@@ -773,6 +1072,7 @@ journal_t * journal_init_inode (struct i
+ 
+ 	journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
+ 	journal->j_blocksize = inode->i_sb->s_blocksize;
++	jbd_stats_proc_init(journal);
+ 
+ 	/* journal descriptor can store up to n blocks -bzzz */
+ 	n = journal->j_blocksize / sizeof(journal_block_tag_t);
+@@ -1161,6 +1461,8 @@ void journal_destroy(journal_t *journal)
+ 		brelse(journal->j_sb_buffer);
+ 	}
+ 
++	if (journal->j_proc_entry)
++		jbd_stats_proc_exit(journal);
+ 	if (journal->j_inode)
+ 		iput(journal->j_inode);
+ 	if (journal->j_revoke)
+@@ -2027,6 +2329,28 @@ static void __exit remove_jbd_proc_entry
+ 
+ #endif
+ 
++#if defined(CONFIG_PROC_FS)
++
++#define JBD_STATS_PROC_NAME "fs/jbd"
++
++static void __init create_jbd_stats_proc_entry(void)
++{
++	proc_jbd_stats = proc_mkdir(JBD_STATS_PROC_NAME, NULL);
++}
++
++static void __exit remove_jbd_stats_proc_entry(void)
++{
++	if (proc_jbd_stats)
++		remove_proc_entry(JBD_STATS_PROC_NAME, NULL);
++}
++
++#else
++
++#define create_jbd_stats_proc_entry() do {} while (0)
++#define remove_jbd_stats_proc_entry() do {} while (0)
++
++#endif
++
+ kmem_cache_t *jbd_handle_cache;
+ 
+ static int __init journal_init_handle_cache(void)
+@@ -2090,6 +2414,7 @@ static int __init journal_init(void)
+ 	if (ret != 0)
+ 		journal_destroy_caches();
+ 	create_jbd_proc_entry();
++	create_jbd_stats_proc_entry();
+ 	return ret;
+ }
+ 
+@@ -2101,6 +2426,7 @@ static void __exit journal_exit(void)
+ 		printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
+ #endif
+ 	remove_jbd_proc_entry();
++	remove_jbd_stats_proc_entry();
+ 	journal_destroy_caches();
+ }
+ 
+Index: linux-2.6.18-8.1.8/fs/jbd/checkpoint.c
+===================================================================
+--- linux-2.6.18-8.1.8.orig/fs/jbd/checkpoint.c	2007-08-28 22:22:10.000000000 +0200
++++ linux-2.6.18-8.1.8/fs/jbd/checkpoint.c	2007-08-28 22:23:23.000000000 +0200
+@@ -231,7 +231,7 @@ __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;
+@@ -249,6 +249,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);
+ 		log_start_commit(journal, tid);
+@@ -278,6 +279,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);
+@@ -321,6 +323,8 @@ int log_do_checkpoint(journal_t *journal
+ 	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:
+ 	/*
+@@ -345,7 +349,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;
+@@ -667,6 +672,8 @@ void __journal_insert_checkpoint(struct 
+ 
+ void __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 +700,25 @@ void __journal_drop_transaction(journal_
+ 	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 = JBD_STATS_CHECKPOINT;
++		stats.ts_tid = transaction->t_tid;
++		stats.u.chp = transaction->t_chp_stats;
++		if (stats.ts_chp_time)
++			stats.ts_chp_time =
++				jbd_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.18-8.1.8/fs/jbd/commit.c
+===================================================================
+--- linux-2.6.18-8.1.8.orig/fs/jbd/commit.c	2007-08-28 22:22:10.000000000 +0200
++++ linux-2.6.18-8.1.8/fs/jbd/commit.c	2007-08-28 22:22:29.000000000 +0200
+@@ -13,6 +13,7 @@
+  * part of the ext2fs journaling system.
+  */
+ 
++#include <linux/jiffies.h>
+ #include <linux/time.h>
+ #include <linux/fs.h>
+ #include <linux/jbd.h>
+@@ -23,6 +24,7 @@
+ #include <linux/smp_lock.h>
+ #include <linux/crc32.h>
+ 
++
+ /*
+  * Default IO end handler for temporary BJ_IO buffer_heads.
+  */
+@@ -355,6 +357,7 @@ write_out_data:
+  */
+ void 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;
+@@ -403,6 +406,11 @@ void journal_commit_transaction(journal_
+ 	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 = jbd_time_diff(commit_transaction->t_start,
++						stats.ts_locked);
++
+ 	spin_lock(&commit_transaction->t_handle_lock);
+ 	while (commit_transaction->t_updates) {
+ 		DEFINE_WAIT(wait);
+@@ -473,6 +481,9 @@ void journal_commit_transaction(journal_
+ 	 */
+ 	journal_switch_revoke_table(journal);
+ 
++	stats.ts_flushing = CURRENT_MSECS;
++	stats.ts_locked = jbd_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;
+@@ -540,6 +551,11 @@ void journal_commit_transaction(journal_
+ 	 */
+ 	commit_transaction->t_state = T_COMMIT;
+ 
++	stats.ts_logging = CURRENT_MSECS;
++	stats.ts_flushing = jbd_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) {
+@@ -698,6 +714,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. */
+@@ -915,6 +932,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++;
+ 			__journal_remove_checkpoint(jh);
+ 		}
+ 
+@@ -989,6 +1007,36 @@ restart_loop:
+ 
+ 	J_ASSERT(commit_transaction->t_state == T_COMMIT);
+ 
++	commit_transaction->t_start = CURRENT_MSECS;
++	stats.ts_logging = jbd_time_diff(stats.ts_logging,
++					 commit_transaction->t_start);
++
++	/*
++	 * File the transaction for history
++	 */
++	stats.ts_type = JBD_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;
+
diff --git a/lustre/kernel_patches/series/2.6-rhel5.series b/lustre/kernel_patches/series/2.6-rhel5.series
index 076164b54fb1412ad6c0dee02c2ca37f349c2f5d..1d46583cb3a41cefd68f79eb204804044dad5bc3 100644
--- a/lustre/kernel_patches/series/2.6-rhel5.series
+++ b/lustre/kernel_patches/series/2.6-rhel5.series
@@ -9,3 +9,4 @@ export-2.6.18-vanilla.patch
 export-show_task-2.6.18-vanilla.patch
 sd_iostats-2.6-rhel4.patch
 export_symbol_numa-2.6-fc5.patch
+jbd-stats-2.6-rhel5.patch
diff --git a/lustre/kernel_patches/series/2.6.18-vanilla.series b/lustre/kernel_patches/series/2.6.18-vanilla.series
index bca2d165d4966018f6b868f1e3b0ed2fab56fba5..8dabcf6f2b26d4534a02e1b76dde1b5a2acc7312 100644
--- a/lustre/kernel_patches/series/2.6.18-vanilla.series
+++ b/lustre/kernel_patches/series/2.6.18-vanilla.series
@@ -13,3 +13,4 @@ sd_iostats-2.6-rhel4.patch
 export_symbol_numa-2.6.18.patch
 jbd-16tb-overflow-fixes.patch
 jbd-check-for-unmapped-buffer.patch
+jbd-stats-2.6-rhel5.patch