static void v9fs_set_netfs_context(struct inode *inode)
 {
        struct v9fs_inode *v9inode = V9FS_I(inode);
-       netfs_inode_init(&v9inode->netfs, &v9fs_req_ops);
+       netfs_inode_init(&v9inode->netfs, &v9fs_req_ops, true);
 }
 
 int v9fs_init_inode(struct v9fs_session_info *v9ses,
 
        /* there shouldn't be an existing inode */
        BUG_ON(!(inode->i_state & I_NEW));
 
-       netfs_inode_init(&vnode->netfs, NULL);
+       netfs_inode_init(&vnode->netfs, NULL, false);
        inode->i_size           = 0;
        inode->i_mode           = S_IFDIR | S_IRUGO | S_IXUGO;
        if (root) {
 
  */
 static void afs_set_netfs_context(struct afs_vnode *vnode)
 {
-       netfs_inode_init(&vnode->netfs, &afs_req_ops);
+       netfs_inode_init(&vnode->netfs, &afs_req_ops, true);
 }
 
 /*
        struct inode *inode = &vnode->netfs.inode;
        struct timespec64 t;
        umode_t mode;
+       bool unexpected_jump = false;
        bool data_changed = false;
        bool change_size = vp->set_size;
 
                }
                change_size = true;
                data_changed = true;
+               unexpected_jump = true;
        } else if (vnode->status.type == AFS_FTYPE_DIR) {
                /* Expected directory change is handled elsewhere so
                 * that we can locally edit the directory and save on a
                vnode->netfs.remote_i_size = status->size;
                if (change_size || status->size > i_size_read(inode)) {
                        afs_set_i_size(vnode, status->size);
+                       if (unexpected_jump)
+                               vnode->netfs.zero_point = status->size;
                        inode_set_ctime_to_ts(inode, t);
                        inode_set_atime_to_ts(inode, t);
                }
 static void afs_setattr_edit_file(struct afs_operation *op)
 {
        struct afs_vnode_param *vp = &op->file[0];
-       struct inode *inode = &vp->vnode->netfs.inode;
+       struct afs_vnode *vnode = vp->vnode;
 
        if (op->setattr.attr->ia_valid & ATTR_SIZE) {
                loff_t size = op->setattr.attr->ia_size;
                loff_t i_size = op->setattr.old_i_size;
 
-               if (size < i_size)
-                       truncate_pagecache(inode, size);
-               if (size != i_size)
-                       fscache_resize_cookie(afs_vnode_cache(vp->vnode),
-                                             vp->scb.status.size);
+               if (size != i_size) {
+                       truncate_setsize(&vnode->netfs.inode, size);
+                       netfs_resize_file(&vnode->netfs, size, true);
+                       fscache_resize_cookie(afs_vnode_cache(vnode), size);
+               }
        }
 }
 
                 */
                if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) &&
                    attr->ia_size < i_size &&
-                   attr->ia_size > vnode->status.size) {
-                       truncate_pagecache(inode, attr->ia_size);
+                   attr->ia_size > vnode->netfs.remote_i_size) {
+                       truncate_setsize(inode, attr->ia_size);
+                       netfs_resize_file(&vnode->netfs, size, false);
                        fscache_resize_cookie(afs_vnode_cache(vnode),
                                              attr->ia_size);
-                       i_size_write(inode, attr->ia_size);
                        ret = 0;
                        goto out_unlock;
                }
 
        doutc(fsc->client, "%p\n", &ci->netfs.inode);
 
        /* Set parameters for the netfs library */
-       netfs_inode_init(&ci->netfs, &ceph_netfs_ops);
+       netfs_inode_init(&ci->netfs, &ceph_netfs_ops, false);
 
        spin_lock_init(&ci->i_ceph_lock);
 
 
        if (folio_test_uptodate(folio))
                return NETFS_FOLIO_IS_UPTODATE;
 
-       if (pos >= ctx->remote_i_size)
+       if (pos >= ctx->zero_point)
                return NETFS_MODIFY_AND_CLEAR;
 
        if (!maybe_trouble && offset == 0 && len >= flen)
 
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        struct netfs_inode *ictx = netfs_inode(inode);
+       unsigned long long end;
        ssize_t ret;
 
        _enter("%llx,%zx,%llx", iocb->ki_pos, iov_iter_count(from), i_size_read(inode));
        ret = kiocb_invalidate_pages(iocb, iov_iter_count(from));
        if (ret < 0)
                goto out;
+       end = iocb->ki_pos + iov_iter_count(from);
+       if (end > ictx->zero_point)
+               ictx->zero_point = end;
 
        fscache_invalidate(netfs_i_cookie(ictx), NULL, i_size_read(inode),
                           FSCACHE_INVAL_DIO_WRITE);
 
                        struct iov_iter *io_iter)
 {
        enum netfs_io_source source = NETFS_DOWNLOAD_FROM_SERVER;
+       struct netfs_inode *ictx = netfs_inode(rreq->inode);
        size_t lsize;
 
        _enter("%llx-%llx,%llx", subreq->start, subreq->start + subreq->len, rreq->i_size);
                 * to make serial calls, it can indicate a short read and then
                 * we will call it again.
                 */
+               if (rreq->origin != NETFS_DIO_READ) {
+                       if (subreq->start >= ictx->zero_point) {
+                               source = NETFS_FILL_WITH_ZEROES;
+                               goto set;
+                       }
+                       if (subreq->len > ictx->zero_point - subreq->start)
+                               subreq->len = ictx->zero_point - subreq->start;
+               }
                if (subreq->len > rreq->i_size - subreq->start)
                        subreq->len = rreq->i_size - subreq->start;
                if (rreq->rsize && subreq->len > rreq->rsize)
                }
        }
 
+set:
        if (subreq->len > rreq->len)
                pr_warn("R=%08x[%u] SREQ>RREQ %zx > %zx\n",
                        rreq->debug_id, subreq->debug_index,
 
 bool netfs_release_folio(struct folio *folio, gfp_t gfp)
 {
        struct netfs_inode *ctx = netfs_inode(folio_inode(folio));
+       unsigned long long end;
+
+       end = folio_pos(folio) + folio_size(folio);
+       if (end > ctx->zero_point)
+               ctx->zero_point = end;
 
        if (folio_test_private(folio))
                return false;
 
 }
 static inline void nfs_netfs_inode_init(struct nfs_inode *nfsi)
 {
-       netfs_inode_init(&nfsi->netfs, &nfs_netfs_ops);
+       netfs_inode_init(&nfsi->netfs, &nfs_netfs_ops, false);
 }
 extern void nfs_netfs_initiate_read(struct nfs_pgio_header *hdr);
 extern void nfs_netfs_read_completion(struct nfs_pgio_header *hdr);
 
        if (rc < 0)
                goto set_failed;
 
-       netfs_resize_file(&src_cifsi->netfs, src_end);
+       netfs_resize_file(&src_cifsi->netfs, src_end, true);
        fscache_resize_cookie(cifs_inode_cookie(src_inode), src_end);
        return 0;
 
                        smb_file_src, smb_file_target, off, len, destoff);
                if (rc == 0 && new_size > i_size_read(target_inode)) {
                        truncate_setsize(target_inode, new_size);
-                       netfs_resize_file(&target_cifsi->netfs, new_size);
+                       netfs_resize_file(&target_cifsi->netfs, new_size, true);
                        fscache_resize_cookie(cifs_inode_cookie(target_inode),
                                              new_size);
                }
 
        struct fscache_cookie   *cache;
 #endif
        loff_t                  remote_i_size;  /* Size of the remote file */
+       loff_t                  zero_point;     /* Size after which we assume there's no data
+                                                * on the server */
        unsigned long           flags;
 #define NETFS_ICTX_ODIRECT     0               /* The file has DIO in progress */
 #define NETFS_ICTX_UNBUFFERED  1               /* I/O should not use the pagecache */
  * netfs_inode_init - Initialise a netfslib inode context
  * @ctx: The netfs inode to initialise
  * @ops: The netfs's operations list
+ * @use_zero_point: True to use the zero_point read optimisation
  *
  * Initialise the netfs library context struct.  This is expected to follow on
  * directly from the VFS inode struct.
  */
 static inline void netfs_inode_init(struct netfs_inode *ctx,
-                                   const struct netfs_request_ops *ops)
+                                   const struct netfs_request_ops *ops,
+                                   bool use_zero_point)
 {
        ctx->ops = ops;
        ctx->remote_i_size = i_size_read(&ctx->inode);
+       ctx->zero_point = LLONG_MAX;
        ctx->flags = 0;
 #if IS_ENABLED(CONFIG_FSCACHE)
        ctx->cache = NULL;
 #endif
+       /* ->releasepage() drives zero_point */
+       if (use_zero_point) {
+               ctx->zero_point = ctx->remote_i_size;
+               mapping_set_release_always(ctx->inode.i_mapping);
+       }
 }
 
 /**
  * netfs_resize_file - Note that a file got resized
  * @ctx: The netfs inode being resized
  * @new_i_size: The new file size
+ * @changed_on_server: The change was applied to the server
  *
  * Inform the netfs lib that a file got resized so that it can adjust its state.
  */
-static inline void netfs_resize_file(struct netfs_inode *ctx, loff_t new_i_size)
+static inline void netfs_resize_file(struct netfs_inode *ctx, loff_t new_i_size,
+                                    bool changed_on_server)
 {
-       ctx->remote_i_size = new_i_size;
+       if (changed_on_server)
+               ctx->remote_i_size = new_i_size;
+       if (new_i_size < ctx->zero_point)
+               ctx->zero_point = new_i_size;
 }
 
 /**