]> git.neil.brown.name Git - LaFS.git/commitdiff
Make sure inodes don't get forgotten during cleaning.
authorNeilBrown <neilb@suse.de>
Sat, 8 Aug 2009 04:23:40 +0000 (14:23 +1000)
committerNeilBrown <neilb@suse.de>
Sat, 8 Aug 2009 04:23:40 +0000 (14:23 +1000)
During cleaning and other times when inodes might have dirty
index blocks we don't want the inode to be pushed out due to
apparently not being in use.

However there is a difficulty in holding a reference on the inode as
that cause the truncate following a final unlink to be delayed.

So to compromise, whenever the InoIdx block for an inode is pinned,
hold a reference to the inode as long as the link count is non-zero.

Once the link count becomes zero, we drop the extra ref and if this
leads to the inode being deleted, the current delaying of this
deletion (while the dblock is references) will keep the inode around
just long enough.

block.c
dir.c
index.c
inode.c
lafs.h
state.h

diff --git a/block.c b/block.c
index 038c5c969577a5dd691c5216d0ce0cb10205314a..ba828032e51f2205a5323bab670d937267e96c2b 100644 (file)
--- a/block.c
+++ b/block.c
@@ -613,6 +613,7 @@ lafs_erase_iblock(struct indexblock *b)
                                   [!!test_bit(B_Phase1, &b->b.flags)]);
                clear_bit(B_Pinned, &b->b.flags);
                spin_unlock(&fs->lock);
+               lafs_inode_checkpin(b->b.inode);
                if (!test_bit(B_Root, &b->b.flags))
                        lafs_refile(&b->b.parent->b, 0);
                if (onlist)
diff --git a/dir.c b/dir.c
index b1a35a84bb0cb82ee3b717c8b740ce15b6c72225..b87c0d5f959dfd2786bacd905ea73a2b949b44d3 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -663,6 +663,7 @@ lafs_create(struct inode *dir, struct dentry *de, int mode,
        ino->i_nlink = 1;
        LAFSI(ino)->md.file.parent = dir->i_ino;
        lafs_dirty_inode(ino);
+       lafs_inode_checkpin(ino);
        dir_log_commit(&uh, fs, dir, &de->d_name, ino->i_ino, DIROP_LINK, NULL);
        dir_create_commit(&doh, fs, dir, de->d_name.name, de->d_name.len,
                          ino->i_ino, DT_REG);
@@ -720,6 +721,7 @@ lafs_link(struct dentry *from, struct inode *dir, struct dentry *to)
 
        inode_inc_link_count(inode);
        lafs_dirty_inode(inode);
+       lafs_inode_checkpin(inode);
        clear_bit(B_PinPending, &inodb->b.flags);
        putdref(inodb, MKREF(inode_update));
 
@@ -788,6 +790,7 @@ lafs_unlink(struct inode *dir, struct dentry *de)
                lafs_orphan_commit(&oi);
        lafs_checkpoint_unlock(fs);
        lafs_dirty_inode(inode);
+       lafs_inode_checkpin(inode);
        lafs_dir_handle_orphans(dir);
        putdref(inodb, MKREF(inode_update));
        clear_bit(B_PinPending, &inodb->b.flags);
@@ -854,6 +857,8 @@ lafs_rmdir(struct inode *dir, struct dentry *de)
        clear_bit(B_PinPending, &inodb->b.flags);
        putdref(inodb, MKREF(inode_update));
        lafs_dirty_inode(dir);
+       lafs_inode_checkpin(inode);
+       lafs_inode_checkpin(dir);
        lafs_checkpoint_unlock(fs);
        lafs_dir_handle_orphans(dir);
        return 0;
@@ -927,6 +932,7 @@ lafs_symlink(struct inode *dir, struct dentry *de,
        putdref(b, MKREF(symlink));
        ino->i_size = l;
        lafs_dirty_inode(ino);
+       lafs_inode_checkpin(ino);
 
        dir_log_commit(&uh, fs, dir, &de->d_name, ino->i_ino, DIROP_LINK, NULL);
        dir_create_commit(&doh, fs, dir, de->d_name.name, de->d_name.len,
@@ -990,6 +996,8 @@ lafs_mkdir(struct inode *dir, struct dentry *de, int mode)
        ino->i_nlink = 2; /* From parent, and from '.' */
        lafs_dirty_inode(dir);
        lafs_dirty_inode(ino);
+       lafs_inode_checkpin(dir);
+       lafs_inode_checkpin(ino);
        lafs_orphan_release(fs, inodb);
        dir_create_commit(&doh, fs, dir, de->d_name.name, de->d_name.len,
                          ino->i_ino, DT_DIR);
@@ -1063,6 +1071,7 @@ lafs_mknod(struct inode *dir, struct dentry *de, int mode,
        LAFSI(ino)->md.file.parent = dir->i_ino;
        ino->i_nlink = 1;
        lafs_dirty_inode(ino);
+       lafs_inode_checkpin(ino);
 
        dir_log_commit(&uh, fs, dir, &de->d_name, ino->i_ino, DIROP_LINK, NULL);
        dir_create_commit(&doh, fs, dir, de->d_name.name, de->d_name.len,
@@ -1209,8 +1218,11 @@ lafs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        inode_dec_link_count(new_inode);
                inode_dec_link_count(new_inode);
                lafs_dirty_inode(new_inode);
+               lafs_inode_checkpin(new_inode);
        }
        lafs_dirty_inode(old_inode);
+       lafs_inode_checkpin(new_dir);
+       lafs_inode_checkpin(old_dir);
        clear_bit(B_PinPending, &olddb->b.flags);
        putdref(olddb, MKREF(inode_update));
        if (new_inode) {
diff --git a/index.c b/index.c
index 361ff55e2ac92407a0d0901bacb48cf5a31049d9..f492b3239db5458cd4bdd1f7f63e4c79e77c0087 100644 (file)
--- a/index.c
+++ b/index.c
@@ -391,6 +391,7 @@ void lafs_pin_block_ph(struct block *b, int ph)
 
                        /* FIXME unpin the db if it is in the same phase?? */
                        spin_unlock(&ino->i_mapping->private_lock);
+                       lafs_inode_checkpin(p->inode);
                        ino = db->b.inode;
                        spin_lock(&ino->i_mapping->private_lock);
                }
@@ -481,6 +482,7 @@ void lafs_phase_flip(struct fs *fs, struct block *b)
                                atomic_dec(&b->parent->pincnt[oldphase]);
                                lafs_refile(&b->parent->b, 0);
                        }
+                       lafs_inode_checkpin(b->inode);
                }
                lafs_iounlock_block(b, 0);
                return;
@@ -726,6 +728,7 @@ void lafs_refile(struct block *b, int dec)
                int ph;
                int free_me = 0;
                int onlru = 0;
+               struct inode *checkpin = NULL;
                spin_lock(&lafs_hash_lock);
 
                if (!list_empty(&b->lru) &&
@@ -770,6 +773,9 @@ void lafs_refile(struct block *b, int dec)
                                                dec = onlru;
                                        spin_unlock(&fs->lock);
                                }
+                               if (test_bit(B_InoIdx, &b->flags) &&
+                                   b->inode->i_nlink)
+                                       checkpin = b->inode;
                                if (!test_bit(B_Root, &b->flags)) {
                                        atomic_dec(&b->parent->pincnt[ph]);
                                        if (next_parent != &b->parent->b) {
@@ -945,6 +951,9 @@ void lafs_refile(struct block *b, int dec)
                        physref = 0;
                }
 
+               if (checkpin)
+                       lafs_inode_checkpin(checkpin);
+
                if (free_me)
                        kfree(b);
                b = NULL;
diff --git a/inode.c b/inode.c
index 9ef51eb5c6a37b293db28888527bd8fecf3b1411..ff7e74d3cc3c6e137186532553dd434f1030e329 100644 (file)
--- a/inode.c
+++ b/inode.c
@@ -264,6 +264,41 @@ if (!ino->i_nlink) ino->i_nlink = 1;
        return err;
 }
 
+void lafs_inode_checkpin(struct inode *ino)
+{
+       /* Make sure I_Pinned is set correctly.
+        * It should be set precisely if i_nlink is non-zero,
+        * and ->iblock is B_Pinned.
+        * When it is set, we own a reference to the inode.
+        *
+        * This needs to be called whenever we change
+        * i_nlink, and whenever we pin or unpin an InoIdx
+        * block.
+        */
+       if (ino->i_nlink == 0) {
+               /* I_Pinned should not be set */
+               if (test_and_clear_bit(I_Pinned, &LAFSI(ino)->iflags))
+                       iput(ino);
+       } else {
+               /* Need to check if iblock is Pinned. */
+               struct indexblock *ib = NULL;
+               if (LAFSI(ino)->iblock) {
+                       spin_lock(&ino->i_data.private_lock);
+                       ib = LAFSI(ino)->iblock;
+                       if (ib && !test_bit(B_Pinned, &ib->b.flags))
+                               ib = NULL;
+                       spin_unlock(&ino->i_data.private_lock);
+               }
+               if (ib) {
+                       if (!test_and_set_bit(I_Pinned, &LAFSI(ino)->iflags))
+                               igrab(ino);
+               } else {
+                       if (test_and_clear_bit(I_Pinned, &LAFSI(ino)->iflags))
+                               iput(ino);
+               }
+       }
+}
+
 struct datablock *lafs_inode_dblock(struct inode *ino, int async, REFARG)
 {
        struct datablock *db = NULL;
diff --git a/lafs.h b/lafs.h
index 8e3baa6ba17734e34ef19de4bf275bc7560b2c5f..5637fce14f936f87ffbcbffd66ca014944ec0aa4 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -119,6 +119,7 @@ int __must_check lafs_mount(struct fs *fs);
 struct inode *lafs_iget(struct super_block *fs, ino_t inum, int async);
 struct inode *lafs_iget_fs(struct fs *fs, int fsnum, int inum, int async);
 int __must_check lafs_import_inode(struct inode *ino, struct datablock *b);
+void lafs_inode_checkpin(struct inode *ino);
 void lafs_clear_inode(struct inode *ino);
 void lafs_delete_inode(struct inode *ino);
 void lafs_dirty_inode(struct inode *ino);
diff --git a/state.h b/state.h
index 58ac84705f2b0a8881d64243d90c822d078efbd7..955ab9a11206e476117a6aa2ae584bdd0340a862 100644 (file)
--- a/state.h
+++ b/state.h
@@ -460,6 +460,9 @@ struct lafs_inode {
 #define        I_Deleting 3    /* preserve the inode while truncate-on-delete happens */
 #define I_Destroyed 4  /* inode destroy has been delayed */
 #define        I_Trunc 5       /* a truncation is in process */
+#define        I_Pinned 6      /* InoIdx is pinned, i_nlink is non-zero, and consequently
+                        * we own an extra ref to the inode.
+                        */
 /* next three indicate if we hold a reference on the relevant qent */
 #define        I_QUid  8
 #define        I_QGid  9