]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/nfs/inode.c
Merge tag 'nfs-for-6.4-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
[thirdparty/linux.git] / fs / nfs / inode.c
index 97a76706fd542b9aa149a578a04aa8b42bbf2aad..a910b9a638c5e95f46630aeb6b03b1e2ed5a4dac 100644 (file)
@@ -208,11 +208,12 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
 
        nfsi->cache_validity |= flags;
 
-       if (inode->i_mapping->nrpages == 0)
-               nfsi->cache_validity &= ~(NFS_INO_INVALID_DATA |
-                                         NFS_INO_DATA_INVAL_DEFER);
-       else if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
-               nfsi->cache_validity &= ~NFS_INO_DATA_INVAL_DEFER;
+       if (inode->i_mapping->nrpages == 0) {
+               nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
+               nfs_ooo_clear(nfsi);
+       } else if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
+               nfs_ooo_clear(nfsi);
+       }
        trace_nfs_set_cache_invalid(inode, 0);
 }
 EXPORT_SYMBOL_GPL(nfs_set_cache_invalid);
@@ -677,9 +678,10 @@ static int nfs_vmtruncate(struct inode * inode, loff_t offset)
        trace_nfs_size_truncate(inode, offset);
        i_size_write(inode, offset);
        /* Optimisation */
-       if (offset == 0)
-               NFS_I(inode)->cache_validity &= ~(NFS_INO_INVALID_DATA |
-                               NFS_INO_DATA_INVAL_DEFER);
+       if (offset == 0) {
+               NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
+               nfs_ooo_clear(NFS_I(inode));
+       }
        NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
 
        spin_unlock(&inode->i_lock);
@@ -1107,7 +1109,7 @@ void nfs_inode_attach_open_context(struct nfs_open_context *ctx)
 
        spin_lock(&inode->i_lock);
        if (list_empty(&nfsi->open_files) &&
-           (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER))
+           nfs_ooo_test(nfsi))
                nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA |
                                                     NFS_INO_REVAL_FORCED);
        list_add_tail_rcu(&ctx->list, &nfsi->open_files);
@@ -1351,8 +1353,8 @@ int nfs_clear_invalid_mapping(struct address_space *mapping)
 
        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_ooo_clear(nfsi);
        spin_unlock(&inode->i_lock);
        trace_nfs_invalidate_mapping_enter(inode);
        ret = nfs_invalidate_mapping(inode, mapping);
@@ -1814,6 +1816,66 @@ static int nfs_inode_finish_partial_attr_update(const struct nfs_fattr *fattr,
        return 0;
 }
 
+static void nfs_ooo_merge(struct nfs_inode *nfsi,
+                         u64 start, u64 end)
+{
+       int i, cnt;
+
+       if (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER)
+               /* No point merging anything */
+               return;
+
+       if (!nfsi->ooo) {
+               nfsi->ooo = kmalloc(sizeof(*nfsi->ooo), GFP_ATOMIC);
+               if (!nfsi->ooo) {
+                       nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
+                       return;
+               }
+               nfsi->ooo->cnt = 0;
+       }
+
+       /* add this range, merging if possible */
+       cnt = nfsi->ooo->cnt;
+       for (i = 0; i < cnt; i++) {
+               if (end == nfsi->ooo->gap[i].start)
+                       end = nfsi->ooo->gap[i].end;
+               else if (start == nfsi->ooo->gap[i].end)
+                       start = nfsi->ooo->gap[i].start;
+               else
+                       continue;
+               /* Remove 'i' from table and loop to insert the new range */
+               cnt -= 1;
+               nfsi->ooo->gap[i] = nfsi->ooo->gap[cnt];
+               i = -1;
+       }
+       if (start != end) {
+               if (cnt >= ARRAY_SIZE(nfsi->ooo->gap)) {
+                       nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
+                       kfree(nfsi->ooo);
+                       nfsi->ooo = NULL;
+                       return;
+               }
+               nfsi->ooo->gap[cnt].start = start;
+               nfsi->ooo->gap[cnt].end = end;
+               cnt += 1;
+       }
+       nfsi->ooo->cnt = cnt;
+}
+
+static void nfs_ooo_record(struct nfs_inode *nfsi,
+                          struct nfs_fattr *fattr)
+{
+       /* This reply was out-of-order, so record in the
+        * pre/post change id, possibly cancelling
+        * gaps created when iversion was jumpped forward.
+        */
+       if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) &&
+           (fattr->valid & NFS_ATTR_FATTR_PRECHANGE))
+               nfs_ooo_merge(nfsi,
+                             fattr->change_attr,
+                             fattr->pre_change_attr);
+}
+
 static int nfs_refresh_inode_locked(struct inode *inode,
                                    struct nfs_fattr *fattr)
 {
@@ -1824,8 +1886,12 @@ static int nfs_refresh_inode_locked(struct inode *inode,
 
        if (attr_cmp > 0 || nfs_inode_finish_partial_attr_update(fattr, inode))
                ret = nfs_update_inode(inode, fattr);
-       else if (attr_cmp == 0)
-               ret = nfs_check_inode_attributes(inode, fattr);
+       else {
+               nfs_ooo_record(NFS_I(inode), fattr);
+
+               if (attr_cmp == 0)
+                       ret = nfs_check_inode_attributes(inode, fattr);
+       }
 
        trace_nfs_refresh_inode_exit(inode, ret);
        return ret;
@@ -1916,6 +1982,8 @@ int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fa
        if (attr_cmp < 0)
                return 0;
        if ((fattr->valid & NFS_ATTR_FATTR) == 0 || !attr_cmp) {
+               /* Record the pre/post change info before clearing PRECHANGE */
+               nfs_ooo_record(NFS_I(inode), fattr);
                fattr->valid &= ~(NFS_ATTR_FATTR_PRECHANGE
                                | NFS_ATTR_FATTR_PRESIZE
                                | NFS_ATTR_FATTR_PREMTIME
@@ -2070,6 +2138,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 
        /* More cache consistency checks */
        if (fattr->valid & NFS_ATTR_FATTR_CHANGE) {
+               if (!have_writers && nfsi->ooo && nfsi->ooo->cnt == 1 &&
+                   nfsi->ooo->gap[0].end == inode_peek_iversion_raw(inode)) {
+                       /* There is one remaining gap that hasn't been
+                        * merged into iversion - do that now.
+                        */
+                       inode_set_iversion_raw(inode, nfsi->ooo->gap[0].start);
+                       kfree(nfsi->ooo);
+                       nfsi->ooo = NULL;
+               }
                if (!inode_eq_iversion_raw(inode, fattr->change_attr)) {
                        /* Could it be a race with writeback? */
                        if (!(have_writers || have_delegation)) {
@@ -2091,8 +2168,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                dprintk("NFS: change_attr change on server for file %s/%ld\n",
                                                inode->i_sb->s_id,
                                                inode->i_ino);
-                       } else if (!have_delegation)
-                               nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
+                       } else if (!have_delegation) {
+                               nfs_ooo_record(nfsi, fattr);
+                               nfs_ooo_merge(nfsi, inode_peek_iversion_raw(inode),
+                                             fattr->change_attr);
+                       }
                        inode_set_iversion_raw(inode, fattr->change_attr);
                }
        } else {
@@ -2246,18 +2326,22 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
                return NULL;
        nfsi->flags = 0UL;
        nfsi->cache_validity = 0UL;
+       nfsi->ooo = NULL;
 #if IS_ENABLED(CONFIG_NFS_V4)
        nfsi->nfs4_acl = NULL;
 #endif /* CONFIG_NFS_V4 */
 #ifdef CONFIG_NFS_V4_2
        nfsi->xattr_cache = NULL;
 #endif
+       nfs_netfs_inode_init(nfsi);
+
        return &nfsi->vfs_inode;
 }
 EXPORT_SYMBOL_GPL(nfs_alloc_inode);
 
 void nfs_free_inode(struct inode *inode)
 {
+       kfree(NFS_I(inode)->ooo);
        kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
 }
 EXPORT_SYMBOL_GPL(nfs_free_inode);