]> git.neil.brown.name Git - LaFS.git/commitdiff
Truncate from arbitrary offset in file.
authorNeilBrown <neilb@suse.de>
Thu, 19 Mar 2009 10:46:35 +0000 (21:46 +1100)
committerNeilBrown <neilb@suse.de>
Thu, 19 Mar 2009 10:46:35 +0000 (21:46 +1100)
When truncating a file, allow that we might not be truncating
from the beginning.

inode.c
modify.c

diff --git a/inode.c b/inode.c
index 8597d5d3087fae165fb05943aaad386044f73e49..5d369e1eaa1013206ea1591c5466b6d3bb23749f 100644 (file)
--- a/inode.c
+++ b/inode.c
@@ -474,6 +474,9 @@ static void inode_handle_orphan_loop(struct inode *ino)
 
 static int prune(void *data, u32 addr, u64 paddr, int len)
 {
+       /* This whole index block is being pruned, just account
+        * for everything and it will be cleared afterwards
+        */
        struct indexblock *ib = data;
        struct inode *ino = ib->b.inode;
        struct fs *fs = fs_from_inode(ino);
@@ -487,12 +490,55 @@ static int prune(void *data, u32 addr, u64 paddr, int len)
        return len;
 }
 
+static int prune_some(void *data, u32 addr, u64 paddr, int len)
+{
+       /* Part of this index block is being pruned.  Copy
+        * what addresses we can into uninc_table so that
+        * it can be 'incorporated'
+        * We should probably share some code with
+        * lafs_allocated_block??
+        */
+       struct indexblock *ib = data;
+       struct inode *ino = ib->b.inode;
+       struct fs *fs = fs_from_inode(ino);
+       int ph = !! test_bit(B_Phase1, &ib->b.flags);
+       int i;
+       if (paddr == 0 || len == 0)
+               return 0;
+       dprintk("PRUNE2 %d for %d at %lld\n", addr, len, (long long)paddr);
+       for (i = 0 ; i < len ; i++) {
+               /* FIXME do I need to lock anything or is our IOLock enough? */
+               struct addr *a;
+               spin_lock(&fs->lock);
+               a = &ib->uninc_table.pending_addr
+                       [ib->uninc_table.pending_cnt - 1];
+               if (ib->uninc_table.pending_cnt >= 1 &&
+                   a->fileaddr + a->cnt == addr+i &&
+                   a->physaddr + a->cnt == paddr+i)
+                       a->cnt++;
+               else if (ib->uninc_table.pending_cnt <
+                        ARRAY_SIZE(ib->uninc_table.pending_addr)) {
+                       a++;
+                       a->fileaddr = addr + i;
+                       a->physaddr = paddr + i;
+                       a->cnt = 1;
+                       ib->uninc_table.pending_cnt ++;
+               } else {
+                       spin_unlock(&fs->lock);
+                       break;
+               }
+               spin_unlock(&fs->lock);
+               lafs_summary_update(fs, ino, paddr+i, 0, 0, ph);
+       }
+       return i;
+}
+
 static int lafs_inode_handle_orphan(struct datablock *b)
 {
        struct indexblock *ib, *ib2;
        struct inode *ino = b->my_inode;
        struct fs *fs = fs_from_inode(ino);
-       u32 trunc_next;
+       u32 trunc_next, next_trunc;
 
        /* FIXME if the trunc has completed, we don't really
         * need the iblock...
@@ -534,56 +580,87 @@ static int lafs_inode_handle_orphan(struct datablock *b)
                 * that we can do now, it will do for us.  So just
                 * let it.
                 */
-
+               struct indexblock *tmp;
+               struct indexblock *next;
+               u32 lastaddr;
+
+               if (!test_bit(B_Pinned, &ib->b.flags)) {
+                       /* must be finished */
+                       clear_bit(I_Trunc, &LAFSI(ino)->iflags);
+                       wake_up(&fs->trunc_wait);
+                       putiref(ib, MKREF(inode_handle_orphan));
+                       return 1; /* Try again whenever */
+               }
+               if (fs->checkpointing) {
+                       putiref(ib, MKREF(inode_handle_orphan));
+                       return 2; /* means "Try again after the checkpoint" */
+               }
                /* FIXME I need some sort of lock to safely walk
                 * down this list */
-               while (!list_empty(&ib->children) && !fs->checkpointing) {
-                       struct indexblock *tmp;
-                       ib2 = ib;
-                       /* Find a Pinned descendent of ib which has no
-                        * Pinned descendents.
-                        */
-               retry:
-                       list_for_each_entry(tmp, &ib2->children, b.siblings)
-                               if (test_bit(B_Index, &tmp->b.flags) &&
-                                   test_bit(B_Pinned, &tmp->b.flags)) {
-                                       ib2 = tmp;
-                                       goto retry;
-                               }
 
-                       /* ib2 is an index block with no Pinned children.
-                        * Incorporating it should unpin it.
-                        */
-                       getiref(ib2, MKREF(inode_handle_orphan2));
-                       lafs_iolock_block(&ib2->b);
-                       /* call lafs_incorporate at least once to ensure
-                        * that lafs_erase_iblock gets called
-                        */
-                       do
-                               lafs_incorporate(fs, ib2);
-                       while (ib2->uninc_table.pending_cnt || ib2->uninc);
-                       lafs_iounlock_block(&ib2->b, 0);
-                       putiref(ib2, MKREF(inode_handle_orphan2));
-                       printk(".");
-                       if (!list_empty(&ib2->b.siblings)) {
-                               static int cnt = 10;
-                               printk("looping on %s\n", strblk(&ib2->b));
-                               cnt --;
-                               if (cnt < 0) BUG();
+               ib2 = ib;
+               lastaddr = (i_size_read(ino) +
+                           ino->i_sb->s_blocksize - 1)
+                                   >> ino->i_sb->s_blocksize_bits;
+               /* Find a Pinned descendent of ib which has no
+                * Pinned descendents.
+                * Prefer blocks that are beyond EOF.
+                * If there are none, descend the last block that
+                * is not after EOF and look at its children.
+                */
+               next = ib;
+               while (next) {
+                       ib2 = next;
+                       next = NULL;
+                       list_for_each_entry(tmp, &ib2->children, b.siblings) {
+                               if (!test_bit(B_Index, &tmp->b.flags) ||
+                                   !test_bit(B_Pinned, &tmp->b.flags))
+                                       continue;
+                               if (tmp->b.fileaddr >= lastaddr) {
+                                       next = tmp;
+                                       break;
+                               }
+                               if (next == NULL ||
+                                   tmp->b.fileaddr > next->b.fileaddr)
+                                       next = tmp;
                        }
                }
+               if (ib2->b.fileaddr < lastaddr) {
+                       /* Must be all done */
+                       clear_bit(I_Trunc, &LAFSI(ino)->iflags);
+                       wake_up(&fs->trunc_wait);
+                       putiref(ib, MKREF(inode_handle_orphan));
+                       return 1; /* Try again whenever */
+               }                               
+
+               /* ib2 is an index block beyond EOF with no
+                * Pinned children.
+                * Incorporating it should unpin it.
+                */
+               getiref(ib2, MKREF(inode_handle_orphan2));
+               lafs_iolock_block(&ib2->b);
+               /* call lafs_incorporate at least once to ensure
+                * that lafs_erase_iblock gets called
+                */
+               do
+                       lafs_incorporate(fs, ib2);
+               while (ib2->uninc_table.pending_cnt || ib2->uninc);
+               lafs_iounlock_block(&ib2->b, 0);
+               putiref(ib2, MKREF(inode_handle_orphan2));
+               printk(".");
+               if (!list_empty(&ib2->b.siblings)) {
+                       static int cnt = 10;
+                       printk("looping on %s\n", strblk(&ib2->b));
+                       cnt --;
+                       if (cnt < 0) BUG();
+               }
                putiref(ib, MKREF(inode_handle_orphan));
-               if (fs->checkpointing)
-                       return 2; /* means "Try again after the checkpoint" */
-               /* Truncation has finished */
-               clear_bit(I_Trunc, &LAFSI(ino)->iflags);
-               wake_up(&fs->trunc_wait);
                return 1; /* Try again whenever */
        }
 
        putiref(ib, MKREF(inode_handle_orphan));
 
-       ib = lafs_leaf_find(ino, trunc_next, 1, &trunc_next,
+       ib = lafs_leaf_find(ino, trunc_next, 1, &next_trunc,
                            0, MKREF(inode_handle_orphan3));
 
        if (IS_ERR(ib))
@@ -605,12 +682,35 @@ static int lafs_inode_handle_orphan(struct datablock *b)
 
        lafs_checkpoint_lock(fs);
        if (lafs_reserve_block(&ib->b, ReleaseSpace) == -EAGAIN) {
+               putiref(ib, MKREF(inode_handle_orphan3));
                lafs_checkpoint_unlock(fs);
                return 2;
        }
 
-       LAFSI(ino)->trunc_next = trunc_next;
+       /* It might be that this can happen, in which case
+        * we simply update trunc_next and loop.  But I'd like
+        * to be sure before I implement that
+        */
+       BUG_ON(!test_bit(B_Valid, &ib->b.flags));
+
+       if (ib->b.fileaddr < trunc_next) {
+               /* We only want to truncate part of this index block.
+                * So we copy addresses into uninc_table and then
+                * all lafs_incorporate.
+                * This might cause the index tree to grow, so we
+                * cannot trust next_trunc
+                */
+               lafs_iolock_block(&ib->b);
+               if (ib->uninc_table.pending_cnt == 0 &&
+                   ib->uninc == NULL)
+                       lafs_walk_leaf_index(ib, prune_some, ib);
+               lafs_incorporate(fs, ib);
+               lafs_iounlock_block(&ib->b, 0);
+               return 1;
+       }
+       LAFSI(ino)->trunc_next = next_trunc;
        lafs_iolock_block(&ib->b);
+
        while (ib->uninc_table.pending_cnt || ib->uninc)
                lafs_incorporate(fs, ib);
        lafs_iounlock_block(&ib->b, 0);
@@ -1268,7 +1368,9 @@ void lafs_truncate(struct inode *ino)
        lafs_orphan_commit(&oi);
 
        set_bit(I_Trunc, &LAFSI(ino)->iflags);
-       LAFSI(ino)->trunc_next = 0;
+       LAFSI(ino)->trunc_next = (i_size_read(ino) +
+                                 ino->i_sb->s_blocksize - 1)
+                                         >> ino->i_sb->s_blocksize_bits;
        putdref(db, MKREF(trunc));
 
        inode_handle_orphan_loop(ino);
index eb9136092e69a6c0efa355e3adb1a742378a3cdf..615184b6e6b9d669957e62b6249f6ac58dcdb988 100644 (file)
--- a/modify.c
+++ b/modify.c
@@ -296,6 +296,7 @@ static int incorporate_index(struct block *uninc, char *buf, int size)
                while (i < icnt) {
                        encode48(b, 0ULL);
                        encode32(b, 0);
+                       i++;
                }
                return credits;
        }