NFS: Always clear an invalid mapping when attempting a buffered write
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 8 Feb 2021 13:55:47 +0000 (08:55 -0500)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Mon, 8 Feb 2021 16:41:38 +0000 (11:41 -0500)
If the page cache is invalid, then we can't do read-modify-write, so
ensure that we do clear it when we know it is invalid.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/file.c
fs/nfs/inode.c
include/linux/nfs_fs.h

index 02795a0..03fd1dc 100644 (file)
@@ -632,6 +632,8 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
                        goto out;
        }
 
+       nfs_clear_invalid_mapping(file->f_mapping);
+
        since = filemap_sample_wb_err(file->f_mapping);
        nfs_start_io_write(inode);
        result = generic_write_checks(iocb, from);
index 522aa10..2533053 100644 (file)
@@ -1257,55 +1257,19 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
        return 0;
 }
 
-bool nfs_mapping_need_revalidate_inode(struct inode *inode)
-{
-       return nfs_check_cache_invalid(inode, NFS_INO_REVAL_PAGECACHE) ||
-               NFS_STALE(inode);
-}
-
-int nfs_revalidate_mapping_rcu(struct inode *inode)
-{
-       struct nfs_inode *nfsi = NFS_I(inode);
-       unsigned long *bitlock = &nfsi->flags;
-       int ret = 0;
-
-       if (IS_SWAPFILE(inode))
-               goto out;
-       if (nfs_mapping_need_revalidate_inode(inode)) {
-               ret = -ECHILD;
-               goto out;
-       }
-       spin_lock(&inode->i_lock);
-       if (test_bit(NFS_INO_INVALIDATING, bitlock) ||
-           (nfsi->cache_validity & NFS_INO_INVALID_DATA))
-               ret = -ECHILD;
-       spin_unlock(&inode->i_lock);
-out:
-       return ret;
-}
-
 /**
- * nfs_revalidate_mapping - Revalidate the pagecache
- * @inode: pointer to host inode
+ * nfs_clear_invalid_mapping - Conditionally clear a mapping
  * @mapping: pointer to mapping
+ *
+ * If the NFS_INO_INVALID_DATA inode flag is set, clear the mapping.
  */
-int nfs_revalidate_mapping(struct inode *inode,
-               struct address_space *mapping)
+int nfs_clear_invalid_mapping(struct address_space *mapping)
 {
+       struct inode *inode = mapping->host;
        struct nfs_inode *nfsi = NFS_I(inode);
        unsigned long *bitlock = &nfsi->flags;
        int ret = 0;
 
-       /* swapfiles are not supposed to be shared. */
-       if (IS_SWAPFILE(inode))
-               goto out;
-
-       if (nfs_mapping_need_revalidate_inode(inode)) {
-               ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
-               if (ret < 0)
-                       goto out;
-       }
-
        /*
         * We must clear NFS_INO_INVALID_DATA first to ensure that
         * invalidations that come in while we're shooting down the mappings
@@ -1336,8 +1300,8 @@ int nfs_revalidate_mapping(struct inode *inode,
 
        set_bit(NFS_INO_INVALIDATING, bitlock);
        smp_wmb();
-       nfsi->cache_validity &= ~(NFS_INO_INVALID_DATA|
-                       NFS_INO_DATA_INVAL_DEFER);
+       nfsi->cache_validity &=
+               ~(NFS_INO_INVALID_DATA | NFS_INO_DATA_INVAL_DEFER);
        spin_unlock(&inode->i_lock);
        trace_nfs_invalidate_mapping_enter(inode);
        ret = nfs_invalidate_mapping(inode, mapping);
@@ -1350,6 +1314,53 @@ out:
        return ret;
 }
 
+bool nfs_mapping_need_revalidate_inode(struct inode *inode)
+{
+       return nfs_check_cache_invalid(inode, NFS_INO_REVAL_PAGECACHE) ||
+               NFS_STALE(inode);
+}
+
+int nfs_revalidate_mapping_rcu(struct inode *inode)
+{
+       struct nfs_inode *nfsi = NFS_I(inode);
+       unsigned long *bitlock = &nfsi->flags;
+       int ret = 0;
+
+       if (IS_SWAPFILE(inode))
+               goto out;
+       if (nfs_mapping_need_revalidate_inode(inode)) {
+               ret = -ECHILD;
+               goto out;
+       }
+       spin_lock(&inode->i_lock);
+       if (test_bit(NFS_INO_INVALIDATING, bitlock) ||
+           (nfsi->cache_validity & NFS_INO_INVALID_DATA))
+               ret = -ECHILD;
+       spin_unlock(&inode->i_lock);
+out:
+       return ret;
+}
+
+/**
+ * nfs_revalidate_mapping - Revalidate the pagecache
+ * @inode: pointer to host inode
+ * @mapping: pointer to mapping
+ */
+int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
+{
+       /* swapfiles are not supposed to be shared. */
+       if (IS_SWAPFILE(inode))
+               return 0;
+
+       if (nfs_mapping_need_revalidate_inode(inode)) {
+               int ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return nfs_clear_invalid_mapping(mapping);
+}
+
 static bool nfs_file_has_writers(struct nfs_inode *nfsi)
 {
        struct inode *inode = &nfsi->vfs_inode;
index 3cfcf21..2c66285 100644 (file)
@@ -387,6 +387,7 @@ extern int nfs_open(struct inode *, struct file *);
 extern int nfs_attribute_cache_expired(struct inode *inode);
 extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
 extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
+extern int nfs_clear_invalid_mapping(struct address_space *mapping);
 extern bool nfs_mapping_need_revalidate_inode(struct inode *inode);
 extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
 extern int nfs_revalidate_mapping_rcu(struct inode *inode);