]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/gfs2/log.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
[linux-2.6-omap-h63xx.git] / fs / gfs2 / log.c
index f49a12e24086d36e6975a733c3c1a8b9793c244d..7df7024732523b8beddb57e3a6d6c3d37e035768 100644 (file)
@@ -59,6 +59,26 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
        return blks;
 }
 
+/**
+ * gfs2_remove_from_ail - Remove an entry from the ail lists, updating counters
+ * @mapping: The associated mapping (maybe NULL)
+ * @bd: The gfs2_bufdata to remove
+ *
+ * The log lock _must_ be held when calling this function
+ *
+ */
+
+void gfs2_remove_from_ail(struct address_space *mapping, struct gfs2_bufdata *bd)
+{
+       bd->bd_ail = NULL;
+       list_del_init(&bd->bd_ail_st_list);
+       list_del_init(&bd->bd_ail_gl_list);
+       atomic_dec(&bd->bd_gl->gl_ail_count);
+       if (mapping)
+               gfs2_meta_cache_flush(GFS2_I(mapping->host));
+       brelse(bd->bd_bh);
+}
+
 /**
  * gfs2_ail1_start_one - Start I/O on a part of the AIL
  * @sdp: the filesystem
@@ -83,17 +103,9 @@ 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);
+                               if (!buffer_uptodate(bh))
                                        gfs2_io_error_bh(sdp, bh);
-                                       gfs2_log_lock(sdp);
-                               }
                                list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
                                continue;
                        }
@@ -103,9 +115,16 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
 
                        list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
 
+                       get_bh(bh);
                        gfs2_log_unlock(sdp);
-                       wait_on_buffer(bh);
-                       ll_rw_block(WRITE, 1, &bh);
+                       lock_buffer(bh);
+                       if (test_clear_buffer_dirty(bh)) {
+                               bh->b_end_io = end_buffer_write_sync;
+                               submit_bh(WRITE, bh);
+                       } else {
+                               unlock_buffer(bh);
+                               brelse(bh);
+                       }
                        gfs2_log_lock(sdp);
 
                        retry = 1;
@@ -130,11 +149,6 @@ 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)) {
@@ -155,13 +169,14 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
 
 static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
 {
-       struct list_head *head = &sdp->sd_ail1_list;
+       struct list_head *head;
        u64 sync_gen;
        struct list_head *first;
        struct gfs2_ail *first_ai, *ai, *tmp;
        int done = 0;
 
        gfs2_log_lock(sdp);
+       head = &sdp->sd_ail1_list;
        if (list_empty(head)) {
                gfs2_log_unlock(sdp);
                return;
@@ -233,11 +248,7 @@ static void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
                bd = list_entry(head->prev, struct gfs2_bufdata,
                                bd_ail_st_list);
                gfs2_assert(sdp, bd->bd_ail == ai);
-               bd->bd_ail = NULL;
-               list_del(&bd->bd_ail_st_list);
-               list_del(&bd->bd_ail_gl_list);
-               atomic_dec(&bd->bd_gl->gl_ail_count);
-               brelse(bd->bd_bh);
+               gfs2_remove_from_ail(bd->bd_bh->b_page->mapping, bd);
        }
 }
 
@@ -439,10 +450,10 @@ static unsigned int current_tail(struct gfs2_sbd *sdp)
        return tail;
 }
 
-static inline void log_incr_head(struct gfs2_sbd *sdp)
+void gfs2_log_incr_head(struct gfs2_sbd *sdp)
 {
        if (sdp->sd_log_flush_head == sdp->sd_log_tail)
-               gfs2_assert_withdraw(sdp, sdp->sd_log_flush_head == sdp->sd_log_head);
+               BUG_ON(sdp->sd_log_flush_head != sdp->sd_log_head);
 
        if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) {
                sdp->sd_log_flush_head = 0;
@@ -450,6 +461,23 @@ static inline void log_incr_head(struct gfs2_sbd *sdp)
        }
 }
 
+/**
+ * gfs2_log_write_endio - End of I/O for a log buffer
+ * @bh: The buffer head
+ * @uptodate: I/O Status
+ *
+ */
+
+static void gfs2_log_write_endio(struct buffer_head *bh, int uptodate)
+{
+       struct gfs2_sbd *sdp = bh->b_private;
+       bh->b_private = NULL;
+
+       end_buffer_write_sync(bh, uptodate);
+       if (atomic_dec_and_test(&sdp->sd_log_in_flight))
+               wake_up(&sdp->sd_log_flush_wait);
+}
+
 /**
  * gfs2_log_get_buf - Get and initialize a buffer to use for log control data
  * @sdp: The GFS2 superblock
@@ -460,24 +488,42 @@ static inline void log_incr_head(struct gfs2_sbd *sdp)
 struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp)
 {
        u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head);
-       struct gfs2_log_buf *lb;
        struct buffer_head *bh;
 
-       lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL);
-       list_add(&lb->lb_list, &sdp->sd_log_flush_list);
-
-       bh = lb->lb_bh = sb_getblk(sdp->sd_vfs, blkno);
+       bh = sb_getblk(sdp->sd_vfs, blkno);
        lock_buffer(bh);
        memset(bh->b_data, 0, bh->b_size);
        set_buffer_uptodate(bh);
        clear_buffer_dirty(bh);
-       unlock_buffer(bh);
-
-       log_incr_head(sdp);
+       gfs2_log_incr_head(sdp);
+       atomic_inc(&sdp->sd_log_in_flight);
+       bh->b_private = sdp;
+       bh->b_end_io = gfs2_log_write_endio;
 
        return bh;
 }
 
+/**
+ * gfs2_fake_write_endio - 
+ * @bh: The buffer head
+ * @uptodate: The I/O Status
+ *
+ */
+
+static void gfs2_fake_write_endio(struct buffer_head *bh, int uptodate)
+{
+       struct buffer_head *real_bh = bh->b_private;
+       struct gfs2_bufdata *bd = real_bh->b_private;
+       struct gfs2_sbd *sdp = bd->bd_gl->gl_sbd;
+
+       end_buffer_write_sync(bh, uptodate);
+       free_buffer_head(bh);
+       unlock_buffer(real_bh);
+       brelse(real_bh);
+       if (atomic_dec_and_test(&sdp->sd_log_in_flight))
+               wake_up(&sdp->sd_log_flush_wait);
+}
+
 /**
  * gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log
  * @sdp: the filesystem
@@ -490,22 +536,20 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
                                      struct buffer_head *real)
 {
        u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head);
-       struct gfs2_log_buf *lb;
        struct buffer_head *bh;
 
-       lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL);
-       list_add(&lb->lb_list, &sdp->sd_log_flush_list);
-       lb->lb_real = real;
-
-       bh = lb->lb_bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL);
+       bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL);
        atomic_set(&bh->b_count, 1);
-       bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate);
+       bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate) | (1 << BH_Lock);
        set_bh_page(bh, real->b_page, bh_offset(real));
        bh->b_blocknr = blkno;
        bh->b_size = sdp->sd_sb.sb_bsize;
        bh->b_bdev = sdp->sd_vfs->s_bdev;
+       bh->b_private = real;
+       bh->b_end_io = gfs2_fake_write_endio;
 
-       log_incr_head(sdp);
+       gfs2_log_incr_head(sdp);
+       atomic_inc(&sdp->sd_log_in_flight);
 
        return bh;
 }
@@ -572,45 +616,75 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
                gfs2_assert_withdraw(sdp, !pull);
 
        sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
-       log_incr_head(sdp);
+       gfs2_log_incr_head(sdp);
 }
 
 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;
+       DEFINE_WAIT(wait);
+
+       if (atomic_read(&sdp->sd_log_in_flight)) {
+               do {
+                       prepare_to_wait(&sdp->sd_log_flush_wait, &wait,
+                                       TASK_UNINTERRUPTIBLE);
+                       if (atomic_read(&sdp->sd_log_in_flight))
+                               io_schedule();
+               } while(atomic_read(&sdp->sd_log_in_flight));
+               finish_wait(&sdp->sd_log_flush_wait, &wait);
+       }
 
-       while (!list_empty(head)) {
-               lb = list_entry(head->next, struct gfs2_log_buf, lb_list);
-               list_del(&lb->lb_list);
-               bh = lb->lb_bh;
+       log_write_header(sdp, 0, 0);
+}
 
-               wait_on_buffer(bh);
-               if (!buffer_uptodate(bh))
-                       gfs2_io_error_bh(sdp, bh);
-               if (lb->lb_real) {
-                       while (atomic_read(&bh->b_count) != 1)  /* Grrrr... */
-                               schedule();
-                       free_buffer_head(bh);
-               } else
+static void gfs2_ordered_write(struct gfs2_sbd *sdp)
+{
+       struct gfs2_bufdata *bd;
+       struct buffer_head *bh;
+       LIST_HEAD(written);
+
+       gfs2_log_lock(sdp);
+       while (!list_empty(&sdp->sd_log_le_ordered)) {
+               bd = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_bufdata, bd_le.le_list);
+               list_move(&bd->bd_le.le_list, &written);
+               bh = bd->bd_bh;
+               if (!buffer_dirty(bh))
+                       continue;
+               get_bh(bh);
+               gfs2_log_unlock(sdp);
+               lock_buffer(bh);
+               if (test_clear_buffer_dirty(bh)) {
+                       bh->b_end_io = end_buffer_write_sync;
+                       submit_bh(WRITE, bh);
+               } else {
+                       unlock_buffer(bh);
                        brelse(bh);
-               kfree(lb);
-               flushcount++;
+               }
+               gfs2_log_lock(sdp);
        }
+       list_splice(&written, &sdp->sd_log_le_ordered);
+       gfs2_log_unlock(sdp);
+}
 
-       /* 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);
+static void gfs2_ordered_wait(struct gfs2_sbd *sdp)
+{
+       struct gfs2_bufdata *bd;
+       struct buffer_head *bh;
 
-               gfs2_ail1_empty(sdp, 0);
-               if (sdp->sd_log_tail != tail)
-                       log_pull_tail(sdp, tail);
+       gfs2_log_lock(sdp);
+       while (!list_empty(&sdp->sd_log_le_ordered)) {
+               bd = list_entry(sdp->sd_log_le_ordered.prev, struct gfs2_bufdata, bd_le.le_list);
+               bh = bd->bd_bh;
+               if (buffer_locked(bh)) {
+                       get_bh(bh);
+                       gfs2_log_unlock(sdp);
+                       wait_on_buffer(bh);
+                       brelse(bh);
+                       gfs2_log_lock(sdp);
+                       continue;
+               }
+               list_del_init(&bd->bd_le.le_list);
        }
+       gfs2_log_unlock(sdp);
 }
 
 /**
@@ -640,10 +714,16 @@ 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_num_jdata ==
-                            sdp->sd_log_commited_buf +
-                            sdp->sd_log_commited_databuf);
+       if (sdp->sd_log_num_buf != sdp->sd_log_commited_buf) {
+               printk(KERN_INFO "GFS2: log buf %u %u\n", sdp->sd_log_num_buf,
+                      sdp->sd_log_commited_buf);
+               gfs2_assert_withdraw(sdp, 0);
+       }
+       if (sdp->sd_log_num_databuf != sdp->sd_log_commited_databuf) {
+               printk(KERN_INFO "GFS2: log databuf %u %u\n",
+                      sdp->sd_log_num_databuf, sdp->sd_log_commited_databuf);
+               gfs2_assert_withdraw(sdp, 0);
+       }
        gfs2_assert_withdraw(sdp,
                        sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
 
@@ -651,8 +731,11 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        sdp->sd_log_flush_wrapped = 0;
        ai->ai_first = sdp->sd_log_flush_head;
 
+       gfs2_ordered_write(sdp);
        lops_before_commit(sdp);
-       if (!list_empty(&sdp->sd_log_flush_list))
+       gfs2_ordered_wait(sdp);
+
+       if (sdp->sd_log_head != sdp->sd_log_flush_head)
                log_flush_commit(sdp);
        else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
                gfs2_log_lock(sdp);
@@ -744,7 +827,6 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
        gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_gl);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_buf);
-       gfs2_assert_withdraw(sdp, !sdp->sd_log_num_jdata);
        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);