From: NeilBrown Date: Mon, 28 Sep 2009 02:26:29 +0000 (+1000) Subject: incorporate: handle empty block correctly. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=e279d734fc788a1679864aea7be98a5e1318c190;p=LaFS.git incorporate: handle empty block correctly. 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. --- diff --git a/index.c b/index.c index b3f2448..9faae6c 100644 --- 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 8474b3c..ce210bd 100644 --- 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, diff --git a/modify.c b/modify.c index 2dfbe20..cfd0d0f 100644 --- 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); } /***************************************************************