[GFS2] Small fixes to logging code
[cascardo/linux.git] / fs / gfs2 / log.c
index 0cace3d..f49a12e 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/gfs2_ondisk.h>
 #include <linux/crc32.h>
 #include <linux/lm_interface.h>
+#include <linux/delay.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -82,6 +83,11 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
 
                        gfs2_assert(sdp, bd->bd_ail == ai);
 
+                       if (!bh){
+                               list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
+                                continue;
+                        }
+
                        if (!buffer_busy(bh)) {
                                if (!buffer_uptodate(bh)) {
                                        gfs2_log_unlock(sdp);
@@ -124,6 +130,11 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
                                         bd_ail_st_list) {
                bh = bd->bd_bh;
 
+               if (!bh){
+                       list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
+                       continue;
+               }
+
                gfs2_assert(sdp, bd->bd_ail == ai);
 
                if (buffer_busy(bh)) {
@@ -142,7 +153,7 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
        return list_empty(&ai->ai_ail1_list);
 }
 
-void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
+static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
 {
        struct list_head *head = &sdp->sd_ail1_list;
        u64 sync_gen;
@@ -261,12 +272,19 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
  * @sdp: The GFS2 superblock
  * @blks: The number of blocks to reserve
  *
+ * Note that we never give out the last few blocks of the journal. Thats
+ * due to the fact that there is a small number of header blocks
+ * associated with each log flush. The exact number can't be known until
+ * flush time, so we ensure that we have just enough free blocks at all
+ * times to avoid running out during a log flush.
+ *
  * Returns: errno
  */
 
 int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
 {
        unsigned int try = 0;
+       unsigned reserved_blks = 6 * (4096 / sdp->sd_vfs->s_blocksize);
 
        if (gfs2_assert_warn(sdp, blks) ||
            gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
@@ -274,7 +292,7 @@ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
 
        mutex_lock(&sdp->sd_log_reserve_mutex);
        gfs2_log_lock(sdp);
-       while(sdp->sd_log_blks_free <= blks) {
+       while(sdp->sd_log_blks_free <= (blks + reserved_blks)) {
                gfs2_log_unlock(sdp);
                gfs2_ail1_empty(sdp, 0);
                gfs2_log_flush(sdp, NULL);
@@ -319,7 +337,8 @@ static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn)
        bh_map.b_size = 1 << inode->i_blkbits;
        error = gfs2_block_map(inode, lbn, 0, &bh_map);
        if (error || !bh_map.b_blocknr)
-               printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, bh_map.b_blocknr, lbn);
+               printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error,
+                      (unsigned long long)bh_map.b_blocknr, lbn);
        gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr);
 
        return bh_map.b_blocknr;
@@ -349,6 +368,58 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer
        return dist;
 }
 
+/**
+ * calc_reserved - Calculate the number of blocks to reserve when
+ *                 refunding a transaction's unused buffers.
+ * @sdp: The GFS2 superblock
+ *
+ * This is complex.  We need to reserve room for all our currently used
+ * metadata buffers (e.g. normal file I/O rewriting file time stamps) and 
+ * all our journaled data buffers for journaled files (e.g. files in the 
+ * meta_fs like rindex, or files for which chattr +j was done.)
+ * If we don't reserve enough space, gfs2_log_refund and gfs2_log_flush
+ * will count it as free space (sd_log_blks_free) and corruption will follow.
+ *
+ * We can have metadata bufs and jdata bufs in the same journal.  So each
+ * type gets its own log header, for which we need to reserve a block.
+ * In fact, each type has the potential for needing more than one header 
+ * in cases where we have more buffers than will fit on a journal page.
+ * Metadata journal entries take up half the space of journaled buffer entries.
+ * Thus, metadata entries have buf_limit (502) and journaled buffers have
+ * databuf_limit (251) before they cause a wrap around.
+ *
+ * Also, we need to reserve blocks for revoke journal entries and one for an
+ * overall header for the lot.
+ *
+ * Returns: the number of blocks reserved
+ */
+static unsigned int calc_reserved(struct gfs2_sbd *sdp)
+{
+       unsigned int reserved = 0;
+       unsigned int mbuf_limit, metabufhdrs_needed;
+       unsigned int dbuf_limit, databufhdrs_needed;
+       unsigned int revokes = 0;
+
+       mbuf_limit = buf_limit(sdp);
+       metabufhdrs_needed = (sdp->sd_log_commited_buf +
+                             (mbuf_limit - 1)) / mbuf_limit;
+       dbuf_limit = databuf_limit(sdp);
+       databufhdrs_needed = (sdp->sd_log_commited_databuf +
+                             (dbuf_limit - 1)) / dbuf_limit;
+
+       if (sdp->sd_log_commited_revoke)
+               revokes = gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
+                                         sizeof(u64));
+
+       reserved = sdp->sd_log_commited_buf + metabufhdrs_needed +
+               sdp->sd_log_commited_databuf + databufhdrs_needed +
+               revokes;
+       /* One for the overall header */
+       if (reserved)
+               reserved++;
+       return reserved;
+}
+
 static unsigned int current_tail(struct gfs2_sbd *sdp)
 {
        struct gfs2_ail *ai;
@@ -439,14 +510,14 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
        return bh;
 }
 
-static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull)
+static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail)
 {
        unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
 
        ail2_empty(sdp, new_tail);
 
        gfs2_log_lock(sdp);
-       sdp->sd_log_blks_free += dist - (pull ? 1 : 0);
+       sdp->sd_log_blks_free += dist;
        gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
        gfs2_log_unlock(sdp);
 
@@ -496,7 +567,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
        brelse(bh);
 
        if (sdp->sd_log_tail != tail)
-               log_pull_tail(sdp, tail, pull);
+               log_pull_tail(sdp, tail);
        else
                gfs2_assert_withdraw(sdp, !pull);
 
@@ -509,6 +580,7 @@ static void log_flush_commit(struct gfs2_sbd *sdp)
        struct list_head *head = &sdp->sd_log_flush_list;
        struct gfs2_log_buf *lb;
        struct buffer_head *bh;
+       int flushcount = 0;
 
        while (!list_empty(head)) {
                lb = list_entry(head->next, struct gfs2_log_buf, lb_list);
@@ -525,9 +597,20 @@ static void log_flush_commit(struct gfs2_sbd *sdp)
                } else
                        brelse(bh);
                kfree(lb);
+               flushcount++;
        }
 
-       log_write_header(sdp, 0, 0);
+       /* If nothing was journaled, the header is unplanned and unwanted. */
+       if (flushcount) {
+               log_write_header(sdp, 0, 0);
+       } else {
+               unsigned int tail;
+               tail = current_tail(sdp);
+
+               gfs2_ail1_empty(sdp, 0);
+               if (sdp->sd_log_tail != tail)
+                       log_pull_tail(sdp, tail);
+       }
 }
 
 /**
@@ -557,7 +640,10 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        INIT_LIST_HEAD(&ai->ai_ail1_list);
        INIT_LIST_HEAD(&ai->ai_ail2_list);
 
-       gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf == sdp->sd_log_commited_buf);
+       gfs2_assert_withdraw(sdp,
+                            sdp->sd_log_num_buf + sdp->sd_log_num_jdata ==
+                            sdp->sd_log_commited_buf +
+                            sdp->sd_log_commited_databuf);
        gfs2_assert_withdraw(sdp,
                        sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
 
@@ -568,16 +654,19 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        lops_before_commit(sdp);
        if (!list_empty(&sdp->sd_log_flush_list))
                log_flush_commit(sdp);
-       else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle)
+       else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
+               gfs2_log_lock(sdp);
+               sdp->sd_log_blks_free--; /* Adjust for unreserved buffer */
+               gfs2_log_unlock(sdp);
                log_write_header(sdp, 0, PULL);
+       }
        lops_after_commit(sdp, ai);
 
        gfs2_log_lock(sdp);
        sdp->sd_log_head = sdp->sd_log_flush_head;
-       sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs;
        sdp->sd_log_blks_reserved = 0;
        sdp->sd_log_commited_buf = 0;
-       sdp->sd_log_num_hdrs = 0;
+       sdp->sd_log_commited_databuf = 0;
        sdp->sd_log_commited_revoke = 0;
 
        if (!list_empty(&ai->ai_ail1_list)) {
@@ -594,32 +683,26 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
 
 static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
 {
-       unsigned int reserved = 0;
+       unsigned int reserved;
        unsigned int old;
 
        gfs2_log_lock(sdp);
 
        sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm;
-       gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0);
+       sdp->sd_log_commited_databuf += tr->tr_num_databuf_new -
+               tr->tr_num_databuf_rm;
+       gfs2_assert_withdraw(sdp, (((int)sdp->sd_log_commited_buf) >= 0) ||
+                            (((int)sdp->sd_log_commited_databuf) >= 0));
        sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
        gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0);
-
-       if (sdp->sd_log_commited_buf)
-               reserved += sdp->sd_log_commited_buf;
-       if (sdp->sd_log_commited_revoke)
-               reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
-                                           sizeof(u64));
-       if (reserved)
-               reserved++;
-
+       reserved = calc_reserved(sdp);
        old = sdp->sd_log_blks_free;
        sdp->sd_log_blks_free += tr->tr_reserved -
                                 (reserved - sdp->sd_log_blks_reserved);
 
        gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old);
-       gfs2_assert_withdraw(sdp,
-                            sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks +
-                            sdp->sd_log_num_hdrs);
+       gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <=
+                            sdp->sd_jdesc->jd_blocks);
 
        sdp->sd_log_blks_reserved = reserved;
 
@@ -643,12 +726,9 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
        up_read(&sdp->sd_log_flush_lock);
 
        gfs2_log_lock(sdp);
-       if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) {
-               gfs2_log_unlock(sdp);
-               gfs2_log_flush(sdp, NULL);
-       } else {
-               gfs2_log_unlock(sdp);
-       }
+       if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks))
+               wake_up_process(sdp->sd_logd_process);
+       gfs2_log_unlock(sdp);
 }
 
 /**
@@ -668,13 +748,13 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
-       gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs);
        gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
 
        sdp->sd_log_flush_head = sdp->sd_log_head;
        sdp->sd_log_flush_wrapped = 0;
 
-       log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0);
+       log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT,
+                        (sdp->sd_log_tail == current_tail(sdp)) ? 0 : PULL);
 
        gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks);
        gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);
@@ -686,3 +766,21 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
        up_write(&sdp->sd_log_flush_lock);
 }
 
+
+/**
+ * gfs2_meta_syncfs - sync all the buffers in a filesystem
+ * @sdp: the filesystem
+ *
+ */
+
+void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
+{
+       gfs2_log_flush(sdp, NULL);
+       for (;;) {
+               gfs2_ail1_start(sdp, DIO_ALL);
+               if (gfs2_ail1_empty(sdp, DIO_ALL))
+                       break;
+               msleep(10);
+       }
+}
+