Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[pandora-kernel.git] / fs / gfs2 / glops.c
index 25eeb2b..da21eca 100644 (file)
 #include "rgrp.h"
 #include "util.h"
 #include "trans.h"
+#include "dir.h"
 
 /**
- * ail_empty_gl - remove all buffers for a given lock from the AIL
+ * __gfs2_ail_flush - remove all buffers for a given lock from the AIL
  * @gl: the glock
  *
  * None of the buffers should be dirty, locked, or pinned.
  */
 
-static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
+static void __gfs2_ail_flush(struct gfs2_glock *gl)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
        struct list_head *head = &gl->gl_ail_list;
        struct gfs2_bufdata *bd;
        struct buffer_head *bh;
-       struct gfs2_trans tr;
-
-       memset(&tr, 0, sizeof(tr));
-       tr.tr_revokes = atomic_read(&gl->gl_ail_count);
-
-       if (!tr.tr_revokes)
-               return;
-
-       /* A shortened, inline version of gfs2_trans_begin() */
-       tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
-       tr.tr_ip = (unsigned long)__builtin_return_address(0);
-       INIT_LIST_HEAD(&tr.tr_list_buf);
-       gfs2_log_reserve(sdp, tr.tr_reserved);
-       BUG_ON(current->journal_info);
-       current->journal_info = &tr;
 
        spin_lock(&sdp->sd_ail_lock);
        while (!list_empty(head)) {
@@ -62,10 +48,10 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
                                bd_ail_gl_list);
                bh = bd->bd_bh;
                gfs2_remove_from_ail(bd);
-               spin_unlock(&sdp->sd_ail_lock);
-
                bd->bd_bh = NULL;
                bh->b_private = NULL;
+               spin_unlock(&sdp->sd_ail_lock);
+
                bd->bd_blkno = bh->b_blocknr;
                gfs2_log_lock(sdp);
                gfs2_assert_withdraw(sdp, !buffer_busy(bh));
@@ -76,11 +62,51 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
        }
        gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
        spin_unlock(&sdp->sd_ail_lock);
+}
+
+
+static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
+{
+       struct gfs2_sbd *sdp = gl->gl_sbd;
+       struct gfs2_trans tr;
+
+       memset(&tr, 0, sizeof(tr));
+       tr.tr_revokes = atomic_read(&gl->gl_ail_count);
+
+       if (!tr.tr_revokes)
+               return;
+
+       /* A shortened, inline version of gfs2_trans_begin() */
+       tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
+       tr.tr_ip = (unsigned long)__builtin_return_address(0);
+       INIT_LIST_HEAD(&tr.tr_list_buf);
+       gfs2_log_reserve(sdp, tr.tr_reserved);
+       BUG_ON(current->journal_info);
+       current->journal_info = &tr;
+
+       __gfs2_ail_flush(gl);
 
        gfs2_trans_end(sdp);
        gfs2_log_flush(sdp, NULL);
 }
 
+void gfs2_ail_flush(struct gfs2_glock *gl)
+{
+       struct gfs2_sbd *sdp = gl->gl_sbd;
+       unsigned int revokes = atomic_read(&gl->gl_ail_count);
+       int ret;
+
+       if (!revokes)
+               return;
+
+       ret = gfs2_trans_begin(sdp, 0, revokes);
+       if (ret)
+               return;
+       __gfs2_ail_flush(gl);
+       gfs2_trans_end(sdp);
+       gfs2_log_flush(sdp, NULL);
+}
+
 /**
  * rgrp_go_sync - sync out the metadata for this glock
  * @gl: the glock
@@ -193,11 +219,14 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
                if (ip) {
                        set_bit(GIF_INVALID, &ip->i_flags);
                        forget_all_cached_acls(&ip->i_inode);
+                       gfs2_dir_hash_inval(ip);
                }
        }
 
-       if (ip == GFS2_I(gl->gl_sbd->sd_rindex))
+       if (ip == GFS2_I(gl->gl_sbd->sd_rindex)) {
+               gfs2_log_flush(gl->gl_sbd, NULL);
                gl->gl_sbd->sd_rindex_uptodate = 0;
+       }
        if (ip && S_ISREG(ip->i_inode.i_mode))
                truncate_inode_pages(ip->i_inode.i_mapping, 0);
 }
@@ -226,6 +255,120 @@ static int inode_go_demote_ok(const struct gfs2_glock *gl)
        return 1;
 }
 
+/**
+ * gfs2_set_nlink - Set the inode's link count based on on-disk info
+ * @inode: The inode in question
+ * @nlink: The link count
+ *
+ * If the link count has hit zero, it must never be raised, whatever the
+ * on-disk inode might say. When new struct inodes are created the link
+ * count is set to 1, so that we can safely use this test even when reading
+ * in on disk information for the first time.
+ */
+
+static void gfs2_set_nlink(struct inode *inode, u32 nlink)
+{
+       /*
+        * We will need to review setting the nlink count here in the
+        * light of the forthcoming ro bind mount work. This is a reminder
+        * to do that.
+        */
+       if ((inode->i_nlink != nlink) && (inode->i_nlink != 0)) {
+               if (nlink == 0)
+                       clear_nlink(inode);
+               else
+                       inode->i_nlink = nlink;
+       }
+}
+
+static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
+{
+       const struct gfs2_dinode *str = buf;
+       struct timespec atime;
+       u16 height, depth;
+
+       if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
+               goto corrupt;
+       ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino);
+       ip->i_inode.i_mode = be32_to_cpu(str->di_mode);
+       ip->i_inode.i_rdev = 0;
+       switch (ip->i_inode.i_mode & S_IFMT) {
+       case S_IFBLK:
+       case S_IFCHR:
+               ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
+                                          be32_to_cpu(str->di_minor));
+               break;
+       };
+
+       ip->i_inode.i_uid = be32_to_cpu(str->di_uid);
+       ip->i_inode.i_gid = be32_to_cpu(str->di_gid);
+       gfs2_set_nlink(&ip->i_inode, be32_to_cpu(str->di_nlink));
+       i_size_write(&ip->i_inode, be64_to_cpu(str->di_size));
+       gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks));
+       atime.tv_sec = be64_to_cpu(str->di_atime);
+       atime.tv_nsec = be32_to_cpu(str->di_atime_nsec);
+       if (timespec_compare(&ip->i_inode.i_atime, &atime) < 0)
+               ip->i_inode.i_atime = atime;
+       ip->i_inode.i_mtime.tv_sec = be64_to_cpu(str->di_mtime);
+       ip->i_inode.i_mtime.tv_nsec = be32_to_cpu(str->di_mtime_nsec);
+       ip->i_inode.i_ctime.tv_sec = be64_to_cpu(str->di_ctime);
+       ip->i_inode.i_ctime.tv_nsec = be32_to_cpu(str->di_ctime_nsec);
+
+       ip->i_goal = be64_to_cpu(str->di_goal_meta);
+       ip->i_generation = be64_to_cpu(str->di_generation);
+
+       ip->i_diskflags = be32_to_cpu(str->di_flags);
+       ip->i_eattr = be64_to_cpu(str->di_eattr);
+       /* i_diskflags and i_eattr must be set before gfs2_set_inode_flags() */
+       gfs2_set_inode_flags(&ip->i_inode);
+       height = be16_to_cpu(str->di_height);
+       if (unlikely(height > GFS2_MAX_META_HEIGHT))
+               goto corrupt;
+       ip->i_height = (u8)height;
+
+       depth = be16_to_cpu(str->di_depth);
+       if (unlikely(depth > GFS2_DIR_MAX_DEPTH))
+               goto corrupt;
+       ip->i_depth = (u8)depth;
+       ip->i_entries = be32_to_cpu(str->di_entries);
+
+       if (S_ISREG(ip->i_inode.i_mode))
+               gfs2_set_aops(&ip->i_inode);
+
+       return 0;
+corrupt:
+       gfs2_consist_inode(ip);
+       return -EIO;
+}
+
+/**
+ * gfs2_inode_refresh - Refresh the incore copy of the dinode
+ * @ip: The GFS2 inode
+ *
+ * Returns: errno
+ */
+
+int gfs2_inode_refresh(struct gfs2_inode *ip)
+{
+       struct buffer_head *dibh;
+       int error;
+
+       error = gfs2_meta_inode_buffer(ip, &dibh);
+       if (error)
+               return error;
+
+       if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), dibh, GFS2_METATYPE_DI)) {
+               brelse(dibh);
+               return -EIO;
+       }
+
+       error = gfs2_dinode_in(ip, dibh->b_data);
+       brelse(dibh);
+       clear_bit(GIF_INVALID, &ip->i_flags);
+
+       return error;
+}
+
 /**
  * inode_go_lock - operation done after an inode lock is locked by a process
  * @gl: the glock
@@ -409,7 +552,6 @@ const struct gfs2_glock_operations gfs2_inode_glops = {
        .go_lock = inode_go_lock,
        .go_dump = inode_go_dump,
        .go_type = LM_TYPE_INODE,
-       .go_min_hold_time = HZ / 5,
        .go_flags = GLOF_ASPACE,
 };
 
@@ -420,7 +562,6 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = {
        .go_unlock = rgrp_go_unlock,
        .go_dump = gfs2_rgrp_dump,
        .go_type = LM_TYPE_RGRP,
-       .go_min_hold_time = HZ / 5,
        .go_flags = GLOF_ASPACE,
 };