]> Pileus Git - ~andy/linux/blobdiff - fs/nfs/inode.c
Merge branch 'drm-fixes-3.14' of git://people.freedesktop.org/~agd5f/linux into drm...
[~andy/linux] / fs / nfs / inode.c
index ea00b34ff0712d6374908a08832cef0acdef82eb..360114ae8b829bf705eaf4feda49b8fe484de2c0 100644 (file)
@@ -164,17 +164,16 @@ static void nfs_zap_caches_locked(struct inode *inode)
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
                nfs_fscache_invalidate(inode);
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                                       | NFS_INO_INVALID_LABEL
                                        | NFS_INO_INVALID_DATA
                                        | NFS_INO_INVALID_ACCESS
                                        | NFS_INO_INVALID_ACL
                                        | NFS_INO_REVAL_PAGECACHE;
        } else
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                                       | NFS_INO_INVALID_LABEL
                                        | NFS_INO_INVALID_ACCESS
                                        | NFS_INO_INVALID_ACL
                                        | NFS_INO_REVAL_PAGECACHE;
+       nfs_zap_label_cache_locked(nfsi);
 }
 
 void nfs_zap_caches(struct inode *inode)
@@ -266,6 +265,13 @@ nfs_init_locked(struct inode *inode, void *opaque)
 }
 
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static void nfs_clear_label_invalid(struct inode *inode)
+{
+       spin_lock(&inode->i_lock);
+       NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_LABEL;
+       spin_unlock(&inode->i_lock);
+}
+
 void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
                                        struct nfs4_label *label)
 {
@@ -283,6 +289,7 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
                                        __func__,
                                        (char *)label->label,
                                        label->len, error);
+               nfs_clear_label_invalid(inode);
        }
 }
 
@@ -977,11 +984,11 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
                if (ret < 0)
                        return ret;
        }
-       spin_lock(&inode->i_lock);
-       nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
-       if (S_ISDIR(inode->i_mode))
+       if (S_ISDIR(inode->i_mode)) {
+               spin_lock(&inode->i_lock);
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
-       spin_unlock(&inode->i_lock);
+               spin_unlock(&inode->i_lock);
+       }
        nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
        nfs_fscache_wait_on_invalidate(inode);
 
@@ -1008,6 +1015,7 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
 int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       unsigned long *bitlock = &nfsi->flags;
        int ret = 0;
 
        /* swapfiles are not supposed to be shared. */
@@ -1019,12 +1027,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
                if (ret < 0)
                        goto out;
        }
-       if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
-               trace_nfs_invalidate_mapping_enter(inode);
-               ret = nfs_invalidate_mapping(inode, mapping);
-               trace_nfs_invalidate_mapping_exit(inode, ret);
+
+       /*
+        * We must clear NFS_INO_INVALID_DATA first to ensure that
+        * invalidations that come in while we're shooting down the mappings
+        * are respected. But, that leaves a race window where one revalidator
+        * can clear the flag, and then another checks it before the mapping
+        * gets invalidated. Fix that by serializing access to this part of
+        * the function.
+        *
+        * At the same time, we need to allow other tasks to see whether we
+        * might be in the middle of invalidating the pages, so we only set
+        * the bit lock here if it looks like we're going to be doing that.
+        */
+       for (;;) {
+               ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING,
+                                 nfs_wait_bit_killable, TASK_KILLABLE);
+               if (ret)
+                       goto out;
+               spin_lock(&inode->i_lock);
+               if (test_bit(NFS_INO_INVALIDATING, bitlock)) {
+                       spin_unlock(&inode->i_lock);
+                       continue;
+               }
+               if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
+                       break;
+               spin_unlock(&inode->i_lock);
+               goto out;
        }
 
+       set_bit(NFS_INO_INVALIDATING, bitlock);
+       smp_wmb();
+       nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
+       spin_unlock(&inode->i_lock);
+       trace_nfs_invalidate_mapping_enter(inode);
+       ret = nfs_invalidate_mapping(inode, mapping);
+       trace_nfs_invalidate_mapping_exit(inode, ret);
+
+       clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
+       smp_mb__after_clear_bit();
+       wake_up_bit(bitlock, NFS_INO_INVALIDATING);
 out:
        return ret;
 }
@@ -1613,7 +1655,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                inode->i_blocks = fattr->du.nfs2.blocks;
 
        /* Update attrtimeo value if we're out of the unstable period */
-       if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) {
+       if (invalid & NFS_INO_INVALID_ATTR) {
                nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
@@ -1626,7 +1668,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                }
        }
        invalid &= ~NFS_INO_INVALID_ATTR;
-       invalid &= ~NFS_INO_INVALID_LABEL;
        /* Don't invalidate the data if we were to blame */
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))