Merge ../torvalds-2.6/
[pandora-kernel.git] / fs / nfs / inode.c
index 4845911..6922469 100644 (file)
@@ -146,6 +146,8 @@ nfs_delete_inode(struct inode * inode)
 {
        dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
 
+       truncate_inode_pages(&inode->i_data, 0);
+
        nfs_wb_all(inode);
        /*
         * The following should never happen...
@@ -615,14 +617,18 @@ nfs_zap_caches(struct inode *inode)
        struct nfs_inode *nfsi = NFS_I(inode);
        int mode = inode->i_mode;
 
+       spin_lock(&inode->i_lock);
+
        NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
        NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 
        memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
-               nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
        else
-               nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
+
+       spin_unlock(&inode->i_lock);
 }
 
 static void nfs_zap_acl_cache(struct inode *inode)
@@ -632,7 +638,9 @@ static void nfs_zap_acl_cache(struct inode *inode)
        clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache;
        if (clear_acl_cache != NULL)
                clear_acl_cache(inode);
-       NFS_I(inode)->flags &= ~NFS_INO_INVALID_ACL;
+       spin_lock(&inode->i_lock);
+       NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
+       spin_unlock(&inode->i_lock);
 }
 
 /*
@@ -739,7 +747,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                        inode->i_fop = &nfs_dir_operations;
                        if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
                            && fattr->size <= NFS_LIMIT_READDIRPLUS)
-                               NFS_FLAGS(inode) |= NFS_INO_ADVISE_RDPLUS;
+                               set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
                } else if (S_ISLNK(inode->i_mode))
                        inode->i_op = &nfs_symlink_inode_operations;
                else
@@ -814,55 +822,84 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                nfs_wb_all(inode);
        }
        error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
-       if (error == 0) {
+       if (error == 0)
                nfs_refresh_inode(inode, &fattr);
+       nfs_end_data_update(inode);
+       unlock_kernel();
+       return error;
+}
+
+/**
+ * nfs_setattr_update_inode - Update inode metadata after a setattr call.
+ * @inode: pointer to struct inode
+ * @attr: pointer to struct iattr
+ *
+ * Note: we do this in the *proc.c in order to ensure that
+ *       it works for things like exclusive creates too.
+ */
+void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
+{
+       if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
                if ((attr->ia_valid & ATTR_MODE) != 0) {
-                       int mode;
-                       mode = inode->i_mode & ~S_IALLUGO;
-                       mode |= attr->ia_mode & S_IALLUGO;
+                       int mode = attr->ia_mode & S_IALLUGO;
+                       mode |= inode->i_mode & ~S_IALLUGO;
                        inode->i_mode = mode;
                }
                if ((attr->ia_valid & ATTR_UID) != 0)
                        inode->i_uid = attr->ia_uid;
                if ((attr->ia_valid & ATTR_GID) != 0)
                        inode->i_gid = attr->ia_gid;
-               if ((attr->ia_valid & ATTR_SIZE) != 0) {
-                       inode->i_size = attr->ia_size;
-                       vmtruncate(inode, attr->ia_size);
-               }
+               spin_lock(&inode->i_lock);
+               NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+               spin_unlock(&inode->i_lock);
+       }
+       if ((attr->ia_valid & ATTR_SIZE) != 0) {
+               inode->i_size = attr->ia_size;
+               vmtruncate(inode, attr->ia_size);
        }
-       if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
-               NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-       nfs_end_data_update(inode);
-       unlock_kernel();
-       return error;
+}
+
+static int nfs_wait_schedule(void *word)
+{
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       schedule();
+       return 0;
 }
 
 /*
  * Wait for the inode to get unlocked.
- * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
  */
-static int
-nfs_wait_on_inode(struct inode *inode, int flag)
+static int nfs_wait_on_inode(struct inode *inode)
 {
        struct rpc_clnt *clnt = NFS_CLIENT(inode);
        struct nfs_inode *nfsi = NFS_I(inode);
-
+       sigset_t oldmask;
        int error;
-       if (!(NFS_FLAGS(inode) & flag))
-               return 0;
+
        atomic_inc(&inode->i_count);
-       error = nfs_wait_event(clnt, nfsi->nfs_i_wait,
-                               !(NFS_FLAGS(inode) & flag));
+       rpc_clnt_sigmask(clnt, &oldmask);
+       error = wait_on_bit_lock(&nfsi->flags, NFS_INO_REVALIDATING,
+                                       nfs_wait_schedule, TASK_INTERRUPTIBLE);
+       rpc_clnt_sigunmask(clnt, &oldmask);
        iput(inode);
+
        return error;
 }
 
+static void nfs_wake_up_inode(struct inode *inode)
+{
+       struct nfs_inode *nfsi = NFS_I(inode);
+
+       clear_bit(NFS_INO_REVALIDATING, &nfsi->flags);
+       smp_mb__after_clear_bit();
+       wake_up_bit(&nfsi->flags, NFS_INO_REVALIDATING);
+}
+
 int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 {
        struct inode *inode = dentry->d_inode;
-       struct nfs_inode *nfsi = NFS_I(inode);
-       int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME;
+       int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
        int err;
 
        if (__IS_FLG(inode, MS_NOATIME))
@@ -1008,7 +1045,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
        struct nfs_fattr fattr;
        struct nfs_inode *nfsi = NFS_I(inode);
        unsigned long verifier;
-       unsigned int flags;
+       unsigned long cache_validity;
 
        dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
                inode->i_sb->s_id, (long long)NFS_FILEID(inode));
@@ -1019,18 +1056,19 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
        if (NFS_STALE(inode))
                goto out_nowait;
 
-       while (NFS_REVALIDATING(inode)) {
-               status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
-               if (status < 0)
-                       goto out_nowait;
-               if (NFS_ATTRTIMEO(inode) == 0)
-                       continue;
-               if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
-                       continue;
-               status = NFS_STALE(inode) ? -ESTALE : 0;
-               goto out_nowait;
+       status = nfs_wait_on_inode(inode);
+       if (status < 0)
+               goto out;
+       if (NFS_STALE(inode)) {
+               status = -ESTALE;
+               /* Do we trust the cached ESTALE? */
+               if (NFS_ATTRTIMEO(inode) != 0) {
+                       if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME)) {
+                               /* no */
+                       } else
+                               goto out;
+               }
        }
-       NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
 
        /* Protect against RPC races by saving the change attribute */
        verifier = nfs_save_change_attribute(inode);
@@ -1042,7 +1080,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                if (status == -ESTALE) {
                        nfs_zap_caches(inode);
                        if (!S_ISDIR(inode->i_mode))
-                               NFS_FLAGS(inode) |= NFS_INO_STALE;
+                               set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
                }
                goto out;
        }
@@ -1054,25 +1092,30 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                         (long long)NFS_FILEID(inode), status);
                goto out;
        }
-       flags = nfsi->flags;
-       nfsi->flags &= ~NFS_INO_REVAL_PAGECACHE;
+       spin_lock(&inode->i_lock);
+       cache_validity = nfsi->cache_validity;
+       nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
+
        /*
         * We may need to keep the attributes marked as invalid if
         * we raced with nfs_end_attr_update().
         */
        if (verifier == nfsi->cache_change_attribute)
-               nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
-       /* Do the page cache invalidation */
+               nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
+       spin_unlock(&inode->i_lock);
+
        nfs_revalidate_mapping(inode, inode->i_mapping);
-       if (flags & NFS_INO_INVALID_ACL)
+
+       if (cache_validity & NFS_INO_INVALID_ACL)
                nfs_zap_acl_cache(inode);
+
        dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode));
 
-out:
-       NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
-       wake_up(&nfsi->nfs_i_wait);
+ out:
+       nfs_wake_up_inode(inode);
+
  out_nowait:
        unlock_kernel();
        return status;
@@ -1096,7 +1139,7 @@ int nfs_attribute_timeout(struct inode *inode)
  */
 int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
-       if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
+       if (!(NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
                        && !nfs_attribute_timeout(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
@@ -1111,19 +1154,23 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       if (nfsi->flags & NFS_INO_INVALID_DATA) {
+       if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
                if (S_ISREG(inode->i_mode)) {
                        if (filemap_fdatawrite(mapping) == 0)
                                filemap_fdatawait(mapping);
                        nfs_wb_all(inode);
                }
                invalidate_inode_pages2(mapping);
-               nfsi->flags &= ~NFS_INO_INVALID_DATA;
+
+               spin_lock(&inode->i_lock);
+               nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
                if (S_ISDIR(inode->i_mode)) {
                        memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
                        /* This ensures we revalidate child dentries */
                        nfsi->cache_change_attribute++;
                }
+               spin_unlock(&inode->i_lock);
+
                dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
                                inode->i_sb->s_id,
                                (long long)NFS_FILEID(inode));
@@ -1153,10 +1200,12 @@ void nfs_end_data_update(struct inode *inode)
 
        if (!nfs_have_delegation(inode, FMODE_READ)) {
                /* Mark the attribute cache for revalidation */
-               nfsi->flags |= NFS_INO_INVALID_ATTR;
+               spin_lock(&inode->i_lock);
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                /* Directories and symlinks: invalidate page cache too */
                if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-                       nfsi->flags |= NFS_INO_INVALID_DATA;
+                       nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+               spin_unlock(&inode->i_lock);
        }
        nfsi->cache_change_attribute ++;
        atomic_dec(&nfsi->data_updates);
@@ -1181,6 +1230,8 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
        if (nfs_have_delegation(inode, FMODE_READ))
                return 0;
 
+       spin_lock(&inode->i_lock);
+
        /* Are we in the process of updating data on the server? */
        data_unstable = nfs_caches_unstable(inode);
 
@@ -1189,19 +1240,23 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
                                && nfsi->change_attr == fattr->pre_change_attr)
                        nfsi->change_attr = fattr->change_attr;
                if (nfsi->change_attr != fattr->change_attr) {
-                       nfsi->flags |= NFS_INO_INVALID_ATTR;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                        if (!data_unstable)
-                               nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+                               nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
                }
        }
 
-       if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+       if ((fattr->valid & NFS_ATTR_FATTR) == 0) {
+               spin_unlock(&inode->i_lock);
                return 0;
+       }
 
        /* Has the inode gone and changed behind our back? */
        if (nfsi->fileid != fattr->fileid
-                       || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+                       || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+               spin_unlock(&inode->i_lock);
                return -EIO;
+       }
 
        cur_size = i_size_read(inode);
        new_isize = nfs_size_to_loff_t(fattr->size);
@@ -1216,30 +1271,31 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
 
        /* Verify a few of the more important attributes */
        if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
-               nfsi->flags |= NFS_INO_INVALID_ATTR;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (!data_unstable)
-                       nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+                       nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
        }
        if (cur_size != new_isize) {
-               nfsi->flags |= NFS_INO_INVALID_ATTR;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (nfsi->npages == 0)
-                       nfsi->flags |= NFS_INO_REVAL_PAGECACHE;
+                       nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
        }
 
        /* Have any file permissions changed? */
        if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
                        || inode->i_uid != fattr->uid
                        || inode->i_gid != fattr->gid)
-               nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
 
        /* Has the link count changed? */
        if (inode->i_nlink != fattr->nlink)
-               nfsi->flags |= NFS_INO_INVALID_ATTR;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
 
        if (!timespec_equal(&inode->i_atime, &fattr->atime))
-               nfsi->flags |= NFS_INO_INVALID_ATIME;
+               nfsi->cache_validity |= NFS_INO_INVALID_ATIME;
 
        nfsi->read_cache_jiffies = fattr->timestamp;
+       spin_unlock(&inode->i_lock);
        return 0;
 }
 
@@ -1278,11 +1334,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
                goto out_err;
        }
 
+       spin_lock(&inode->i_lock);
+
        /*
         * Make sure the inode's type hasn't changed.
         */
-       if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+       if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+               spin_unlock(&inode->i_lock);
                goto out_changed;
+       }
 
        /*
         * Update the read time so we don't revalidate too often.
@@ -1373,8 +1433,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
                                || S_ISLNK(inode->i_mode)))
                invalid &= ~NFS_INO_INVALID_DATA;
        if (!nfs_have_delegation(inode, FMODE_READ))
-               nfsi->flags |= invalid;
+               nfsi->cache_validity |= invalid;
 
+       spin_unlock(&inode->i_lock);
        return 0;
  out_changed:
        /*
@@ -1391,7 +1452,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
         */
        nfs_invalidate_inode(inode);
  out_err:
-       NFS_FLAGS(inode) |= NFS_INO_STALE;
+       set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
        return -ESTALE;
 }
 
@@ -1950,7 +2011,8 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
        nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL);
        if (!nfsi)
                return NULL;
-       nfsi->flags = 0;
+       nfsi->flags = 0UL;
+       nfsi->cache_validity = 0UL;
 #ifdef CONFIG_NFS_V3_ACL
        nfsi->acl_access = ERR_PTR(-EAGAIN);
        nfsi->acl_default = ERR_PTR(-EAGAIN);
@@ -1982,7 +2044,6 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
                nfsi->ndirty = 0;
                nfsi->ncommit = 0;
                nfsi->npages = 0;
-               init_waitqueue_head(&nfsi->nfs_i_wait);
                nfs4_init_once(nfsi);
        }
 }