]> git.neil.brown.name Git - LaFS.git/commitdiff
Split B_Writeback out from B_IOLock
authorNeilBrown <neilb@suse.de>
Tue, 25 Aug 2009 07:09:49 +0000 (17:09 +1000)
committerNeilBrown <neilb@suse.de>
Tue, 25 Aug 2009 07:09:49 +0000 (17:09 +1000)
It is best to have Writeback separate from Lock, just like pages
do.  Then readonly accesses don't have to wait for writeback to
finish.

block.c
checkpoint.c
cluster.c
file.c
index.c
inode.c
io.c
lafs.h
state.h

diff --git a/block.c b/block.c
index 52d817aaa7380d6125221671f84ef220fe98b9c8..8a57b1b2e66e21a67c341554a9a108441340d335 100644 (file)
--- a/block.c
+++ b/block.c
@@ -194,7 +194,7 @@ void lafs_invalidate_page(struct page *page, unsigned long offset)
                                lafs_erase_dblock(&b[i]);
                        else if (b_start >= offset) {
                                /* Just remove block from mapping */
-                               lafs_iolock_block(&b[i].b);
+                               lafs_iolock_written(&b[i].b);
                                LAFS_BUG(test_bit(B_Dirty, &b[i].b.flags),
                                         &b[i].b);
                                LAFS_BUG(test_bit(B_Realloc, &b[i].b.flags),
@@ -247,7 +247,8 @@ int lafs_release_page(struct page *page, gfp_t gfp_flags)
                        set_bit(AS_EIO, &mapping->flags);
                if (test_bit(B_Dirty, &b[i].b.flags) ||
                    test_bit(B_Pinned, &b[i].b.flags) ||
-                   test_bit(B_IOLock, &b[i].b.flags)
+                   test_bit(B_IOLock, &b[i].b.flags) ||
+                   test_bit(B_Writeback, &b[i].b.flags)
                    /* NOTE: if we find an Uninc is set when we
                     * need to invalidate the page, then we
                     * should be waiting for all pages to be gone
@@ -496,7 +497,7 @@ lafs_erase_dblock(struct datablock *b)
        struct fs *fs = fs_from_inode(b->b.inode);
 
        dprintk("Eraseblock for %s\n", strblk(&b->b));
-       lafs_iolock_block(&b->b);
+       lafs_iolock_written(&b->b);
        if (b->b.physaddr == 0 &&
            b->b.fileaddr == 0 &&
            LAFSI(b->b.inode)->depth == 0) {
@@ -512,7 +513,7 @@ lafs_erase_dblock(struct datablock *b)
                        getiref_locked(ib, MKREF("erasedblock"));
                spin_unlock(&b->b.inode->i_data.private_lock);
                if (ib) {
-                       lafs_iolock_block(&ib->b);
+                       lafs_iolock_written(&ib->b);
                        if (ib->depth == 0) {
                                LAFS_BUG(LAFSI(b->b.inode)->depth !=
                                         ib->depth, &b->b);
index 1e33e4f9ef7c45692f509220bdf52253661262bb..508629f2e40f097292be8313e38d40ebef8c6c50 100644 (file)
@@ -78,6 +78,8 @@ static char *strflags(struct block *b)
                strcat(ans, "Valid,");
        if (test_bit(B_Dirty, &b->flags))
                strcat(ans, "Dirty,");
+       if (test_bit(B_Writeback, &b->flags))
+               strcat(ans, "Writeback,");
        if (test_bit(B_Linked, &b->flags))
                strcat(ans, "Linked,");
        if (test_bit(B_Realloc, &b->flags))
@@ -337,17 +339,17 @@ struct block *lafs_get_flushable(struct fs *fs, int phase)
                /* the list counted a reference.  Now we hold it */
                list_del_init(&b->lru);
        spin_unlock(&fs->lock);
-       if (b)
-               /* Need an iolock, but if the list gets put on another
-                * lru (like a cluster or back on leafs) then we lose
-                * interest.
+       if (b) {
+               /* Need an iolock, but if the block is in writeback,
+                * we don't want it.
                 */
-               if (lafs_iolock_block_empty(b) == 0) {
-                       /* gave up on the lock */
+               lafs_iolock_block(b);
+               if (test_bit(B_Writeback, &b->flags)) {
+                       lafs_iounlock_block(b);
                        putref(b, MKREF(leaf));
                        goto retry;
                }
-
+       }
        return b;
 }
 
index b1a5ebf70f9ac0ed92bce9977a0a61e29d65b773..f7f9accdb4b052fe5de2e8b188106b268848624f 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -700,6 +700,9 @@ unsigned long long lafs_cluster_allocate(struct block *b, int cnum)
                        cluster_flush(fs, cnum);
        }
 
+       if (test_and_set_bit(B_Writeback, &b->flags))
+               LAFS_BUG(1, b);
+       lafs_iounlock_block(b);
        /* insert into list ensuring there is enough space
         * in cluster head
         */
@@ -1016,7 +1019,7 @@ static void cluster_done(struct fs *fs, struct wc *wc)
 
        list_for_each_entry_safe(b, tmp, &tofree, lru) {
                list_del_init(&b->lru);
-               lafs_iounlock_block(b);
+               lafs_writeback_done(b);
                putref(b, MKREF(cluster));
        }
 }
diff --git a/file.c b/file.c
index e88f4fa51d62ddbf7097eb51fd00b70ea2c097f0..65e07f3ce9b13aa3c19f93a516fe9dfb21acb442 100644 (file)
--- a/file.c
+++ b/file.c
@@ -205,7 +205,7 @@ lafs_writepage(struct page *page, struct writeback_control *wbc)
                        b0 = getdref(b, MKREF(writepage0));
 
                if (test_bit(B_Dirty, &b->b.flags)) {
-                       lafs_iolock_block(&b->b);
+                       lafs_iolock_written(&b->b);
                        lafs_cluster_allocate(&b->b, 0);
                }
                putdref(b, MKREF(writepage));
@@ -213,7 +213,7 @@ lafs_writepage(struct page *page, struct writeback_control *wbc)
        if (b0) {
                set_page_writeback(page);
                set_bit(B_HaveWriteback, &b0->b.flags);
-               lafs_iocheck_block(b0, 0);
+               lafs_iocheck_writeback(b0, 0);
        }
        unlock_page(page); /* FIXME this must not happen before
                              the writes complete! */
@@ -227,7 +227,7 @@ lafs_writepage(struct page *page, struct writeback_control *wbc)
                 */
                struct datablock *b = lafs_inode_dblock(ino, 0,
                                                        MKREF(writepageflush));
-               lafs_iolock_block(&b->b);
+               lafs_iolock_written(&b->b);
                lafs_cluster_allocate(&b->b, 0);
                putdref(b, MKREF(writepageflush));
        }
@@ -263,7 +263,7 @@ static void lafs_sync_page(struct page *page)
                // FIXME this test will flush a little too often.
                // We need some better way to know if a block is in
                // the current cluster queue...
-               if (test_bit(B_IOLock, &b->b.flags) &&
+               if (test_bit(B_Writeback, &b->b.flags) &&
                    test_bit(B_Dirty, &b->b.flags) &&
                    !lafs_cluster_empty(fs, 0)
                        ) {
diff --git a/index.c b/index.c
index 13f5701df6692235933a79cd7f98280bf3ad8f40..a66db7cb5a9734adc4b430e209b3269971e5c0b3 100644 (file)
--- a/index.c
+++ b/index.c
@@ -734,6 +734,7 @@ void lafs_refile(struct block *b, int dec)
 
                if (!list_empty(&b->lru) &&
                    !test_bit(B_IOLock, &b->flags) &&
+                   !test_bit(B_Writeback, &b->flags) &&
                    !test_bit(B_OnFree, &b->flags))
                        /* If B_IOLock, then it might be on the cluster
                         * list, but not the LRU.
@@ -791,6 +792,7 @@ void lafs_refile(struct block *b, int dec)
                if ((list_empty(&b->lru) || test_bit(B_OnFree, &b->flags)) &&
                    test_bit(B_Pinned, &b->flags) &&
                    !test_bit(B_IOLock, &b->flags) &&
+                   !test_bit(B_Writeback, &b->flags) &&
                    (!test_bit(B_Index, &b->flags) ||
                     atomic_read(&iblk(b)->pincnt[ph]) == 0)) {
                        if (test_and_clear_bit(B_OnFree, &b->flags)) {
@@ -1782,7 +1784,7 @@ int lafs_add_block_address(struct fs *fs, struct block *blk)
                 * two index blocks, which would require an alloc.
                 */
 
-               lafs_iolock_block(&p->b);
+               lafs_iolock_written(&p->b);
                lafs_incorporate(fs, p);
                lafs_iounlock_block(&p->b);
                return 0;
@@ -1812,7 +1814,10 @@ int lafs_allocated_block(struct fs *fs, struct block *blk, u64 phys)
        LAFS_BUG(!test_bit(B_Dirty, &blk->flags) &&
                 !test_bit(B_Realloc, &blk->flags) &&
                 phys != 0, blk);
-       LAFS_BUG(!test_bit(B_IOLock, &blk->flags), blk);
+       if (phys)
+               LAFS_BUG(!test_bit(B_Writeback, &blk->flags), blk);
+       else
+               LAFS_BUG(!test_bit(B_IOLock, &blk->flags), blk);
 
        if (test_bit(B_Root, &blk->flags)) {
                int i;
diff --git a/inode.c b/inode.c
index 6ba494f74b406a91abbe03167903efd0d96fcb48..a51d7df3ca5966ec3781237641993a5f4d646220 100644 (file)
--- a/inode.c
+++ b/inode.c
@@ -600,7 +600,7 @@ static int lafs_inode_handle_orphan(struct datablock *b)
                        LAFS_BUG(LAFSI(ino)->type != 0, &b->b);
                        lafs_orphan_release(fs, b);
                        if (test_bit(B_Dirty, &ib->b.flags)) {
-                               lafs_iolock_block(&ib->b);
+                               lafs_iolock_written(&ib->b);
                                lafs_cluster_allocate(&ib->b, 0);
                        }
                        lafs_erase_dblock(b);
@@ -685,7 +685,7 @@ static int lafs_inode_handle_orphan(struct datablock *b)
                 * Incorporating it should unpin it.
                 */
                getiref(ib2, MKREF(inode_handle_orphan2));
-               lafs_iolock_block(&ib2->b);
+               lafs_iolock_written(&ib2->b);
                do
                        lafs_incorporate(fs, ib2);
                while (ib2->uninc_table.pending_cnt || ib2->uninc);
diff --git a/io.c b/io.c
index 4d3a9d5c6cf474112af1a1bf5fe1643d35db71ab..dcf106adc00b2926c5112e209f8209c4982ecbbb 100644 (file)
--- a/io.c
+++ b/io.c
@@ -191,10 +191,8 @@ void lafs_io_wake(struct block *b)
 {
        wake_up(&block_wait);
 }      
-int
-_lafs_iolock_block(struct block *b, int checkempty)
+void _lafs_iolock_block(struct block *b)
 {
-       int locked = 1;
        if (test_and_set_bit(B_IOLock, &b->flags)) {
                DEFINE_WAIT(wq);
 #ifdef DEBUG_IOLOCK
@@ -204,17 +202,13 @@ _lafs_iolock_block(struct block *b, int checkempty)
 #endif
                for (;;) {
                        prepare_to_wait(&block_wait, &wq, TASK_UNINTERRUPTIBLE);
-                       if (checkempty && !list_empty_careful(&b->lru)) {
-                               locked = 0;
-                               break;
-                       }
+
                        if (!test_and_set_bit(B_IOLock, &b->flags))
                                break;
                        schedule();
                }
                finish_wait(&block_wait, &wq);
        }
-       return locked;
 }
 
 void
@@ -236,13 +230,27 @@ lafs_iounlock_block(struct block *b)
 
 }
 
+void lafs_writeback_done(struct block *b)
+{
+       /* remove writeback flag on this block.
+        * If it is last on page, release page as well.
+        */
+
+       if (test_bit(B_Index, &b->flags))
+               clear_bit(B_Writeback, &b->flags);
+       else
+               lafs_iocheck_writeback(dblk(b), 1);
+
+       lafs_io_wake(b);
+}
+
 void lafs_iocheck_block(struct datablock *db, int unlock)
 {
        struct page *page = db->page;
        struct datablock *blist;
        int n, i;
        int locked = 0;
-       int havelock = 0, havewrite = 0;
+       int havelock = 0;
 
        if (!page)
                return;
@@ -261,8 +269,6 @@ void lafs_iocheck_block(struct datablock *db, int unlock)
        }
        if (!locked && test_and_clear_bit(B_HaveLock, &blist->b.flags))
                havelock = 1;
-       if (!locked && test_and_clear_bit(B_HaveWriteback, &blist->b.flags))
-               havewrite = 1;
        bit_spin_unlock(B_IOLockLock, &blist->b.flags);
 
        if (havelock) {
@@ -270,6 +276,35 @@ void lafs_iocheck_block(struct datablock *db, int unlock)
                        SetPageUptodate(page);
                unlock_page(page);
        }
+}
+
+void lafs_iocheck_writeback(struct datablock *db, int unlock)
+{
+       struct page *page = db->page;
+       struct datablock *blist;
+       int n, i;
+       int locked = 0;
+       int havewrite = 0;
+
+       if (!page)
+               return;
+       blist = (struct datablock *)page->private;
+       if (!blist)
+               return;
+
+       n = 1<<(PAGE_CACHE_SHIFT - blist->b.inode->i_blkbits);
+       bit_spin_lock(B_IOLockLock, &blist->b.flags);
+       if (unlock)
+               clear_bit(B_Writeback, &db->b.flags);
+       for (i = 0 ; i < n; i++) {
+               if (test_bit(B_Writeback, &blist[i].b.flags))
+                       locked++;
+               /* FIXME what about checking uptodate ?? */
+       }
+       if (!locked && test_and_clear_bit(B_HaveWriteback, &blist->b.flags))
+               havewrite = 1;
+       bit_spin_unlock(B_IOLockLock, &blist->b.flags);
+
        if (havewrite)
                end_page_writeback(page);
 }
@@ -309,6 +344,27 @@ lafs_wait_block_async(struct block *b)
                return -EAGAIN;
 }
 
+static void wait_writeback(struct block *b)
+{
+       if (test_bit(B_Writeback, &b->flags)) {
+               DEFINE_WAIT(wq);
+               for (;;) {
+                       prepare_to_wait(&block_wait, &wq, TASK_UNINTERRUPTIBLE);
+                       if (test_bit(B_Writeback, &b->flags))
+                               schedule();
+                       else
+                               break;
+               }
+               finish_wait(&block_wait, &wq);
+       }
+}
+
+void lafs_iolock_written(struct block *b)
+{
+       lafs_iolock_block(b);
+       wait_writeback(b);
+}
+
 static void
 block_loaded(struct bio *bio, int error)
 {
diff --git a/lafs.h b/lafs.h
index 4c523741a82fff94c2f114c3f74df76724d684ce..22e526819d48798d14a3b117495bd917f90abd9a 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -108,13 +108,15 @@ u32 lafs_leaf_next(struct indexblock *ib, u32 start);
 #else
 #define set_iolock_info(b) (0)
 #endif
-#define lafs_iolock_block(b) do { _lafs_iolock_block(b, 0); set_iolock_info(b); } while(0)
-#define lafs_iolock_block_empty(b) (_lafs_iolock_block(b, 1) ? ( set_iolock_info(b), 1): 0)
+#define lafs_iolock_block(b) do { _lafs_iolock_block(b); set_iolock_info(b); } while(0)
 
 void lafs_io_wake(struct block *b);
-int _lafs_iolock_block(struct block *b, int checkempty);
+void _lafs_iolock_block(struct block *b);
+void lafs_iolock_written(struct block *b);
 void lafs_iounlock_block(struct block *b);
 void lafs_iocheck_block(struct datablock *db, int unlock);
+void lafs_iocheck_writeback(struct datablock *db, int unlock);
+void lafs_writeback_done(struct block *b);
 
 void lafs_super_write(struct fs *fs, int dev, u64 addr, char *buf, int size);
 int lafs_super_wait(struct fs *fs);
diff --git a/state.h b/state.h
index c46adf4ccc3fcb0f0866aecf4b668404135f9fb8..b3b548154899ea543c505ea27735682b1a915f7b 100644 (file)
--- a/state.h
+++ b/state.h
@@ -433,6 +433,7 @@ enum {
        B_UnincCredit,  /* Uninc carries a credit */
        B_OnFree,       /* index block on the free list */
        B_IOLock,       /* Block is undergoing IO */
+       B_Writeback,    /* Block is in a cluster */
        B_IOLockLock,   /* lock while testing IOLock on a page */
 
        B_HaveLock,     /* We own the page lock and when all blocks are unlocked,