]> git.neil.brown.name Git - LaFS.git/commitdiff
incorporate: handle empty block correctly.
authorNeilBrown <neilb@suse.de>
Mon, 28 Sep 2009 02:26:29 +0000 (12:26 +1000)
committerNeilBrown <neilb@suse.de>
Mon, 28 Sep 2009 02:26:29 +0000 (12:26 +1000)
When a block becomes empty, we need to remove it from the
indexing tree, causing the next block to take over the address
space if possible, else maybe the previous block.

index.c
lafs.h
modify.c

diff --git a/index.c b/index.c
index b3f244876248353d9181808eb29b005012b5cb66..9faae6c20ef3208af5326018c57ae85442b11cd7 100644 (file)
--- a/index.c
+++ b/index.c
@@ -266,8 +266,8 @@ struct indexblock *getiref_locked(struct indexblock *ib, REFARG)
        return ib;
 }
 
-static struct indexblock *
-iblock_get(struct inode *ino, faddr_t addr, int depth, paddr_t phys, REFARG)
+struct indexblock *
+lafs_iblock_get(struct inode *ino, faddr_t addr, int depth, paddr_t phys, REFARG)
 {
        /* Find the index block with this inode/depth/address.
         * If it isn't in the cache, create it with given
@@ -1340,7 +1340,7 @@ lafs_leaf_find(struct inode *inode, u32 addr, int adopt, u32 *next,
                                     addr, &iaddr, next);
                unmap_iblock(ib, buf);
 
-               ib2 = iblock_get(inode, iaddr, ib->depth-1, iphys, REF);
+               ib2 = lafs_iblock_get(inode, iaddr, ib->depth-1, iphys, REF);
 
                if (!ib2) {
                        /* if iphys==0, then we were expecting to find a
@@ -1367,6 +1367,9 @@ lafs_leaf_find(struct inode *inode, u32 addr, int adopt, u32 *next,
                         */
                        putiref(ib2, REF);
                        lafs_iounlock_block(&ib->b);
+                       down_read(&ib->b.inode->i_alloc_sem);
+                       /* ib2 should have been dealt with by now */
+                       up_read(&ib->b.inode->i_alloc_sem);
                        putiref(ib, REF);
                        goto retry;
                }
diff --git a/lafs.h b/lafs.h
index 8474b3cbced02cde0ed3b049633bf27b2770aaaf..ce210bd70df90a87315f7df2455718648274ea78 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -637,6 +637,8 @@ int lafs_add_block_address(struct fs *fs, struct block *blk);
 void lafs_phase_flip(struct fs *fs, struct block *b);
 struct indexblock * __must_check
 lafs_make_iblock(struct inode *ino, int adopt, int async, REFARG);
+struct indexblock *
+lafs_iblock_get(struct inode *ino, faddr_t addr, int depth, paddr_t phys, REFARG);
 
 /* io.c */
 void lafs_write_head(struct fs *fs, struct cluster_head *head, u64 virt,
index 2dfbe20928fc04f74854c101321ff6495be26377..cfd0d0f15bc601b38f84791d54ea32b3195781b9 100644 (file)
--- a/modify.c
+++ b/modify.c
@@ -1563,6 +1563,43 @@ static int do_incorporate_internal(struct fs *fs, struct indexblock *ib,
        return 2;
 }
 
+static u32 remove_from_index(struct indexblock *ib, u32 addr)
+{
+       /* addr exists in the given internal index block.
+        * we want to remove it, shuffle down following addresses,
+        * and return the next address if one exists, else 0.
+        */
+       char *buf = map_iblock(ib);
+       char *p;
+       int len = 1 << ib->b.inode->i_blkbits;
+       u32 rv = 0;
+       u64 phys;
+       u32 a;
+       int found = 0;
+
+       p = buf+2;
+       len -= 2;
+       while (len > 10) {
+               phys = decode48(p);
+               a = decode32(p);
+               len -= 10;
+               if (found) {
+                       p -= 20;
+                       encode48(p, phys);
+                       encode32(p, a);
+                       p += 10;
+                       if (rv == 0)
+                               rv = a;
+               } else if (a == addr)
+                       found = 1;
+               else
+                       BUG_ON(a > addr);
+       }
+       unmap_iblock(ib, buf);
+       lafs_dirty_iblock(ib);
+       return rv;
+}
+
 /* Incorporate all the addresses in uninc_table into this
  * indexblock.  Each address carried a credit to allow for
  * indexblock splitting etc.
@@ -1572,9 +1609,10 @@ static int do_incorporate_internal(struct fs *fs, struct indexblock *ib,
 void lafs_incorporate(struct fs *fs, struct indexblock *ib)
 {
        int offset;     /* start of block where indexing is */
-       struct indexblock *new = NULL;
+       struct indexblock *new = NULL, *orig_ib, *nxt;
        struct block *uninc = NULL;
        int rv = 0;
+       u32 next;
        char *buf;
        struct uninc uit;
        int blocksize = fs->prime_sb->s_blocksize;
@@ -1801,23 +1839,109 @@ void lafs_incorporate(struct fs *fs, struct indexblock *ib)
        temp_credits = 0;
        lafs_space_return(fs, uit.credits);
 
-       /* If this internal index block is now empty, we either
-        * invalidate it or merge it with the first dependent block.
-        * If it is the first index block in it's parent we need to
-        * adjust the start of parent and possibly other ancestors,
-        * and incorporate them immediately.
+       /* If this index block is now empty, we either
+        * invalidate it and possibly find something else to take
+        * it's place.
+        * If there is nothing to take it's place, and it is the
+        * first block in the parent, we need to recurse up and
+        * resolve the parent in the same way.
         */
-       if (test_bit(B_Valid, &ib->b.flags) &&
-           list_empty(&ib->children) &&
-           ib->depth > 1 &&
-           lafs_index_empty(ib)) {
-               struct block *nxt = list_entry(ib->b.siblings.next,
-                                              struct block, siblings);
-               if (ib->b.siblings.next != &ib->b.parent->children &&
-                   test_bit(B_PrimaryRef, &nxt->flags)) {
-                       /* */
+       if (test_bit(B_InoIdx, &ib->b.flags)) {
+               /* Empty InoIdx blocks aren't a problem, though I should
+                * probably reduce depth to 0 or 1.
+                */
+               ib->depth = 0;
+               LAFSI(ib->b.inode)->depth = 0;
+               /* Don't need to dirty the inode - the fact that we just
+                * did incorporation should ensure it is already dirty
+                */
+               return;
+       }
+       if (!list_empty(&ib->children))
+               /* If there are child, we cannot treat this block as empty. */
+               return;
+       if (ib->depth > 1 && ! lafs_index_empty(ib))
+               return;
+       if (ib->depth == 1 && lafs_leaf_next(ib, 0) != 0xFFFFFFFF)
+               return;
+
+       orig_ib = ib;
+ recurse:
+       /* OK, it is empty now.  There are various ways we can handle
+        * this.  All involve modification of the parent in some way,
+        * so we need to get a lock on said parent.
+        * This requires dropping the lock on this block, so we
+        * take i_alloc_sem to stop index looks from getting in our way.
+        */
+       if (test_bit(B_PhysValid, &ib->b.flags))
+               lafs_allocated_block(fs, &ib->b, 0);
+       else {
+               ib->b.physaddr = 0;
+               set_bit(B_PhysValid, &ib->b.flags);
+       }
+       down_write(&ib->b.inode->i_alloc_sem);
+       /* Any index lookup in ib will notice it is invalid and block
+        * on i_alloc_sem, so it is safe to unlock ib
+        */
+       lafs_iounlock_block(&ib->b);
+       lafs_iolock_block(&ib->b.parent->b);
+       /* Now anything looking for an address that could be in ib will
+        * block on the parent first, so we don't need i_alloc_sem
+        * any more.
+        */
+       up_write(&ib->b.inode->i_alloc_sem);
+
+       /* Options for how to handle the fact that ib is now empty.
+        * If this block is not yet incorporated, it can
+        * be quietly deleted.
+        * If it is, but next isn't re-address that one to take the place.
+        * If next is incorporated and dirty, re-address it likewise
+        * If next is not dirty, update parent and possibly re-address next
+        * If no next, and parent not empty, just delete
+        * If parent becomes empty, recurse upwards.
+        */
+       if (test_and_clear_bit(B_PrimaryRef, &ib->b.flags)) {
+               /* This was not incorporated yet */
+               int cr = incorp_index(&ib->b);
+               list_del_init(&ib->b.siblings);
+               if (test_and_clear_bit(B_Dirty, &ib->b.flags))
+                       cr++;
+               lafs_space_return(fs, cr);
+       } else if (ib->b.siblings.next != &ib->b.parent->children &&
+                  (nxt = list_entry(ib->b.siblings.next, struct indexblock, b.siblings)) &&
+                  test_bit(B_PrimaryRef, &nxt->b.flags)) {
+               /* Next block is not incorporated yet, so simply re-address
+                * it to replace this block.
+                */
+               nxt->b.fileaddr = ib->b.fileaddr;
+               set_bit(B_PhysValid, &nxt->b.flags);
+               lafs_hash_iblock(nxt);
+               clear_bit(B_PrimaryRef, &nxt->b.flags);
+               putiref(ib, MKREF(primary));
+       } else if ((next = remove_from_index(ib->b.parent, ib->b.fileaddr)) != 0) {
+               /* Found a 'next' block in the parent and have updated the parent.
+                * Need to check for block in cache and re-address
+                */
+               nxt = lafs_iblock_get(ib->b.inode, next, ib->depth, 0, MKREF(inc2));
+               if (nxt) {
+                       nxt->b.fileaddr = ib->b.fileaddr;
+                       lafs_hash_iblock(nxt);
+                       putiref(nxt, MKREF(inc2));
                }
+       } else if (ib->b.parent->b.fileaddr == ib->b.fileaddr &&
+                  !test_bit(B_InoIdx, &ib->b.parent->b.flags)) {
+               /* The parent has just become empty and needs to be 
+                * removed from it's parent.
+                */
+               ib = ib->b.parent;
+               goto recurse;
        }
+       /* Current hold iolock on ib->b.parent, and refcnt on orig_ib
+        * which is a descendent.
+        * Need to return with iolock on orig_ib.
+        */
+       lafs_iolock_block(&orig_ib->b);
+       lafs_iounlock_block(&ib->b.parent->b);
 }
 
 /***************************************************************