Commit 8e85fb3f authored by Johann Lombardi's avatar Johann Lombardi Committed by Theodore Ts'o

jbd2: jbd2 stats through procfs

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.

Sometimes, investigating performance problems, i find useful to have
stats from jbd about transaction's lifetime, size, etc. here is a
patch for review and inclusion probably.

for example, stats after creation of 3M files in htree directory:

[root@bob ~]# cat /proc/fs/jbd/sda/history
R/C  tid   wait  run   lock  flush log   hndls  block inlog ctime write drop  close
R    261   8260  2720  0     0     750   9892   8170  8187
C    259                                                    750   0     4885  1
R    262   20    2200  10    0     770   9836   8170  8187
R    263   30    2200  10    0     3070  9812   8170  8187
R    264   0     5000  10    0     1340  0      0     0
C    261                                                    8240  3212  4957  0
R    265   8260  1470  0     0     4640  9854   8170  8187
R    266   0     5000  10    0     1460  0      0     0
C    262                                                    8210  2989  4868  0
R    267   8230  1490  10    0     4440  9875   8171  8188
R    268   0     5000  10    0     1260  0      0     0
C    263                                                    7710  2937  4908  0
R    269   7730  1470  10    0     3330  9841   8170  8187
R    270   0     5000  10    0     830   0      0     0
C    265                                                    8140  3234  4898  0
C    267                                                    720   0     4849  1
R    271   8630  2740  20    0     740   9819   8170  8187
C    269                                                    800   0     4214  1
R    272   40    2170  10    0     830   9716   8170  8187
R    273   40    2280  0     0     3530  9799   8170  8187
R    274   0     5000  10    0     990   0      0     0


where,

R     - line for transaction's life from T_RUNNING to T_FINISHED
C     - line for transaction's checkpointing
tid   - transaction's id
wait  - for how long we were waiting for new transaction to start
         (the longest period journal_start() took in this transaction)
run   - real transaction's lifetime (from T_RUNNING to T_LOCKED
lock  - how long we were waiting for all handles to close
         (time the transaction was in T_LOCKED)
flush - how long it took to flush all data (data=ordered)
log   - how long it took to write the transaction to the log
hndls - how many handles got to the transaction
block - how many blocks got to the transaction
inlog - how many blocks are written to the log (block + descriptors)
ctime - how long it took to checkpoint the transaction
write - how many blocks have been written during checkpointing
drop  - how many blocks have been dropped during checkpointing
close - how many running transactions have been closed to checkpoint this one

all times are in msec.


[root@bob ~]# cat /proc/fs/jbd/sda/info
280 transaction, each upto 8192 blocks
average:
  1633ms waiting for transaction
  3616ms running transaction
  5ms transaction was being locked
  1ms flushing data (in ordered mode)
  1799ms logging transaction
  11781 handles per transaction
  5629 blocks per transaction
  5641 logged blocks per transaction
Signed-off-by: default avatarJohann Lombardi <johann.lombardi@bull.net>
Signed-off-by: default avatarMariusz Kozlowski <m.kozlowski@tuxland.pl>
Signed-off-by: default avatarMingming Cao <cmm@us.ibm.com>
Signed-off-by: default avatarEric Sandeen <sandeen@redhat.com>
parent 4df3d265
...@@ -232,7 +232,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count) ...@@ -232,7 +232,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count)
* Called under jbd_lock_bh_state(jh2bh(jh)), and drops it * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it
*/ */
static int __process_buffer(journal_t *journal, struct journal_head *jh, 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); struct buffer_head *bh = jh2bh(jh);
int ret = 0; int ret = 0;
...@@ -250,6 +251,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, ...@@ -250,6 +251,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
transaction_t *t = jh->b_transaction; transaction_t *t = jh->b_transaction;
tid_t tid = t->t_tid; tid_t tid = t->t_tid;
transaction->t_chp_stats.cs_forced_to_close++;
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_log_start_commit(journal, tid); jbd2_log_start_commit(journal, tid);
...@@ -279,6 +281,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, ...@@ -279,6 +281,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
bhs[*batch_count] = bh; bhs[*batch_count] = bh;
__buffer_relink_io(jh); __buffer_relink_io(jh);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
transaction->t_chp_stats.cs_written++;
(*batch_count)++; (*batch_count)++;
if (*batch_count == NR_BATCH) { if (*batch_count == NR_BATCH) {
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
...@@ -322,6 +325,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) ...@@ -322,6 +325,8 @@ int jbd2_log_do_checkpoint(journal_t *journal)
if (!journal->j_checkpoint_transactions) if (!journal->j_checkpoint_transactions)
goto out; goto out;
transaction = journal->j_checkpoint_transactions; transaction = journal->j_checkpoint_transactions;
if (transaction->t_chp_stats.cs_chp_time == 0)
transaction->t_chp_stats.cs_chp_time = jiffies;
this_tid = transaction->t_tid; this_tid = transaction->t_tid;
restart: restart:
/* /*
...@@ -346,7 +351,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) ...@@ -346,7 +351,8 @@ int jbd2_log_do_checkpoint(journal_t *journal)
retry = 1; retry = 1;
break; 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)){ if (!retry && lock_need_resched(&journal->j_list_lock)){
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
retry = 1; retry = 1;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/jiffies.h>
/* /*
* Default IO end handler for temporary BJ_IO buffer_heads. * Default IO end handler for temporary BJ_IO buffer_heads.
...@@ -290,6 +291,7 @@ static inline void write_tag_block(int tag_bytes, journal_block_tag_t *tag, ...@@ -290,6 +291,7 @@ static inline void write_tag_block(int tag_bytes, journal_block_tag_t *tag,
*/ */
void jbd2_journal_commit_transaction(journal_t *journal) void jbd2_journal_commit_transaction(journal_t *journal)
{ {
struct transaction_stats_s stats;
transaction_t *commit_transaction; transaction_t *commit_transaction;
struct journal_head *jh, *new_jh, *descriptor; struct journal_head *jh, *new_jh, *descriptor;
struct buffer_head **wbuf = journal->j_wbuf; struct buffer_head **wbuf = journal->j_wbuf;
...@@ -337,6 +339,11 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -337,6 +339,11 @@ void jbd2_journal_commit_transaction(journal_t *journal)
spin_lock(&journal->j_state_lock); spin_lock(&journal->j_state_lock);
commit_transaction->t_state = T_LOCKED; commit_transaction->t_state = T_LOCKED;
stats.u.run.rs_wait = commit_transaction->t_max_wait;
stats.u.run.rs_locked = jiffies;
stats.u.run.rs_running = jbd2_time_diff(commit_transaction->t_start,
stats.u.run.rs_locked);
spin_lock(&commit_transaction->t_handle_lock); spin_lock(&commit_transaction->t_handle_lock);
while (commit_transaction->t_updates) { while (commit_transaction->t_updates) {
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
...@@ -407,6 +414,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -407,6 +414,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
*/ */
jbd2_journal_switch_revoke_table(journal); jbd2_journal_switch_revoke_table(journal);
stats.u.run.rs_flushing = jiffies;
stats.u.run.rs_locked = jbd2_time_diff(stats.u.run.rs_locked,
stats.u.run.rs_flushing);
commit_transaction->t_state = T_FLUSH; commit_transaction->t_state = T_FLUSH;
journal->j_committing_transaction = commit_transaction; journal->j_committing_transaction = commit_transaction;
journal->j_running_transaction = NULL; journal->j_running_transaction = NULL;
...@@ -498,6 +509,12 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -498,6 +509,12 @@ void jbd2_journal_commit_transaction(journal_t *journal)
*/ */
commit_transaction->t_state = T_COMMIT; commit_transaction->t_state = T_COMMIT;
stats.u.run.rs_logging = jiffies;
stats.u.run.rs_flushing = jbd2_time_diff(stats.u.run.rs_flushing,
stats.u.run.rs_logging);
stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits;
stats.u.run.rs_blocks_logged = 0;
descriptor = NULL; descriptor = NULL;
bufs = 0; bufs = 0;
while (commit_transaction->t_buffers) { while (commit_transaction->t_buffers) {
...@@ -646,6 +663,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -646,6 +663,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
submit_bh(WRITE, bh); submit_bh(WRITE, bh);
} }
cond_resched(); cond_resched();
stats.u.run.rs_blocks_logged += bufs;
/* Force a new descriptor to be generated next /* Force a new descriptor to be generated next
time round the loop. */ time round the loop. */
...@@ -816,6 +834,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -816,6 +834,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
cp_transaction = jh->b_cp_transaction; cp_transaction = jh->b_cp_transaction;
if (cp_transaction) { if (cp_transaction) {
JBUFFER_TRACE(jh, "remove from old cp transaction"); JBUFFER_TRACE(jh, "remove from old cp transaction");
cp_transaction->t_chp_stats.cs_dropped++;
__jbd2_journal_remove_checkpoint(jh); __jbd2_journal_remove_checkpoint(jh);
} }
...@@ -890,6 +909,36 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -890,6 +909,36 @@ void jbd2_journal_commit_transaction(journal_t *journal)
J_ASSERT(commit_transaction->t_state == T_COMMIT); J_ASSERT(commit_transaction->t_state == T_COMMIT);
commit_transaction->t_start = jiffies;
stats.u.run.rs_logging = jbd2_time_diff(stats.u.run.rs_logging,
commit_transaction->t_start);
/*
* File the transaction for history
*/
stats.ts_type = JBD2_STATS_RUN;
stats.ts_tid = commit_transaction->t_tid;
stats.u.run.rs_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.u.run.rs_wait += stats.u.run.rs_wait;
journal->j_stats.u.run.rs_running += stats.u.run.rs_running;
journal->j_stats.u.run.rs_locked += stats.u.run.rs_locked;
journal->j_stats.u.run.rs_flushing += stats.u.run.rs_flushing;
journal->j_stats.u.run.rs_logging += stats.u.run.rs_logging;
journal->j_stats.u.run.rs_handle_count += stats.u.run.rs_handle_count;
journal->j_stats.u.run.rs_blocks += stats.u.run.rs_blocks;
journal->j_stats.u.run.rs_blocks_logged += stats.u.run.rs_blocks_logged;
spin_unlock(&journal->j_history_lock);
commit_transaction->t_state = T_FINISHED; commit_transaction->t_state = T_FINISHED;
J_ASSERT(commit_transaction == journal->j_committing_transaction); J_ASSERT(commit_transaction == journal->j_committing_transaction);
journal->j_commit_sequence = commit_transaction->t_tid; journal->j_commit_sequence = commit_transaction->t_tid;
......
This diff is collapsed.
...@@ -59,6 +59,8 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) ...@@ -59,6 +59,8 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction)
J_ASSERT(journal->j_running_transaction == NULL); J_ASSERT(journal->j_running_transaction == NULL);
journal->j_running_transaction = transaction; journal->j_running_transaction = transaction;
transaction->t_max_wait = 0;
transaction->t_start = jiffies;
return transaction; return transaction;
} }
...@@ -85,6 +87,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle) ...@@ -85,6 +87,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle)
int nblocks = handle->h_buffer_credits; int nblocks = handle->h_buffer_credits;
transaction_t *new_transaction = NULL; transaction_t *new_transaction = NULL;
int ret = 0; int ret = 0;
unsigned long ts = jiffies;
if (nblocks > journal->j_max_transaction_buffers) { if (nblocks > journal->j_max_transaction_buffers) {
printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
...@@ -217,6 +220,12 @@ static int start_this_handle(journal_t *journal, handle_t *handle) ...@@ -217,6 +220,12 @@ static int start_this_handle(journal_t *journal, handle_t *handle)
/* OK, account for the buffers that this operation expects to /* OK, account for the buffers that this operation expects to
* use and add the handle to the running transaction. */ * 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; handle->h_transaction = transaction;
transaction->t_outstanding_credits += nblocks; transaction->t_outstanding_credits += nblocks;
transaction->t_updates++; transaction->t_updates++;
......
...@@ -395,6 +395,16 @@ struct handle_s ...@@ -395,6 +395,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 /* The transaction_t type is the guts of the journaling mechanism. It
* tracks a compound transaction through its various states: * tracks a compound transaction through its various states:
* *
...@@ -531,6 +541,21 @@ struct transaction_s ...@@ -531,6 +541,21 @@ struct transaction_s
*/ */
spinlock_t t_handle_lock; 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 * Number of outstanding updates running on this transaction
* [t_handle_lock] * [t_handle_lock]
...@@ -562,6 +587,39 @@ struct transaction_s ...@@ -562,6 +587,39 @@ 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
static inline unsigned long
jbd2_time_diff(unsigned long start, unsigned long end)
{
if (end >= start)
return end - start;
return end + (MAX_JIFFY_OFFSET - start);
}
/** /**
* struct journal_s - The journal_s type is the concrete type associated with * struct journal_s - The journal_s type is the concrete type associated with
* journal_t. * journal_t.
...@@ -623,6 +681,12 @@ struct transaction_s ...@@ -623,6 +681,12 @@ struct transaction_s
* @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the
* number that will fit in j_blocksize * number that will fit in j_blocksize
* @j_last_sync_writer: most recent pid which did a synchronous write * @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. * @j_private: An opaque pointer to fs-private information.
*/ */
...@@ -814,6 +878,19 @@ struct journal_s ...@@ -814,6 +878,19 @@ struct journal_s
pid_t j_last_sync_writer; pid_t j_last_sync_writer;
/*
* Journal statistics
*/
struct transaction_stats_s *j_history;
int j_history_max;
int j_history_cur;
/*
* Protect the transactions statistics history
*/
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 * An opaque pointer to fs-private information. ext3 puts its
* superblock pointer here * superblock pointer here
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment