]> git.neil.brown.name Git - LaFS.git/blobdiff - block.c
README update
[LaFS.git] / block.c
diff --git a/block.c b/block.c
index 6d9b04a1f764c4681a65463bd6b3d8695fbe6a1b..9dd562915442903e83cc6669305593cc10a1aae7 100644 (file)
--- a/block.c
+++ b/block.c
@@ -159,7 +159,10 @@ lafs_get_block(struct inode *ino, unsigned long index, struct page *p,
 
        b = (struct datablock *)p->private;
        b += index & ((1<<bits)-1);
+       /* spinlock is just to sync with lafs_refile */
+       spin_lock(&ino->i_data.private_lock);
        getdref_locked(b, REF);
+       spin_unlock(&ino->i_data.private_lock);
 
        if (unlock) {
                unlock_page(p);
@@ -200,6 +203,10 @@ void lafs_invalidate_page(struct page *page, unsigned long offset)
                 *   wait for any pending IO to complete (so page can be freed)
                 */
                for (i = 0; i < (1<<bits); i++) {
+                       spin_lock(&ino->i_data.private_lock);
+                       (void)getdref_locked(&b[i], MKREF(inval));
+                       spin_unlock(&ino->i_data.private_lock);
+
                        if (b_start >= offset &&
                            test_and_clear_bit(B_Async, &b[i].b.flags))
                                putdref(&b[i], MKREF(Async));
@@ -222,6 +229,7 @@ void lafs_invalidate_page(struct page *page, unsigned long offset)
                        LAFS_BUG(offset == 0 &&
                                 test_bit(B_IOLock, &b[i].b.flags),
                                 &b[i].b);
+                       putdref(&b[i], MKREF(inval));
                }
        }
        if (offset == 0) {
@@ -380,6 +388,8 @@ lafs_reserve_block(struct block *b, int alloc_type)
                         b);
        else
                err = lafs_setparent(dblk(b));
+       if (err)
+               return err;
 
        /* If there is already a physaddr, or the data is
         * stored in the inode, then we aren't really allocating
@@ -404,37 +414,46 @@ lafs_reserve_block(struct block *b, int alloc_type)
         */
        in_emergency = test_bit(EmergencyClean, &fs->fsstate);
        /* Allocate space in the filesystem */
-       err = err ?: lafs_prealloc(b, alloc_type);
+       err = lafs_prealloc(b, alloc_type);
+       if (err) {
+               if (alloc_type == NewSpace) {
+                       if (in_emergency)
+                               return -ENOSPC;
+                       return -EAGAIN;
+               }
+               if (alloc_type == ReleaseSpace)
+                       return -EAGAIN;
+               LAFS_BUG(1, b);
+       }
 
        /* Allocate space in the file (and quota set) */
-       if (err == 0 && b->physaddr == 0 &&
+       if (b->physaddr == 0 &&
            !test_bit(B_Index, &b->flags) &&
            !test_and_set_bit(B_Prealloc, &b->flags)) {
                err = lafs_summary_allocate(fs, b->inode, 1);
                if (err)
                        clear_bit(B_Prealloc, &b->flags);
        }
+       if (err) {
+               LAFS_BUG(alloc_type == AccountSpace, b);
+               return err;
+       }
 
        /* Having reserved the block, we need to get a segref,
         * which will involve reserving those blocks too.
-        * However we never get a segref for Root.
+        * However we never get a segref for Root or any
+        * InoIdx block.
         */
 
+       if (test_bit(B_InoIdx, &b->flags))
+               b = &LAFSI(b->inode)->dblock->b;
+
        while (err == 0
               && !test_bit(B_Root, &b->flags)
               && !test_bit(B_SegRef, &b->flags))
                err = lafs_seg_ref_block(b, 0);
 
-       if (err == 0)
-               return 0;
-       if (alloc_type == NewSpace) {
-               if (in_emergency)
-                       return -ENOSPC;
-               return -EAGAIN;
-       }
-       if (alloc_type == ReleaseSpace)
-               return -EAGAIN;
-       LAFS_BUG(1, b);
+       return err;
 }
 
 int
@@ -451,26 +470,6 @@ lafs_pin_dblock(struct datablock *b, int alloc_type)
         */
        int err;
        struct fs *fs = fs_from_inode(b->b.inode);
-       struct block *blk;
-       struct inode *ino = NULL;
-
-       /* We don't pin a datablock of an inode if there is an
-        * InoIdx block. We pin the InoIdx block instead.
-        * They might both be pinned at the same time, but
-        * only when the index block has swapped phase and the
-        * data block is waiting to be written.
-        */
-       if ((ino = rcu_my_inode(b)) != NULL &&
-           LAFSI(ino)->iblock) {
-               struct indexblock *ib = lafs_make_iblock(ino, ADOPT, SYNC,
-                                                        MKREF(pindb));
-               if (IS_ERR(ib))
-                       blk = getref(&b->b, MKREF(pindb));
-               else
-                       blk = &ib->b;
-       } else
-               blk = getref(&b->b, MKREF(pindb));
-       rcu_iput(ino);
 
        LAFS_BUG(!test_bit(B_PinPending, &b->b.flags), &b->b);
        if (LAFSI(b->b.inode)->type != TypeSegmentMap) {
@@ -481,8 +480,7 @@ lafs_pin_dblock(struct datablock *b, int alloc_type)
                 * been written and we want to flip it before it
                 * can be dirtied.
                 */
-               if (blk == &b->b &&
-                   test_bit(B_Pinned, &b->b.flags) &&
+               if (test_bit(B_Pinned, &b->b.flags) &&
                    !!test_bit(B_Phase1, &b->b.flags) != fs->phase) {
                        clear_bit(B_PinPending, &b->b.flags);
                        lafs_refile(&b->b, 0);
@@ -491,15 +489,12 @@ lafs_pin_dblock(struct datablock *b, int alloc_type)
                lafs_iounlock_block(&b->b);
        }
 
-       err = lafs_reserve_block(blk, alloc_type);
+       err = lafs_reserve_block(&b->b, alloc_type);
 
-       if (err) {
-               putref(blk, MKREF(pindb));
+       if (err)
                return err;
-       }
 
-       lafs_pin_block(blk);
-       putref(blk, MKREF(pindb));
+       lafs_pin_block(&b->b);
        return 0;
 }
 
@@ -542,24 +537,38 @@ erase_dblock_locked(struct datablock *b)
                 * block lives in.
                 * Need private_lock to be allowed to dereference ->iblock
                 * though if b was dirty we shouldn't.... FIXME.
+                * We need to hold the ref to idb for the getiref_locked_needsync to
+                * be safe.
                 */
                struct indexblock *ib;
-               spin_lock(&b->b.inode->i_data.private_lock);
+               struct datablock *idb = lafs_inode_dblock(b->b.inode, SYNC, MKREF(erase));
+               if (IS_ERR(idb)) {
+                       /* not much we can do here */
+                       BUG();
+                       goto skip;
+               }
+               spin_lock(&lafs_hash_lock);
                ib = LAFSI(b->b.inode)->iblock;
                if (ib)
-                       getiref_locked(ib, MKREF(erasedblock));
-               spin_unlock(&b->b.inode->i_data.private_lock);
+                       getiref_locked_needsync(ib, MKREF(erasedblock));
+               spin_unlock(&lafs_hash_lock);
+               sync_ref(&ib->b);
+               putdref(idb, MKREF(erase));
                if (ib) {
                        lafs_iolock_written(&ib->b);
                        if (ib->depth == 0) {
                                LAFS_BUG(LAFSI(b->b.inode)->depth !=
                                         ib->depth, &b->b);
+                               ib->depth = 1;
+                               LAFSI(b->b.inode)->depth = 1;
                                lafs_clear_index(ib);
                                clear_bit(B_PhysValid, &b->b.flags);
+                               clear_bit(B_SegRef, &b->b.flags); /* Just in case */
                        }
                        lafs_iounlock_block(&ib->b);
                        putiref(ib, MKREF(erasedblock));
                }
+       skip:;
        }
 
        if (LAFSI(b->b.inode)->type == TypeInodeFile) {
@@ -586,10 +595,8 @@ erase_dblock_locked(struct datablock *b)
                 * leaf list, so we must remove it.
                 * However it is IOLocked so it might not be on the leaf list.
                 */
-               int onlru = 0;
                LAFS_BUG(test_bit(B_Writeback, &b->b.flags), &b->b);
                if (!list_empty(&b->b.lru)) {
-                       onlru = 1;
                        list_del_init(&b->b.lru);
                }
                if (!test_bit(B_Root, &b->b.flags))
@@ -599,8 +606,6 @@ erase_dblock_locked(struct datablock *b)
                spin_unlock(&fs->lock);
                if (!test_bit(B_Root, &b->b.flags))
                        lafs_refile(&b->b.parent->b, 0);
-               if (onlru)
-                       putiref(b, MKREF(leaf));
        } else
                spin_unlock(&fs->lock);
 
@@ -642,7 +647,7 @@ lafs_erase_dblock_async(struct datablock *b)
 }
 
 void
-lafs_dirty_iblock(struct indexblock *b)
+lafs_dirty_iblock(struct indexblock *b, int want_realloc)
 {
        /* Note, only need to set the phase if locked.
         * Then no-one may change it while in phase transition.
@@ -651,6 +656,36 @@ lafs_dirty_iblock(struct indexblock *b)
         */
 
        LAFS_BUG(!test_bit(B_Pinned, &b->b.flags), &b->b);
+       LAFS_BUG(!test_bit(B_Valid, &b->b.flags) && b->depth > 0, &b->b);
+
+       if (want_realloc) {
+               /* Try to make for Realloc instead.  If we cannot get the
+                * credits, fall back on Dirty
+                */
+               struct fs *fs = fs_from_inode(b->b.inode);
+               if (!test_bit(B_Realloc, &b->b.flags)) {
+                       /* I cannot use B_Credit to fill B_Realloc as that
+                        * might still be needed for B_Dirty.
+                        * So if we cannot allocated a new credit,
+                        * just set the block as 'dirty' now.
+                        */
+                       if (lafs_space_alloc(fs, 1, CleanSpace) == 1) {
+                               if (test_and_set_bit(B_Realloc, &b->b.flags))
+                                       lafs_space_return(fs, 1);
+                       } else
+                               goto dirty;
+               }
+               if (!test_bit(B_UnincCredit, &b->b.flags)) {
+                       /* Ditto for UnincCredit */
+                       if (lafs_space_alloc(fs, 1, CleanSpace) == 1) {
+                               if (test_and_set_bit(B_UnincCredit, &b->b.flags))
+                                       lafs_space_return(fs, 1);
+                       } else
+                               goto dirty;
+               }
+               return;
+       }
+dirty:
        if (!test_and_set_bit(B_Dirty, &b->b.flags)) {
                if (!test_and_clear_bit(B_Credit, &b->b.flags)) {
                        printk(KERN_ERR "Why have I no credits?\n");