Merge tag 'netfs-prep-20220318' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 31 Mar 2022 22:49:36 +0000 (15:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 31 Mar 2022 22:49:36 +0000 (15:49 -0700)
Pull netfs updates from David Howells:
 "Netfs prep for write helpers.

  Having had a go at implementing write helpers and content encryption
  support in netfslib, it seems that the netfs_read_{,sub}request
  structs and the equivalent write request structs were almost the same
  and so should be merged, thereby requiring only one set of
  alloc/get/put functions and a common set of tracepoints.

  Merging the structs also has the advantage that if a bounce buffer is
  added to the request struct, a read operation can be performed to fill
  the bounce buffer, the contents of the buffer can be modified and then
  a write operation can be performed on it to send the data wherever it
  needs to go using the same request structure all the way through. The
  I/O handlers would then transparently perform any required crypto.
  This should make it easier to perform RMW cycles if needed.

  The potentially common functions and structs, however, by their names
  all proclaim themselves to be associated with the read side of things.

  The bulk of these changes alter this in the following ways:

   - Rename struct netfs_read_{,sub}request to netfs_io_{,sub}request.

   - Rename some enums, members and flags to make them more appropriate.

   - Adjust some comments to match.

   - Drop "read"/"rreq" from the names of common functions. For
     instance, netfs_get_read_request() becomes netfs_get_request().

   - The ->init_rreq() and ->issue_op() methods become ->init_request()
     and ->issue_read(). I've kept the latter as a read-specific
     function and in another branch added an ->issue_write() method.

  The driver source is then reorganised into a number of files:

        fs/netfs/buffered_read.c        Create read reqs to the pagecache
        fs/netfs/io.c                   Dispatchers for read and write reqs
        fs/netfs/main.c                 Some general miscellaneous bits
        fs/netfs/objects.c              Alloc, get and put functions
        fs/netfs/stats.c                Optional procfs statistics.

  and future development can be fitted into this scheme, e.g.:

        fs/netfs/buffered_write.c       Modify the pagecache
        fs/netfs/buffered_flush.c       Writeback from the pagecache
        fs/netfs/direct_read.c          DIO read support
        fs/netfs/direct_write.c         DIO write support
        fs/netfs/unbuffered_write.c     Write modifications directly back

  Beyond the above changes, there are also some changes that affect how
  things work:

   - Make fscache_end_operation() generally available.

   - In the netfs tracing header, generate enums from the symbol ->
     string mapping tables rather than manually coding them.

   - Add a struct for filesystems that uses netfslib to put into their
     inode wrapper structs to hold extra state that netfslib is
     interested in, such as the fscache cookie. This allows netfslib
     functions to be set in filesystem operation tables and jumped to
     directly without having to have a filesystem wrapper.

   - Add a member to the struct added above to track the remote inode
     length as that may differ if local modifications are buffered. We
     may need to supply an appropriate EOF pointer when storing data (in
     AFS for example).

   - Pass extra information to netfs_alloc_request() so that the
     ->init_request() hook can access it and retain information to
     indicate the origin of the operation.

   - Make the ->init_request() hook return an error, thereby allowing a
     filesystem that isn't allowed to cache an inode (ceph or cifs, for
     example) to skip readahead.

   - Switch to using refcount_t for subrequests and add tracepoints to
     log refcount changes for the request and subrequest structs.

   - Add a function to consolidate dispatching a read request. Similar
     code is used in three places and another couple are likely to be
     added in the future"

Link: https://lore.kernel.org/all/2639515.1648483225@warthog.procyon.org.uk/
* tag 'netfs-prep-20220318' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Maintain netfs_i_context::remote_i_size
  netfs: Keep track of the actual remote file size
  netfs: Split some core bits out into their own file
  netfs: Split fs/netfs/read_helper.c
  netfs: Rename read_helper.c to io.c
  netfs: Prepare to split read_helper.c
  netfs: Add a function to consolidate beginning a read
  netfs: Add a netfs inode context
  ceph: Make ceph_init_request() check caps on readahead
  netfs: Change ->init_request() to return an error code
  netfs: Refactor arguments for netfs_alloc_read_request
  netfs: Adjust the netfs_failure tracepoint to indicate non-subreq lines
  netfs: Trace refcounting on the netfs_io_subrequest struct
  netfs: Trace refcounting on the netfs_io_request struct
  netfs: Adjust the netfs_rreq tracepoint slightly
  netfs: Split netfs_io_* object handling out
  netfs: Finish off rename of netfs_read_request to netfs_io_request
  netfs: Rename netfs_read_*request to netfs_io_*request
  netfs: Generate enums from trace symbol mapping lists
  fscache: export fscache_end_operation()

14 files changed:
1  2 
fs/9p/vfs_addr.c
fs/9p/vfs_inode.c
fs/afs/file.c
fs/afs/internal.h
fs/afs/super.c
fs/afs/write.c
fs/cachefiles/io.c
fs/ceph/addr.c
fs/ceph/cache.h
fs/ceph/inode.c
fs/ceph/super.h
fs/nfs/fscache.c
include/linux/fscache.h
include/trace/events/cachefiles.h

diff --combined fs/9p/vfs_addr.c
  #include "fid.h"
  
  /**
-  * v9fs_req_issue_op - Issue a read from 9P
+  * v9fs_issue_read - Issue a read from 9P
   * @subreq: The read to make
   */
- static void v9fs_req_issue_op(struct netfs_read_subrequest *subreq)
+ static void v9fs_issue_read(struct netfs_io_subrequest *subreq)
  {
-       struct netfs_read_request *rreq = subreq->rreq;
+       struct netfs_io_request *rreq = subreq->rreq;
        struct p9_fid *fid = rreq->netfs_priv;
        struct iov_iter to;
        loff_t pos = subreq->start + subreq->transferred;
  }
  
  /**
-  * v9fs_init_rreq - Initialise a read request
+  * v9fs_init_request - Initialise a read request
   * @rreq: The read request
   * @file: The file being read from
   */
- static void v9fs_init_rreq(struct netfs_read_request *rreq, struct file *file)
+ static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file)
  {
        struct p9_fid *fid = file->private_data;
  
        refcount_inc(&fid->count);
        rreq->netfs_priv = fid;
+       return 0;
  }
  
  /**
-  * v9fs_req_cleanup - Cleanup request initialized by v9fs_init_rreq
+  * v9fs_req_cleanup - Cleanup request initialized by v9fs_init_request
   * @mapping: unused mapping of request to cleanup
   * @priv: private data to cleanup, a fid, guaranted non-null.
   */
@@@ -76,22 -77,11 +77,11 @@@ static void v9fs_req_cleanup(struct add
        p9_client_clunk(fid);
  }
  
- /**
-  * v9fs_is_cache_enabled - Determine if caching is enabled for an inode
-  * @inode: The inode to check
-  */
- static bool v9fs_is_cache_enabled(struct inode *inode)
- {
-       struct fscache_cookie *cookie = v9fs_inode_cookie(V9FS_I(inode));
-       return fscache_cookie_enabled(cookie) && cookie->cache_priv;
- }
  /**
   * v9fs_begin_cache_operation - Begin a cache operation for a read
   * @rreq: The read request
   */
- static int v9fs_begin_cache_operation(struct netfs_read_request *rreq)
+ static int v9fs_begin_cache_operation(struct netfs_io_request *rreq)
  {
  #ifdef CONFIG_9P_FSCACHE
        struct fscache_cookie *cookie = v9fs_inode_cookie(V9FS_I(rreq->inode));
  #endif
  }
  
- static const struct netfs_read_request_ops v9fs_req_ops = {
-       .init_rreq              = v9fs_init_rreq,
-       .is_cache_enabled       = v9fs_is_cache_enabled,
+ const struct netfs_request_ops v9fs_req_ops = {
+       .init_request           = v9fs_init_request,
        .begin_cache_operation  = v9fs_begin_cache_operation,
-       .issue_op               = v9fs_req_issue_op,
+       .issue_read             = v9fs_issue_read,
        .cleanup                = v9fs_req_cleanup,
  };
  
- /**
-  * v9fs_vfs_readpage - read an entire page in from 9P
-  * @file: file being read
-  * @page: structure to page
-  *
-  */
- static int v9fs_vfs_readpage(struct file *file, struct page *page)
- {
-       struct folio *folio = page_folio(page);
-       return netfs_readpage(file, folio, &v9fs_req_ops, NULL);
- }
- /**
-  * v9fs_vfs_readahead - read a set of pages from 9P
-  * @ractl: The readahead parameters
-  */
- static void v9fs_vfs_readahead(struct readahead_control *ractl)
- {
-       netfs_readahead(ractl, &v9fs_req_ops, NULL);
- }
  /**
   * v9fs_release_page - release the private state associated with a page
   * @page: The page to be released
@@@ -158,9 -125,18 +125,9 @@@ static int v9fs_release_page(struct pag
        return 1;
  }
  
 -/**
 - * v9fs_invalidate_page - Invalidate a page completely or partially
 - * @page: The page to be invalidated
 - * @offset: offset of the invalidated region
 - * @length: length of the invalidated region
 - */
 -
 -static void v9fs_invalidate_page(struct page *page, unsigned int offset,
 -                               unsigned int length)
 +static void v9fs_invalidate_folio(struct folio *folio, size_t offset,
 +                               size_t length)
  {
 -      struct folio *folio = page_folio(page);
 -
        folio_wait_fscache(folio);
  }
  
@@@ -240,8 -216,16 +207,8 @@@ static int v9fs_vfs_writepage(struct pa
        return retval;
  }
  
 -/**
 - * v9fs_launder_page - Writeback a dirty page
 - * @page: The page to be cleaned up
 - *
 - * Returns 0 on success.
 - */
 -
 -static int v9fs_launder_page(struct page *page)
 +static int v9fs_launder_folio(struct folio *folio)
  {
 -      struct folio *folio = page_folio(page);
        int retval;
  
        if (folio_clear_dirty_for_io(folio)) {
@@@ -308,8 -292,7 +275,7 @@@ static int v9fs_write_begin(struct fil
         * file.  We need to do this before we get a lock on the page in case
         * there's more than one writer competing for the same cache block.
         */
-       retval = netfs_write_begin(filp, mapping, pos, len, flags, &folio, fsdata,
-                                  &v9fs_req_ops, NULL);
+       retval = netfs_write_begin(filp, mapping, pos, len, flags, &folio, fsdata);
        if (retval < 0)
                return retval;
  
@@@ -359,25 -342,25 +325,25 @@@ out
   * Mark a page as having been made dirty and thus needing writeback.  We also
   * need to pin the cache object to write back to.
   */
 -static int v9fs_set_page_dirty(struct page *page)
 +static bool v9fs_dirty_folio(struct address_space *mapping, struct folio *folio)
  {
 -      struct v9fs_inode *v9inode = V9FS_I(page->mapping->host);
 +      struct v9fs_inode *v9inode = V9FS_I(mapping->host);
  
 -      return fscache_set_page_dirty(page, v9fs_inode_cookie(v9inode));
 +      return fscache_dirty_folio(mapping, folio, v9fs_inode_cookie(v9inode));
  }
  #else
 -#define v9fs_set_page_dirty __set_page_dirty_nobuffers
 +#define v9fs_dirty_folio filemap_dirty_folio
  #endif
  
  const struct address_space_operations v9fs_addr_operations = {
-       .readpage = v9fs_vfs_readpage,
-       .readahead = v9fs_vfs_readahead,
+       .readpage = netfs_readpage,
+       .readahead = netfs_readahead,
 -      .set_page_dirty = v9fs_set_page_dirty,
 +      .dirty_folio = v9fs_dirty_folio,
        .writepage = v9fs_vfs_writepage,
        .write_begin = v9fs_write_begin,
        .write_end = v9fs_write_end,
        .releasepage = v9fs_release_page,
 -      .invalidatepage = v9fs_invalidate_page,
 -      .launder_page = v9fs_launder_page,
 +      .invalidate_folio = v9fs_invalidate_folio,
 +      .launder_folio = v9fs_launder_folio,
        .direct_IO = v9fs_direct_IO,
  };
diff --combined fs/9p/vfs_inode.c
@@@ -228,12 -228,9 +228,9 @@@ struct inode *v9fs_alloc_inode(struct s
  {
        struct v9fs_inode *v9inode;
  
 -      v9inode = kmem_cache_alloc(v9fs_inode_cache, GFP_KERNEL);
 +      v9inode = alloc_inode_sb(sb, v9fs_inode_cache, GFP_KERNEL);
        if (!v9inode)
                return NULL;
- #ifdef CONFIG_9P_FSCACHE
-       v9inode->fscache = NULL;
- #endif
        v9inode->writeback_fid = NULL;
        v9inode->cache_validity = 0;
        mutex_init(&v9inode->v_mutex);
@@@ -250,6 -247,14 +247,14 @@@ void v9fs_free_inode(struct inode *inod
        kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
  }
  
+ /*
+  * Set parameters for the netfs library
+  */
+ static void v9fs_set_netfs_context(struct inode *inode)
+ {
+       netfs_i_context_init(inode, &v9fs_req_ops);
+ }
  int v9fs_init_inode(struct v9fs_session_info *v9ses,
                    struct inode *inode, umode_t mode, dev_t rdev)
  {
                err = -EINVAL;
                goto error;
        }
+       v9fs_set_netfs_context(inode);
  error:
        return err;
  
diff --combined fs/afs/file.c
  #include "internal.h"
  
  static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
- static int afs_readpage(struct file *file, struct page *page);
  static int afs_symlink_readpage(struct file *file, struct page *page);
 -static void afs_invalidatepage(struct page *page, unsigned int offset,
 -                             unsigned int length);
 +static void afs_invalidate_folio(struct folio *folio, size_t offset,
 +                             size_t length);
  static int afs_releasepage(struct page *page, gfp_t gfp_flags);
  
- static void afs_readahead(struct readahead_control *ractl);
  static ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter);
  static void afs_vm_open(struct vm_area_struct *area);
  static void afs_vm_close(struct vm_area_struct *area);
@@@ -52,12 -50,12 +50,12 @@@ const struct inode_operations afs_file_
  };
  
  const struct address_space_operations afs_file_aops = {
-       .readpage       = afs_readpage,
-       .readahead      = afs_readahead,
+       .readpage       = netfs_readpage,
+       .readahead      = netfs_readahead,
 -      .set_page_dirty = afs_set_page_dirty,
 -      .launder_page   = afs_launder_page,
 +      .dirty_folio    = afs_dirty_folio,
 +      .launder_folio  = afs_launder_folio,
        .releasepage    = afs_releasepage,
 -      .invalidatepage = afs_invalidatepage,
 +      .invalidate_folio = afs_invalidate_folio,
        .write_begin    = afs_write_begin,
        .write_end      = afs_write_end,
        .writepage      = afs_writepage,
@@@ -67,7 -65,7 +65,7 @@@
  const struct address_space_operations afs_symlink_aops = {
        .readpage       = afs_symlink_readpage,
        .releasepage    = afs_releasepage,
 -      .invalidatepage = afs_invalidatepage,
 +      .invalidate_folio = afs_invalidate_folio,
  };
  
  static const struct vm_operations_struct afs_vm_ops = {
@@@ -240,7 -238,7 +238,7 @@@ void afs_put_read(struct afs_read *req
  static void afs_fetch_data_notify(struct afs_operation *op)
  {
        struct afs_read *req = op->fetch.req;
-       struct netfs_read_subrequest *subreq = req->subreq;
+       struct netfs_io_subrequest *subreq = req->subreq;
        int error = op->error;
  
        if (error == -ECONNABORTED)
@@@ -310,7 -308,7 +308,7 @@@ int afs_fetch_data(struct afs_vnode *vn
        return afs_do_sync_operation(op);
  }
  
- static void afs_req_issue_op(struct netfs_read_subrequest *subreq)
+ static void afs_issue_read(struct netfs_io_subrequest *subreq)
  {
        struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
        struct afs_read *fsreq;
@@@ -359,19 -357,13 +357,13 @@@ static int afs_symlink_readpage(struct 
        return ret;
  }
  
- static void afs_init_rreq(struct netfs_read_request *rreq, struct file *file)
+ static int afs_init_request(struct netfs_io_request *rreq, struct file *file)
  {
        rreq->netfs_priv = key_get(afs_file_key(file));
+       return 0;
  }
  
- static bool afs_is_cache_enabled(struct inode *inode)
- {
-       struct fscache_cookie *cookie = afs_vnode_cache(AFS_FS_I(inode));
-       return fscache_cookie_enabled(cookie) && cookie->cache_priv;
- }
- static int afs_begin_cache_operation(struct netfs_read_request *rreq)
+ static int afs_begin_cache_operation(struct netfs_io_request *rreq)
  {
  #ifdef CONFIG_AFS_FSCACHE
        struct afs_vnode *vnode = AFS_FS_I(rreq->inode);
@@@ -396,27 -388,14 +388,14 @@@ static void afs_priv_cleanup(struct add
        key_put(netfs_priv);
  }
  
- const struct netfs_read_request_ops afs_req_ops = {
-       .init_rreq              = afs_init_rreq,
-       .is_cache_enabled       = afs_is_cache_enabled,
+ const struct netfs_request_ops afs_req_ops = {
+       .init_request           = afs_init_request,
        .begin_cache_operation  = afs_begin_cache_operation,
        .check_write_begin      = afs_check_write_begin,
-       .issue_op               = afs_req_issue_op,
+       .issue_read             = afs_issue_read,
        .cleanup                = afs_priv_cleanup,
  };
  
- static int afs_readpage(struct file *file, struct page *page)
- {
-       struct folio *folio = page_folio(page);
-       return netfs_readpage(file, folio, &afs_req_ops, NULL);
- }
- static void afs_readahead(struct readahead_control *ractl)
- {
-       netfs_readahead(ractl, &afs_req_ops, NULL);
- }
  int afs_write_inode(struct inode *inode, struct writeback_control *wbc)
  {
        fscache_unpin_writeback(wbc, afs_vnode_cache(AFS_FS_I(inode)));
   * Adjust the dirty region of the page on truncation or full invalidation,
   * getting rid of the markers altogether if the region is entirely invalidated.
   */
 -static void afs_invalidate_dirty(struct folio *folio, unsigned int offset,
 -                               unsigned int length)
 +static void afs_invalidate_dirty(struct folio *folio, size_t offset,
 +                               size_t length)
  {
        struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));
        unsigned long priv;
@@@ -485,14 -464,16 +464,14 @@@ full_invalidate
   * - release a page and clean up its private data if offset is 0 (indicating
   *   the entire page)
   */
 -static void afs_invalidatepage(struct page *page, unsigned int offset,
 -                             unsigned int length)
 +static void afs_invalidate_folio(struct folio *folio, size_t offset,
 +                             size_t length)
  {
 -      struct folio *folio = page_folio(page);
 -
 -      _enter("{%lu},%u,%u", folio_index(folio), offset, length);
 +      _enter("{%lu},%zu,%zu", folio->index, offset, length);
  
 -      BUG_ON(!PageLocked(page));
 +      BUG_ON(!folio_test_locked(folio));
  
 -      if (PagePrivate(page))
 +      if (folio_get_private(folio))
                afs_invalidate_dirty(folio, offset, length);
  
        folio_wait_fscache(folio);
diff --combined fs/afs/internal.h
@@@ -207,7 -207,7 +207,7 @@@ struct afs_read 
        loff_t                  file_size;      /* File size returned by server */
        struct key              *key;           /* The key to use to reissue the read */
        struct afs_vnode        *vnode;         /* The file being read into. */
-       struct netfs_read_subrequest *subreq;   /* Fscache helper read request this belongs to */
+       struct netfs_io_subrequest *subreq;     /* Fscache helper read request this belongs to */
        afs_dataversion_t       data_version;   /* Version number returned by server */
        refcount_t              usage;
        unsigned int            call_debug_id;
@@@ -619,15 -619,16 +619,16 @@@ enum afs_lock_state 
   * leak from one inode to another.
   */
  struct afs_vnode {
-       struct inode            vfs_inode;      /* the VFS's inode record */
+       struct {
+               /* These must be contiguous */
+               struct inode    vfs_inode;      /* the VFS's inode record */
+               struct netfs_i_context netfs_ctx; /* Netfslib context */
+       };
  
        struct afs_volume       *volume;        /* volume on which vnode resides */
        struct afs_fid          fid;            /* the file identifier for this inode */
        struct afs_file_status  status;         /* AFS status info for this file */
        afs_dataversion_t       invalid_before; /* Child dentries are invalid before this */
- #ifdef CONFIG_AFS_FSCACHE
-       struct fscache_cookie   *cache;         /* caching cookie */
- #endif
        struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */
        struct mutex            io_lock;        /* Lock for serialising I/O on this mutex */
        struct rw_semaphore     validate_lock;  /* lock for validating this vnode */
  static inline struct fscache_cookie *afs_vnode_cache(struct afs_vnode *vnode)
  {
  #ifdef CONFIG_AFS_FSCACHE
-       return vnode->cache;
+       return netfs_i_cookie(&vnode->vfs_inode);
  #else
        return NULL;
  #endif
  }
  
+ static inline void afs_vnode_set_cache(struct afs_vnode *vnode,
+                                      struct fscache_cookie *cookie)
+ {
+ #ifdef CONFIG_AFS_FSCACHE
+       vnode->netfs_ctx.cache = cookie;
+ #endif
+ }
  /*
   * cached security record for one user's attempt to access a vnode
   */
@@@ -1063,7 -1072,7 +1072,7 @@@ extern const struct address_space_opera
  extern const struct address_space_operations afs_symlink_aops;
  extern const struct inode_operations afs_file_inode_operations;
  extern const struct file_operations afs_file_operations;
- extern const struct netfs_read_request_ops afs_req_ops;
+ extern const struct netfs_request_ops afs_req_ops;
  
  extern int afs_cache_wb_key(struct afs_vnode *, struct afs_file *);
  extern void afs_put_wb_key(struct afs_wb_key *);
@@@ -1521,9 -1530,9 +1530,9 @@@ extern int afs_check_volume_status(stru
   * write.c
   */
  #ifdef CONFIG_AFS_FSCACHE
 -extern int afs_set_page_dirty(struct page *);
 +bool afs_dirty_folio(struct address_space *, struct folio *);
  #else
 -#define afs_set_page_dirty __set_page_dirty_nobuffers
 +#define afs_dirty_folio filemap_dirty_folio
  #endif
  extern int afs_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
@@@ -1537,7 -1546,7 +1546,7 @@@ extern ssize_t afs_file_write(struct ki
  extern int afs_fsync(struct file *, loff_t, loff_t, int);
  extern vm_fault_t afs_page_mkwrite(struct vm_fault *vmf);
  extern void afs_prune_wb_keys(struct afs_vnode *);
 -extern int afs_launder_page(struct page *);
 +int afs_launder_folio(struct folio *);
  
  /*
   * xattr.c
diff --combined fs/afs/super.c
@@@ -679,7 -679,7 +679,7 @@@ static struct inode *afs_alloc_inode(st
  {
        struct afs_vnode *vnode;
  
 -      vnode = kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL);
 +      vnode = alloc_inode_sb(sb, afs_inode_cachep, GFP_KERNEL);
        if (!vnode)
                return NULL;
  
        /* Reset anything that shouldn't leak from one inode to the next. */
        memset(&vnode->fid, 0, sizeof(vnode->fid));
        memset(&vnode->status, 0, sizeof(vnode->status));
+       afs_vnode_set_cache(vnode, NULL);
  
        vnode->volume           = NULL;
        vnode->lock_key         = NULL;
        vnode->permit_cache     = NULL;
- #ifdef CONFIG_AFS_FSCACHE
-       vnode->cache            = NULL;
- #endif
  
        vnode->flags            = 1 << AFS_VNODE_UNSET;
        vnode->lock_state       = AFS_VNODE_LOCK_NONE;
diff --combined fs/afs/write.c
@@@ -22,10 -22,9 +22,10 @@@ static void afs_write_to_cache(struct a
   * Mark a page as having been made dirty and thus needing writeback.  We also
   * need to pin the cache object to write back to.
   */
 -int afs_set_page_dirty(struct page *page)
 +bool afs_dirty_folio(struct address_space *mapping, struct folio *folio)
  {
 -      return fscache_set_page_dirty(page, afs_vnode_cache(AFS_FS_I(page->mapping->host)));
 +      return fscache_dirty_folio(mapping, folio,
 +                              afs_vnode_cache(AFS_FS_I(mapping->host)));
  }
  static void afs_folio_start_fscache(bool caching, struct folio *folio)
  {
@@@ -60,8 -59,7 +60,7 @@@ int afs_write_begin(struct file *file, 
         * file.  We need to do this before we get a lock on the page in case
         * there's more than one writer competing for the same cache block.
         */
-       ret = netfs_write_begin(file, mapping, pos, len, flags, &folio, fsdata,
-                               &afs_req_ops, NULL);
+       ret = netfs_write_begin(file, mapping, pos, len, flags, &folio, fsdata);
        if (ret < 0)
                return ret;
  
@@@ -355,9 -353,10 +354,10 @@@ static const struct afs_operation_ops a
  static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos,
                          bool laundering)
  {
+       struct netfs_i_context *ictx = &vnode->netfs_ctx;
        struct afs_operation *op;
        struct afs_wb_key *wbk = NULL;
-       loff_t size = iov_iter_count(iter), i_size;
+       loff_t size = iov_iter_count(iter);
        int ret = -ENOKEY;
  
        _enter("%s{%llx:%llu.%u},%llx,%llx",
                return -ENOMEM;
        }
  
-       i_size = i_size_read(&vnode->vfs_inode);
        afs_op_set_vnode(op, 0, vnode);
        op->file[0].dv_delta = 1;
        op->file[0].modification = true;
        op->store.write_iter = iter;
        op->store.pos = pos;
        op->store.size = size;
-       op->store.i_size = max(pos + size, i_size);
+       op->store.i_size = max(pos + size, ictx->remote_i_size);
        op->store.laundering = laundering;
        op->mtime = vnode->vfs_inode.i_mtime;
        op->flags |= AFS_OPERATION_UNINTR;
@@@ -704,7 -701,7 +702,7 @@@ static int afs_writepages_region(struc
        struct folio *folio;
        struct page *head_page;
        ssize_t ret;
 -      int n;
 +      int n, skips = 0;
  
        _enter("%llx,%llx,", start, end);
  
  #ifdef CONFIG_AFS_FSCACHE
                                folio_wait_fscache(folio);
  #endif
 +                      } else {
 +                              start += folio_size(folio);
                        }
                        folio_put(folio);
 +                      if (wbc->sync_mode == WB_SYNC_NONE) {
 +                              if (skips >= 5 || need_resched())
 +                                      break;
 +                              skips++;
 +                      }
                        continue;
                }
  
@@@ -980,8 -970,9 +978,8 @@@ void afs_prune_wb_keys(struct afs_vnod
  /*
   * Clean up a page during invalidation.
   */
 -int afs_launder_page(struct page *subpage)
 +int afs_launder_folio(struct folio *folio)
  {
 -      struct folio *folio = page_folio(subpage);
        struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio));
        struct iov_iter iter;
        struct bio_vec bv[1];
        unsigned int f, t;
        int ret = 0;
  
 -      _enter("{%lx}", folio_index(folio));
 +      _enter("{%lx}", folio->index);
  
        priv = (unsigned long)folio_get_private(folio);
        if (folio_clear_dirty_for_io(folio)) {
diff --combined fs/cachefiles/io.c
@@@ -138,6 -138,7 +138,6 @@@ static int cachefiles_read(struct netfs
        ki->iocb.ki_filp        = file;
        ki->iocb.ki_pos         = start_pos + skipped;
        ki->iocb.ki_flags       = IOCB_DIRECT;
 -      ki->iocb.ki_hint        = ki_hint_validate(file_write_hint(file));
        ki->iocb.ki_ioprio      = get_current_ioprio();
        ki->skipped             = skipped;
        ki->object              = object;
@@@ -312,6 -313,7 +312,6 @@@ static int cachefiles_write(struct netf
        ki->iocb.ki_filp        = file;
        ki->iocb.ki_pos         = start_pos;
        ki->iocb.ki_flags       = IOCB_DIRECT | IOCB_WRITE;
 -      ki->iocb.ki_hint        = ki_hint_validate(file_write_hint(file));
        ki->iocb.ki_ioprio      = get_current_ioprio();
        ki->object              = object;
        ki->inval_counter       = cres->inval_counter;
@@@ -380,18 -382,18 +380,18 @@@ presubmission_error
   * Prepare a read operation, shortening it to a cached/uncached
   * boundary as appropriate.
   */
- static enum netfs_read_source cachefiles_prepare_read(struct netfs_read_subrequest *subreq,
+ static enum netfs_io_source cachefiles_prepare_read(struct netfs_io_subrequest *subreq,
                                                      loff_t i_size)
  {
        enum cachefiles_prepare_read_trace why;
-       struct netfs_read_request *rreq = subreq->rreq;
+       struct netfs_io_request *rreq = subreq->rreq;
        struct netfs_cache_resources *cres = &rreq->cache_resources;
        struct cachefiles_object *object;
        struct cachefiles_cache *cache;
        struct fscache_cookie *cookie = fscache_cres_cookie(cres);
        const struct cred *saved_cred;
        struct file *file = cachefiles_cres_file(cres);
-       enum netfs_read_source ret = NETFS_DOWNLOAD_FROM_SERVER;
+       enum netfs_io_source ret = NETFS_DOWNLOAD_FROM_SERVER;
        loff_t off, to;
        ino_t ino = file ? file_inode(file)->i_ino : 0;
  
        }
  
        if (test_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags)) {
-               __set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags);
+               __set_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
                why = cachefiles_trace_read_no_data;
                goto out_no_object;
        }
        goto out;
  
  download_and_store:
-       __set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags);
+       __set_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
  out:
        cachefiles_end_secure(cache, saved_cred);
  out_no_object:
diff --combined fs/ceph/addr.c
@@@ -76,17 -76,18 +76,17 @@@ static inline struct ceph_snap_context 
   * Dirty a page.  Optimistically adjust accounting, on the assumption
   * that we won't race with invalidate.  If we do, readjust.
   */
 -static int ceph_set_page_dirty(struct page *page)
 +static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio)
  {
 -      struct address_space *mapping = page->mapping;
        struct inode *inode;
        struct ceph_inode_info *ci;
        struct ceph_snap_context *snapc;
  
 -      if (PageDirty(page)) {
 -              dout("%p set_page_dirty %p idx %lu -- already dirty\n",
 -                   mapping->host, page, page->index);
 -              BUG_ON(!PagePrivate(page));
 -              return 0;
 +      if (folio_test_dirty(folio)) {
 +              dout("%p dirty_folio %p idx %lu -- already dirty\n",
 +                   mapping->host, folio, folio->index);
 +              BUG_ON(!folio_get_private(folio));
 +              return false;
        }
  
        inode = mapping->host;
        if (ci->i_wrbuffer_ref == 0)
                ihold(inode);
        ++ci->i_wrbuffer_ref;
 -      dout("%p set_page_dirty %p idx %lu head %d/%d -> %d/%d "
 +      dout("%p dirty_folio %p idx %lu head %d/%d -> %d/%d "
             "snapc %p seq %lld (%d snaps)\n",
 -           mapping->host, page, page->index,
 +           mapping->host, folio, folio->index,
             ci->i_wrbuffer_ref-1, ci->i_wrbuffer_ref_head-1,
             ci->i_wrbuffer_ref, ci->i_wrbuffer_ref_head,
             snapc, snapc->seq, snapc->num_snaps);
        spin_unlock(&ci->i_ceph_lock);
  
        /*
 -       * Reference snap context in page->private.  Also set
 -       * PagePrivate so that we get invalidatepage callback.
 +       * Reference snap context in folio->private.  Also set
 +       * PagePrivate so that we get invalidate_folio callback.
         */
 -      BUG_ON(PagePrivate(page));
 -      attach_page_private(page, snapc);
 +      BUG_ON(folio_get_private(folio));
 +      folio_attach_private(folio, snapc);
  
 -      return ceph_fscache_set_page_dirty(page);
 +      return ceph_fscache_dirty_folio(mapping, folio);
  }
  
  /*
 - * If we are truncating the full page (i.e. offset == 0), adjust the
 - * dirty page counters appropriately.  Only called if there is private
 - * data on the page.
 + * If we are truncating the full folio (i.e. offset == 0), adjust the
 + * dirty folio counters appropriately.  Only called if there is private
 + * data on the folio.
   */
 -static void ceph_invalidatepage(struct page *page, unsigned int offset,
 -                              unsigned int length)
 +static void ceph_invalidate_folio(struct folio *folio, size_t offset,
 +                              size_t length)
  {
        struct inode *inode;
        struct ceph_inode_info *ci;
        struct ceph_snap_context *snapc;
  
 -      inode = page->mapping->host;
 +      inode = folio->mapping->host;
        ci = ceph_inode(inode);
  
 -      if (offset != 0 || length != thp_size(page)) {
 -              dout("%p invalidatepage %p idx %lu partial dirty page %u~%u\n",
 -                   inode, page, page->index, offset, length);
 +      if (offset != 0 || length != folio_size(folio)) {
 +              dout("%p invalidate_folio idx %lu partial dirty page %zu~%zu\n",
 +                   inode, folio->index, offset, length);
                return;
        }
  
 -      WARN_ON(!PageLocked(page));
 -      if (PagePrivate(page)) {
 -              dout("%p invalidatepage %p idx %lu full dirty page\n",
 -                   inode, page, page->index);
 +      WARN_ON(!folio_test_locked(folio));
 +      if (folio_get_private(folio)) {
 +              dout("%p invalidate_folio idx %lu full dirty page\n",
 +                   inode, folio->index);
  
 -              snapc = detach_page_private(page);
 +              snapc = folio_detach_private(folio);
                ceph_put_wrbuffer_cap_refs(ci, 1, snapc);
                ceph_put_snap_context(snapc);
        }
  
 -      wait_on_page_fscache(page);
 +      folio_wait_fscache(folio);
  }
  
  static int ceph_releasepage(struct page *page, gfp_t gfp)
        return 1;
  }
  
- static void ceph_netfs_expand_readahead(struct netfs_read_request *rreq)
+ static void ceph_netfs_expand_readahead(struct netfs_io_request *rreq)
  {
        struct inode *inode = rreq->inode;
        struct ceph_inode_info *ci = ceph_inode(inode);
        rreq->len = roundup(rreq->len, lo->stripe_unit);
  }
  
- static bool ceph_netfs_clamp_length(struct netfs_read_subrequest *subreq)
+ static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq)
  {
        struct inode *inode = subreq->rreq->inode;
        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
@@@ -218,7 -219,7 +218,7 @@@ static void finish_netfs_read(struct ce
  {
        struct ceph_fs_client *fsc = ceph_inode_to_client(req->r_inode);
        struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0);
-       struct netfs_read_subrequest *subreq = req->r_priv;
+       struct netfs_io_subrequest *subreq = req->r_priv;
        int num_pages;
        int err = req->r_result;
  
        iput(req->r_inode);
  }
  
- static bool ceph_netfs_issue_op_inline(struct netfs_read_subrequest *subreq)
+ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
  {
-       struct netfs_read_request *rreq = subreq->rreq;
+       struct netfs_io_request *rreq = subreq->rreq;
        struct inode *inode = rreq->inode;
        struct ceph_mds_reply_info_parsed *rinfo;
        struct ceph_mds_reply_info_in *iinfo;
        size_t len;
  
        __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-       __clear_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags);
+       __clear_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
  
        if (subreq->start >= inode->i_size)
                goto out;
@@@ -297,9 -298,9 +297,9 @@@ out
        return true;
  }
  
- static void ceph_netfs_issue_op(struct netfs_read_subrequest *subreq)
+ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
  {
-       struct netfs_read_request *rreq = subreq->rreq;
+       struct netfs_io_request *rreq = subreq->rreq;
        struct inode *inode = rreq->inode;
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
@@@ -353,6 -354,45 +353,45 @@@ out
        dout("%s: result %d\n", __func__, err);
  }
  
+ static int ceph_init_request(struct netfs_io_request *rreq, struct file *file)
+ {
+       struct inode *inode = rreq->inode;
+       int got = 0, want = CEPH_CAP_FILE_CACHE;
+       int ret = 0;
+       if (rreq->origin != NETFS_READAHEAD)
+               return 0;
+       if (file) {
+               struct ceph_rw_context *rw_ctx;
+               struct ceph_file_info *fi = file->private_data;
+               rw_ctx = ceph_find_rw_context(fi);
+               if (rw_ctx)
+                       return 0;
+       }
+       /*
+        * readahead callers do not necessarily hold Fcb caps
+        * (e.g. fadvise, madvise).
+        */
+       ret = ceph_try_get_caps(inode, CEPH_CAP_FILE_RD, want, true, &got);
+       if (ret < 0) {
+               dout("start_read %p, error getting cap\n", inode);
+               return ret;
+       }
+       if (!(got & want)) {
+               dout("start_read %p, no cache cap\n", inode);
+               return -EACCES;
+       }
+       if (ret == 0)
+               return -EACCES;
+       rreq->netfs_priv = (void *)(uintptr_t)got;
+       return 0;
+ }
  static void ceph_readahead_cleanup(struct address_space *mapping, void *priv)
  {
        struct inode *inode = mapping->host;
                ceph_put_cap_refs(ci, got);
  }
  
static const struct netfs_read_request_ops ceph_netfs_read_ops = {
-       .is_cache_enabled       = ceph_is_cache_enabled,
const struct netfs_request_ops ceph_netfs_ops = {
+       .init_request           = ceph_init_request,
        .begin_cache_operation  = ceph_begin_cache_operation,
-       .issue_op               = ceph_netfs_issue_op,
+       .issue_read             = ceph_netfs_issue_read,
        .expand_readahead       = ceph_netfs_expand_readahead,
        .clamp_length           = ceph_netfs_clamp_length,
        .check_write_begin      = ceph_netfs_check_write_begin,
        .cleanup                = ceph_readahead_cleanup,
  };
  
- /* read a single page, without unlocking it. */
- static int ceph_readpage(struct file *file, struct page *subpage)
- {
-       struct folio *folio = page_folio(subpage);
-       struct inode *inode = file_inode(file);
-       struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_vino vino = ceph_vino(inode);
-       size_t len = folio_size(folio);
-       u64 off = folio_file_pos(folio);
-       dout("readpage ino %llx.%llx file %p off %llu len %zu folio %p index %lu\n inline %d",
-            vino.ino, vino.snap, file, off, len, folio, folio_index(folio),
-            ci->i_inline_version != CEPH_INLINE_NONE);
-       return netfs_readpage(file, folio, &ceph_netfs_read_ops, NULL);
- }
- static void ceph_readahead(struct readahead_control *ractl)
- {
-       struct inode *inode = file_inode(ractl->file);
-       struct ceph_file_info *fi = ractl->file->private_data;
-       struct ceph_rw_context *rw_ctx;
-       int got = 0;
-       int ret = 0;
-       if (ceph_inode(inode)->i_inline_version != CEPH_INLINE_NONE)
-               return;
-       rw_ctx = ceph_find_rw_context(fi);
-       if (!rw_ctx) {
-               /*
-                * readahead callers do not necessarily hold Fcb caps
-                * (e.g. fadvise, madvise).
-                */
-               int want = CEPH_CAP_FILE_CACHE;
-               ret = ceph_try_get_caps(inode, CEPH_CAP_FILE_RD, want, true, &got);
-               if (ret < 0)
-                       dout("start_read %p, error getting cap\n", inode);
-               else if (!(got & want))
-                       dout("start_read %p, no cache cap\n", inode);
-               if (ret <= 0)
-                       return;
-       }
-       netfs_readahead(ractl, &ceph_netfs_read_ops, (void *)(uintptr_t)got);
- }
  #ifdef CONFIG_CEPH_FSCACHE
  static void ceph_set_page_fscache(struct page *page)
  {
@@@ -558,7 -550,6 +549,7 @@@ static u64 get_writepages_data_length(s
   */
  static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
  {
 +      struct folio *folio = page_folio(page);
        struct inode *inode = page->mapping->host;
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
  
        /* is this a partial page at end of file? */
        if (page_off >= ceph_wbc.i_size) {
 -              dout("%p page eof %llu\n", page, ceph_wbc.i_size);
 -              page->mapping->a_ops->invalidatepage(page, 0, thp_size(page));
 +              dout("folio at %lu beyond eof %llu\n", folio->index,
 +                              ceph_wbc.i_size);
 +              folio_invalidate(folio, 0, folio_size(folio));
                return 0;
        }
  
  
        if (atomic_long_inc_return(&fsc->writeback_count) >
            CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb))
 -              set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
 +              fsc->write_congested = true;
  
        req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), page_off, &len, 0, 1,
                                    CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE, snapc,
  
        if (atomic_long_dec_return(&fsc->writeback_count) <
            CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
 -              clear_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
 +              fsc->write_congested = false;
  
        return err;
  }
@@@ -679,10 -669,6 +670,10 @@@ static int ceph_writepage(struct page *
        BUG_ON(!inode);
        ihold(inode);
  
 +      if (wbc->sync_mode == WB_SYNC_NONE &&
 +          ceph_inode_to_client(inode)->write_congested)
 +              return AOP_WRITEPAGE_ACTIVATE;
 +
        wait_on_page_fscache(page);
  
        err = writepage_nounlock(page, wbc);
@@@ -755,7 -741,8 +746,7 @@@ static void writepages_finish(struct ce
                        if (atomic_long_dec_return(&fsc->writeback_count) <
                             CONGESTION_OFF_THRESH(
                                        fsc->mount_options->congestion_kb))
 -                              clear_bdi_congested(inode_to_bdi(inode),
 -                                                  BLK_RW_ASYNC);
 +                              fsc->write_congested = false;
  
                        ceph_put_snap_context(detach_page_private(page));
                        end_page_writeback(page);
@@@ -807,10 -794,6 +798,10 @@@ static int ceph_writepages_start(struc
        bool done = false;
        bool caching = ceph_is_cache_enabled(inode);
  
 +      if (wbc->sync_mode == WB_SYNC_NONE &&
 +          fsc->write_congested)
 +              return 0;
 +
        dout("writepages_start %p (mode=%s)\n", inode,
             wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
             (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD"));
@@@ -918,16 -901,14 +909,16 @@@ get_more_pages
                                continue;
                        }
                        if (page_offset(page) >= ceph_wbc.i_size) {
 -                              dout("%p page eof %llu\n",
 -                                   page, ceph_wbc.i_size);
 +                              struct folio *folio = page_folio(page);
 +
 +                              dout("folio at %lu beyond eof %llu\n",
 +                                   folio->index, ceph_wbc.i_size);
                                if ((ceph_wbc.size_stable ||
 -                                  page_offset(page) >= i_size_read(inode)) &&
 -                                  clear_page_dirty_for_io(page))
 -                                      mapping->a_ops->invalidatepage(page,
 -                                                              0, thp_size(page));
 -                              unlock_page(page);
 +                                  folio_pos(folio) >= i_size_read(inode)) &&
 +                                  folio_clear_dirty_for_io(folio))
 +                                      folio_invalidate(folio, 0,
 +                                                      folio_size(folio));
 +                              folio_unlock(folio);
                                continue;
                        }
                        if (strip_unit_end && (page->index > strip_unit_end)) {
  
                        if (atomic_long_inc_return(&fsc->writeback_count) >
                            CONGESTION_ON_THRESH(
 -                                  fsc->mount_options->congestion_kb)) {
 -                              set_bdi_congested(inode_to_bdi(inode),
 -                                                BLK_RW_ASYNC);
 -                      }
 -
 +                                  fsc->mount_options->congestion_kb))
 +                              fsc->write_congested = true;
  
                        pages[locked_pages++] = page;
                        pvec.pages[i] = NULL;
@@@ -1327,8 -1311,7 +1318,7 @@@ static int ceph_write_begin(struct fil
        struct folio *folio = NULL;
        int r;
  
-       r = netfs_write_begin(file, inode->i_mapping, pos, len, 0, &folio, NULL,
-                             &ceph_netfs_read_ops, NULL);
+       r = netfs_write_begin(file, inode->i_mapping, pos, len, 0, &folio, NULL);
        if (r == 0)
                folio_wait_fscache(folio);
        if (r < 0) {
  }
  
  const struct address_space_operations ceph_aops = {
-       .readpage = ceph_readpage,
-       .readahead = ceph_readahead,
+       .readpage = netfs_readpage,
+       .readahead = netfs_readahead,
        .writepage = ceph_writepage,
        .writepages = ceph_writepages_start,
        .write_begin = ceph_write_begin,
        .write_end = ceph_write_end,
 -      .set_page_dirty = ceph_set_page_dirty,
 -      .invalidatepage = ceph_invalidatepage,
 +      .dirty_folio = ceph_dirty_folio,
 +      .invalidate_folio = ceph_invalidate_folio,
        .releasepage = ceph_releasepage,
        .direct_IO = noop_direct_IO,
  };
@@@ -1654,10 -1637,9 +1644,10 @@@ int ceph_uninline_data(struct file *fil
        struct ceph_osd_request *req;
        struct ceph_cap_flush *prealloc_cf;
        struct folio *folio = NULL;
 +      u64 inline_version = CEPH_INLINE_NONE;
        struct page *pages[1];
 -      u64 len, inline_version;
        int err = 0;
 +      u64 len;
  
        prealloc_cf = ceph_alloc_cap_flush();
        if (!prealloc_cf)
diff --combined fs/ceph/cache.h
@@@ -26,14 -26,9 +26,9 @@@ void ceph_fscache_unuse_cookie(struct i
  void ceph_fscache_update(struct inode *inode);
  void ceph_fscache_invalidate(struct inode *inode, bool dio_write);
  
- static inline void ceph_fscache_inode_init(struct ceph_inode_info *ci)
- {
-       ci->fscache = NULL;
- }
  static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode_info *ci)
  {
-       return ci->fscache;
+       return netfs_i_cookie(&ci->vfs_inode);
  }
  
  static inline void ceph_fscache_resize(struct inode *inode, loff_t to)
@@@ -54,15 -49,15 +49,15 @@@ static inline void ceph_fscache_unpin_w
        fscache_unpin_writeback(wbc, ceph_fscache_cookie(ceph_inode(inode)));
  }
  
 -static inline int ceph_fscache_set_page_dirty(struct page *page)
 +static inline int ceph_fscache_dirty_folio(struct address_space *mapping,
 +              struct folio *folio)
  {
 -      struct inode *inode = page->mapping->host;
 -      struct ceph_inode_info *ci = ceph_inode(inode);
 +      struct ceph_inode_info *ci = ceph_inode(mapping->host);
  
 -      return fscache_set_page_dirty(page, ceph_fscache_cookie(ci));
 +      return fscache_dirty_folio(mapping, folio, ceph_fscache_cookie(ci));
  }
  
- static inline int ceph_begin_cache_operation(struct netfs_read_request *rreq)
+ static inline int ceph_begin_cache_operation(struct netfs_io_request *rreq)
  {
        struct fscache_cookie *cookie = ceph_fscache_cookie(ceph_inode(rreq->inode));
  
@@@ -91,10 -86,6 +86,6 @@@ static inline void ceph_fscache_unregis
  {
  }
  
- static inline void ceph_fscache_inode_init(struct ceph_inode_info *ci)
- {
- }
  static inline void ceph_fscache_register_inode_cookie(struct inode *inode)
  {
  }
@@@ -133,10 -124,9 +124,10 @@@ static inline void ceph_fscache_unpin_w
  {
  }
  
 -static inline int ceph_fscache_set_page_dirty(struct page *page)
 +static inline int ceph_fscache_dirty_folio(struct address_space *mapping,
 +              struct folio *folio)
  {
 -      return __set_page_dirty_nobuffers(page);
 +      return filemap_dirty_folio(mapping, folio);
  }
  
  static inline bool ceph_is_cache_enabled(struct inode *inode)
        return false;
  }
  
- static inline int ceph_begin_cache_operation(struct netfs_read_request *rreq)
+ static inline int ceph_begin_cache_operation(struct netfs_io_request *rreq)
  {
        return -ENOBUFS;
  }
diff --combined fs/ceph/inode.c
@@@ -87,13 -87,13 +87,13 @@@ struct inode *ceph_get_snapdir(struct i
        if (!S_ISDIR(parent->i_mode)) {
                pr_warn_once("bad snapdir parent type (mode=0%o)\n",
                             parent->i_mode);
 -              return ERR_PTR(-ENOTDIR);
 +              goto err;
        }
  
        if (!(inode->i_state & I_NEW) && !S_ISDIR(inode->i_mode)) {
                pr_warn_once("bad snapdir inode type (mode=0%o)\n",
                             inode->i_mode);
 -              return ERR_PTR(-ENOTDIR);
 +              goto err;
        }
  
        inode->i_mode = parent->i_mode;
        }
  
        return inode;
 +err:
 +      if ((inode->i_state & I_NEW))
 +              discard_new_inode(inode);
 +      else
 +              iput(inode);
 +      return ERR_PTR(-ENOTDIR);
  }
  
  const struct inode_operations ceph_file_iops = {
@@@ -453,12 -447,15 +453,15 @@@ struct inode *ceph_alloc_inode(struct s
        struct ceph_inode_info *ci;
        int i;
  
 -      ci = kmem_cache_alloc(ceph_inode_cachep, GFP_NOFS);
 +      ci = alloc_inode_sb(sb, ceph_inode_cachep, GFP_NOFS);
        if (!ci)
                return NULL;
  
        dout("alloc_inode %p\n", &ci->vfs_inode);
  
+       /* Set parameters for the netfs library */
+       netfs_i_context_init(&ci->vfs_inode, &ceph_netfs_ops);
        spin_lock_init(&ci->i_ceph_lock);
  
        ci->i_version = 0;
        INIT_WORK(&ci->i_work, ceph_inode_work);
        ci->i_work_mask = 0;
        memset(&ci->i_btime, '\0', sizeof(ci->i_btime));
-       ceph_fscache_inode_init(ci);
        return &ci->vfs_inode;
  }
  
diff --combined fs/ceph/super.h
  #include <linux/posix_acl.h>
  #include <linux/refcount.h>
  #include <linux/security.h>
+ #include <linux/netfs.h>
+ #include <linux/fscache.h>
  
  #include <linux/ceph/libceph.h>
  
- #ifdef CONFIG_CEPH_FSCACHE
- #include <linux/fscache.h>
- #endif
  /* large granularity for statfs utilization stats to facilitate
   * large volume sizes on 32-bit machines. */
  #define CEPH_BLOCK_SHIFT   22  /* 4 MB */
@@@ -121,7 -119,6 +119,7 @@@ struct ceph_fs_client 
        struct ceph_mds_client *mdsc;
  
        atomic_long_t writeback_count;
 +      bool write_congested;
  
        struct workqueue_struct *inode_wq;
        struct workqueue_struct *cap_wq;
@@@ -318,6 -315,11 +316,11 @@@ struct ceph_inode_xattrs_info 
   * Ceph inode.
   */
  struct ceph_inode_info {
+       struct {
+               /* These must be contiguous */
+               struct inode vfs_inode;
+               struct netfs_i_context netfs_ctx; /* Netfslib context */
+       };
        struct ceph_vino i_vino;   /* ceph ino + snap */
  
        spinlock_t i_ceph_lock;
  
        struct work_struct i_work;
        unsigned long  i_work_mask;
- #ifdef CONFIG_CEPH_FSCACHE
-       struct fscache_cookie *fscache;
- #endif
-       struct inode vfs_inode; /* at end */
  };
  
  static inline struct ceph_inode_info *
@@@ -1216,6 -1213,7 +1214,7 @@@ extern void __ceph_touch_fmode(struct c
  
  /* addr.c */
  extern const struct address_space_operations ceph_aops;
+ extern const struct netfs_request_ops ceph_netfs_ops;
  extern int ceph_mmap(struct file *file, struct vm_area_struct *vma);
  extern int ceph_uninline_data(struct file *file);
  extern int ceph_pool_perm_check(struct inode *inode, int need);
diff --combined fs/nfs/fscache.c
@@@ -19,7 -19,8 +19,7 @@@
  #include "internal.h"
  #include "iostat.h"
  #include "fscache.h"
 -
 -#define NFSDBG_FACILITY               NFSDBG_FSCACHE
 +#include "nfstrace.h"
  
  #define NFS_MAX_KEY_LEN 1000
  
@@@ -127,6 -128,8 +127,6 @@@ int nfs_fscache_get_super_cookie(struc
        vcookie = fscache_acquire_volume(key,
                                         NULL, /* preferred_cache */
                                         NULL, 0 /* coherency_data */);
 -      dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n",
 -               nfss, vcookie);
        if (IS_ERR(vcookie)) {
                if (vcookie != ERR_PTR(-EBUSY)) {
                        kfree(key);
@@@ -149,6 -152,9 +149,6 @@@ void nfs_fscache_release_super_cookie(s
  {
        struct nfs_server *nfss = NFS_SB(sb);
  
 -      dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n",
 -               nfss, nfss->fscache);
 -
        fscache_relinquish_volume(nfss->fscache, NULL, false);
        nfss->fscache = NULL;
        kfree(nfss->fscache_uniq);
@@@ -167,7 -173,7 +167,7 @@@ void nfs_fscache_init_inode(struct inod
        if (!(nfss->fscache && S_ISREG(inode->i_mode)))
                return;
  
 -      nfs_fscache_update_auxdata(&auxdata, nfsi);
 +      nfs_fscache_update_auxdata(&auxdata, inode);
  
        nfsi->fscache = fscache_acquire_cookie(NFS_SB(inode->i_sb)->fscache,
                                               0,
                                               nfsi->fh.size,
                                               &auxdata,      /* aux_data */
                                               sizeof(auxdata),
 -                                             i_size_read(&nfsi->vfs_inode));
 +                                             i_size_read(inode));
  }
  
  /*
@@@ -186,6 -192,8 +186,6 @@@ void nfs_fscache_clear_inode(struct ino
        struct nfs_inode *nfsi = NFS_I(inode);
        struct fscache_cookie *cookie = nfs_i_fscache(inode);
  
 -      dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", nfsi, cookie);
 -
        fscache_relinquish_cookie(cookie, false);
        nfsi->fscache = NULL;
  }
  void nfs_fscache_open_file(struct inode *inode, struct file *filp)
  {
        struct nfs_fscache_inode_auxdata auxdata;
 -      struct nfs_inode *nfsi = NFS_I(inode);
        struct fscache_cookie *cookie = nfs_i_fscache(inode);
        bool open_for_write = inode_is_open_for_write(inode);
  
  
        fscache_use_cookie(cookie, open_for_write);
        if (open_for_write) {
 -              dfprintk(FSCACHE, "NFS: nfsi 0x%p disabling cache\n", nfsi);
 -              nfs_fscache_update_auxdata(&auxdata, nfsi);
 +              nfs_fscache_update_auxdata(&auxdata, inode);
                fscache_invalidate(cookie, &auxdata, i_size_read(inode),
                                   FSCACHE_INVAL_DIO_WRITE);
        }
@@@ -230,22 -240,15 +230,14 @@@ EXPORT_SYMBOL_GPL(nfs_fscache_open_file
  void nfs_fscache_release_file(struct inode *inode, struct file *filp)
  {
        struct nfs_fscache_inode_auxdata auxdata;
 -      struct nfs_inode *nfsi = NFS_I(inode);
        struct fscache_cookie *cookie = nfs_i_fscache(inode);
  
        if (fscache_cookie_valid(cookie)) {
 -              nfs_fscache_update_auxdata(&auxdata, nfsi);
 +              nfs_fscache_update_auxdata(&auxdata, inode);
                fscache_unuse_cookie(cookie, &auxdata, NULL);
        }
  }
  
- static inline void fscache_end_operation(struct netfs_cache_resources *cres)
- {
-       const struct netfs_cache_ops *ops = fscache_operation_valid(cres);
-       if (ops)
-               ops->end_operation(cres);
- }
  /*
   * Fallback page reading interface.
   */
@@@ -308,50 -311,58 +300,50 @@@ static int fscache_fallback_write_page(
  /*
   * Retrieve a page from fscache
   */
 -int __nfs_readpage_from_fscache(struct inode *inode, struct page *page)
 +int __nfs_fscache_read_page(struct inode *inode, struct page *page)
  {
        int ret;
  
 -      dfprintk(FSCACHE,
 -               "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n",
 -               nfs_i_fscache(inode), page, page->index, page->flags, inode);
 -
 +      trace_nfs_fscache_read_page(inode, page);
        if (PageChecked(page)) {
 -              dfprintk(FSCACHE, "NFS:    readpage_from_fscache: PageChecked\n");
                ClearPageChecked(page);
 -              return 1;
 +              ret = 1;
 +              goto out;
        }
  
        ret = fscache_fallback_read_page(inode, page);
        if (ret < 0) {
                nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL);
 -              dfprintk(FSCACHE,
 -                       "NFS:    readpage_from_fscache failed %d\n", ret);
                SetPageChecked(page);
 -              return ret;
 +              goto out;
        }
  
        /* Read completed synchronously */
 -      dfprintk(FSCACHE, "NFS:    readpage_from_fscache: read successful\n");
        nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK);
        SetPageUptodate(page);
 -      return 0;
 +      ret = 0;
 +out:
 +      trace_nfs_fscache_read_page_exit(inode, page, ret);
 +      return ret;
  }
  
  /*
   * Store a newly fetched page in fscache.  We can be certain there's no page
   * stored in the cache as yet otherwise we would've read it from there.
   */
 -void __nfs_readpage_to_fscache(struct inode *inode, struct page *page)
 +void __nfs_fscache_write_page(struct inode *inode, struct page *page)
  {
        int ret;
  
 -      dfprintk(FSCACHE,
 -               "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx))\n",
 -               nfs_i_fscache(inode), page, page->index, page->flags);
 +      trace_nfs_fscache_write_page(inode, page);
  
        ret = fscache_fallback_write_page(inode, page, true);
  
 -      dfprintk(FSCACHE,
 -               "NFS:     readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n",
 -               page, page->index, page->flags, ret);
 -
        if (ret != 0) {
                nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL);
                nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED);
        } else {
                nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_OK);
        }
 +      trace_nfs_fscache_write_page_exit(inode, page, ret);
  }
diff --combined include/linux/fscache.h
@@@ -456,6 -456,20 +456,20 @@@ int fscache_begin_read_operation(struc
        return -ENOBUFS;
  }
  
+ /**
+  * fscache_end_operation - End the read operation for the netfs lib
+  * @cres: The cache resources for the read operation
+  *
+  * Clean up the resources at the end of the read request.
+  */
+ static inline void fscache_end_operation(struct netfs_cache_resources *cres)
+ {
+       const struct netfs_cache_ops *ops = fscache_operation_valid(cres);
+       if (ops)
+               ops->end_operation(cres);
+ }
  /**
   * fscache_read - Start a read from the cache.
   * @cres: The cache resources to use
@@@ -616,11 -630,9 +630,11 @@@ static inline void fscache_write_to_cac
  }
  
  #if __fscache_available
 -extern int fscache_set_page_dirty(struct page *page, struct fscache_cookie *cookie);
 +bool fscache_dirty_folio(struct address_space *mapping, struct folio *folio,
 +              struct fscache_cookie *cookie);
  #else
 -#define fscache_set_page_dirty(PAGE, COOKIE) (__set_page_dirty_nobuffers((PAGE)))
 +#define fscache_dirty_folio(MAPPING, FOLIO, COOKIE) \
 +              filemap_dirty_folio(MAPPING, FOLIO)
  #endif
  
  /**
   * @wbc: The writeback control
   * @cookie: The cookie referring to the cache object
   *
 - * Unpin the writeback resources pinned by fscache_set_page_dirty().  This is
 + * Unpin the writeback resources pinned by fscache_dirty_folio().  This is
   * intended to be called by the netfs's ->write_inode() method.
   */
  static inline void fscache_unpin_writeback(struct writeback_control *wbc,
@@@ -56,7 -56,6 +56,7 @@@ enum cachefiles_coherency_trace 
        cachefiles_coherency_set_ok,
        cachefiles_coherency_vol_check_cmp,
        cachefiles_coherency_vol_check_ok,
 +      cachefiles_coherency_vol_check_resv,
        cachefiles_coherency_vol_check_xattr,
        cachefiles_coherency_vol_set_fail,
        cachefiles_coherency_vol_set_ok,
@@@ -140,7 -139,6 +140,7 @@@ enum cachefiles_error_trace 
        EM(cachefiles_coherency_set_ok,         "SET ok  ")             \
        EM(cachefiles_coherency_vol_check_cmp,  "VOL BAD cmp ")         \
        EM(cachefiles_coherency_vol_check_ok,   "VOL OK      ")         \
 +      EM(cachefiles_coherency_vol_check_resv, "VOL BAD resv") \
        EM(cachefiles_coherency_vol_check_xattr,"VOL BAD xatt")         \
        EM(cachefiles_coherency_vol_set_fail,   "VOL SET fail")         \
        E_(cachefiles_coherency_vol_set_ok,     "VOL SET ok  ")
@@@ -426,8 -424,8 +426,8 @@@ TRACE_EVENT(cachefiles_vol_coherency
            );
  
  TRACE_EVENT(cachefiles_prep_read,
-           TP_PROTO(struct netfs_read_subrequest *sreq,
-                    enum netfs_read_source source,
+           TP_PROTO(struct netfs_io_subrequest *sreq,
+                    enum netfs_io_source source,
                     enum cachefiles_prepare_read_trace why,
                     ino_t cache_inode),
  
                    __field(unsigned int,               rreq            )
                    __field(unsigned short,             index           )
                    __field(unsigned short,             flags           )
-                   __field(enum netfs_read_source,     source          )
+                   __field(enum netfs_io_source,       source          )
                    __field(enum cachefiles_prepare_read_trace, why     )
                    __field(size_t,                     len             )
                    __field(loff_t,                     start           )