]> git.neil.brown.name Git - LaFS.git/commitdiff
Store atime safely when dirty_inode is called.
authorNeilBrown <neilb@suse.de>
Fri, 4 Mar 2011 23:44:22 +0000 (10:44 +1100)
committerNeilBrown <neilb@suse.de>
Fri, 4 Mar 2011 23:44:22 +0000 (10:44 +1100)
When dirty_inode is called the atime might have been the only thing
updated.
If it was, then we want to record it in the atime file and not mark
the inode dirty.
If it wasn't (or if the inode is easily marked dirty) we want to
simply mark the inode dirty and make sure the correct atime is
recorded when the inode is flushed.

So detect the cases based on whether the inode dblock is available and
pinned.  When it isn't just update the atime-delta in the atime file.

Signed-off-by: NeilBrown <neilb@suse.de>
inode.c

diff --git a/inode.c b/inode.c
index 681b8fe701f6189cd1ec2039941fca3d3f774e8c..1cb204037f9d471e5d211404ef944d44a35f9995 100644 (file)
--- a/inode.c
+++ b/inode.c
@@ -543,6 +543,93 @@ void lafs_add_atime_offset(struct timespec *atime, int offset)
        }
 }
 
+static int normalise(int *mantissa)
+{
+       /* Shift down until value can be stored in 12 bits:
+        * Top bit will be '1', so only 11 bits needed.
+        * Not used on values below 2048.
+        */
+       int shift = 0;
+       while (*mantissa >= 4096) {
+               *mantissa >>= 1;
+               shift ++;
+       }
+       return shift;
+}
+
+static int update_atime_delta(struct inode *ino)
+{
+       /* calculate new delta to show the difference between
+        * i_atime and i_accesstime
+        */
+       int rv;
+       if (timespec_compare(&ino->i_atime,
+                            &LAFSI(ino)->md.file.i_accesstime) <= 0) {
+               /* We cannot store negative delta so if i_atime is in the
+                * past, just store zero
+                */
+               rv = 0;
+       } else {
+               struct timespec diff;
+               int shift;
+
+               diff = timespec_sub(ino->i_atime,
+                                   LAFSI(ino)->md.file.i_accesstime);
+               if (diff.tv_sec >= 2048) {
+                       /* Just store the seconds */
+                       rv = diff.tv_sec;
+                       shift = normalise(&rv) + 11;
+               } else {
+                       /* Store the milliseconds */
+                       int rv = diff.tv_nsec / 1000000;
+                       rv += diff.tv_sec * 1000;
+                       if (rv >= 2048)
+                               shift = normalise(&rv) + 1;
+                       else
+                               shift = 0;
+               }
+               if (shift > 31)
+                       rv = 0xFFFF;
+               else {
+                       rv &= 0x7ff;
+                       rv <<= 5;
+                       rv |= shift;
+               }
+       }
+       if (LAFSI(ino)->md.file.atime_offset == rv)
+               return 0;
+
+       LAFSI(ino)->md.file.atime_offset = rv;
+       return 1;
+}
+
+static void store_atime_delta(struct inode *ino)
+{
+       struct inode *at;
+       u32 bnum;
+       struct datablock *b;
+       u16 *atp;
+       int i;
+
+       if (!test_bit(I_AccessTime, &LAFSI(ino)->iflags))
+               /* sorry, nothing we can do here */
+               return;
+
+       /* We own a reference, so this lookup must succeed */
+       at = LAFSI(ino_from_sb(ino->i_sb))->md.fs.accesstime;
+       bnum = ino->i_ino >> (at->i_blkbits-1);
+       b = lafs_get_block(at, bnum, NULL, GFP_NOFS, MKREF(store_atime));
+       BUG_ON(!b);
+       atp = map_dblock(b);
+       i = (ino->i_ino * 2) & ((1<<at->i_blkbits)-1);
+       if (le16_to_cpu(atp[i]) != LAFSI(ino)->md.file.atime_offset) {
+               atp[i] = cpu_to_le16(LAFSI(ino)->md.file.atime_offset);
+               lafs_dirty_dblock(b);
+       }
+       unmap_dblock(b, atp);
+       putdref(b, MKREF(store_atime));
+}
+
 void lafs_inode_checkpin(struct inode *ino)
 {
        /* Make sure I_Pinned is set correctly.
@@ -1152,7 +1239,17 @@ void lafs_dirty_inode(struct inode *ino)
         * 2/ by writeout before requesting a write - to update mtime
         * 3/ by read to update atime
         *
-        * As we don't know which, there is not much we can do.
+        * We want to handle atime updates carefully as they may not change
+        * the stored inode itself.
+        * For all other updates, the inode dblock exists and is pinned.
+        * In those cases we will be updating the inode and so can store
+        * the atime exactly.
+        * For an atime update, the dblock may not exists, or may not be
+        * Pinned.  If it isn't then we don't want to make the inode dirty
+        * but only want to update the delta stored in the atime file.
+        * The block for that should already be pinned.
+        *
+        *
         * We mustn't update the data block as it could be in
         * writeout and we cannot always wait safely.
         * So require that anyone who really cares, dirties the datablock
@@ -1166,8 +1263,31 @@ void lafs_dirty_inode(struct inode *ino)
         */
        struct timespec now;
        struct inode *filesys;
+       int atime_only = 1;
+
+       if (LAFSI(ino)->dblock) {
+               struct datablock *db;
+               spin_lock(&ino->i_data.private_lock);
+               db = LAFSI(ino)->dblock;
+               if (db && test_bit(B_Pinned, &db->b.flags))
+                       atime_only = 0;
+               spin_unlock(&ino->i_data.private_lock);
+       }
+
+       if (atime_only) {
+               if (update_atime_delta(ino))
+                       store_atime_delta(ino);
+               return;
+       }
+       
+
        set_bit(I_Dirty, &LAFSI(ino)->iflags);
        ino->i_sb->s_dirt = 1;
+       LAFSI(ino)->md.file.i_accesstime = ino->i_atime;
+       if (LAFSI(ino)->md.file.atime_offset) {
+               LAFSI(ino)->md.file.atime_offset = 0;
+               store_atime_delta(ino);
+       }
 
        now = current_fs_time(ino->i_sb);
        filesys = ino_from_sb(ino->i_sb);
@@ -1350,8 +1470,7 @@ void lafs_inode_fillblock(struct inode *ino)
                l->creationtime = cpu_to_le64(i->creationtime);
                l->modifytime = cpu_to_le64(encode_time(&ino->i_mtime));
                l->ctime = cpu_to_le64(encode_time(&ino->i_ctime));
-               l->accesstime = cpu_to_le64(encode_time(&ino->i_atime));
-               /* FIXME write 0 to accesstime file */
+               l->accesstime = cpu_to_le64(encode_time(&i->i_accesstime));
                l->size = cpu_to_le64(ino->i_size);
                l->parent = cpu_to_le32(i->parent);
                l->linkcount = cpu_to_le32(ino->i_nlink);