netfs: Add iov_iters to (sub)requests to describe various buffers
authorDavid Howells <dhowells@redhat.com>
Tue, 26 Sep 2023 16:42:26 +0000 (17:42 +0100)
committerDavid Howells <dhowells@redhat.com>
Sun, 24 Dec 2023 15:08:52 +0000 (15:08 +0000)
Add three iov_iter structs:

 (1) Add an iov_iter (->iter) to the I/O request to describe the
     unencrypted-side buffer.

 (2) Add an iov_iter (->io_iter) to the I/O request to describe the
     encrypted-side I/O buffer.  This may be a different size to the buffer
     in (1).

 (3) Add an iov_iter (->io_iter) to the I/O subrequest to describe the part
     of the I/O buffer for that subrequest.

This will allow future patches to point to a bounce buffer instead for
purposes of handling oversize writes, decryption (where we want to save the
encrypted data to the cache) and decompression.

These iov_iters persist for the lifetime of the (sub)request, and so can be
accessed multiple times without worrying about them being deallocated upon
return to the caller.

The network filesystem must appropriately advance the iterator before
terminating the request.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
cc: linux-fsdevel@vger.kernel.org
cc: linux-mm@kvack.org

fs/afs/file.c
fs/netfs/buffered_read.c
fs/netfs/io.c
include/linux/netfs.h

index d152ba4..3403bb7 100644 (file)
@@ -323,11 +323,7 @@ static void afs_issue_read(struct netfs_io_subrequest *subreq)
        fsreq->len      = subreq->len   - subreq->transferred;
        fsreq->key      = key_get(subreq->rreq->netfs_priv);
        fsreq->vnode    = vnode;
-       fsreq->iter     = &fsreq->def_iter;
-
-       iov_iter_xarray(&fsreq->def_iter, ITER_DEST,
-                       &fsreq->vnode->netfs.inode.i_mapping->i_pages,
-                       fsreq->pos, fsreq->len);
+       fsreq->iter     = &subreq->io_iter;
 
        afs_fetch_data(fsreq->vnode, fsreq);
        afs_put_read(fsreq);
index d39d0ff..751556f 100644 (file)
@@ -199,6 +199,10 @@ void netfs_readahead(struct readahead_control *ractl)
 
        netfs_rreq_expand(rreq, ractl);
 
+       /* Set up the output buffer */
+       iov_iter_xarray(&rreq->iter, ITER_DEST, &ractl->mapping->i_pages,
+                       rreq->start, rreq->len);
+
        /* Drop the refs on the folios here rather than in the cache or
         * filesystem.  The locks will be dropped in netfs_rreq_unlock().
         */
@@ -251,6 +255,11 @@ int netfs_read_folio(struct file *file, struct folio *folio)
 
        netfs_stat(&netfs_n_rh_readpage);
        trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_readpage);
+
+       /* Set up the output buffer */
+       iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
+                       rreq->start, rreq->len);
+
        return netfs_begin_read(rreq, true);
 
 discard:
@@ -408,6 +417,10 @@ retry:
        ractl._nr_pages = folio_nr_pages(folio);
        netfs_rreq_expand(rreq, &ractl);
 
+       /* Set up the output buffer */
+       iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
+                       rreq->start, rreq->len);
+
        /* We hold the folio locks, so we can drop the references */
        folio_get(folio);
        while (readahead_folio(&ractl))
index 7f75338..e9d408e 100644 (file)
  */
 static void netfs_clear_unread(struct netfs_io_subrequest *subreq)
 {
-       struct iov_iter iter;
-
-       iov_iter_xarray(&iter, ITER_DEST, &subreq->rreq->mapping->i_pages,
-                       subreq->start + subreq->transferred,
-                       subreq->len   - subreq->transferred);
-       iov_iter_zero(iov_iter_count(&iter), &iter);
+       iov_iter_zero(iov_iter_count(&subreq->io_iter), &subreq->io_iter);
 }
 
 static void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error,
@@ -46,14 +41,9 @@ static void netfs_read_from_cache(struct netfs_io_request *rreq,
                                  enum netfs_read_from_hole read_hole)
 {
        struct netfs_cache_resources *cres = &rreq->cache_resources;
-       struct iov_iter iter;
 
        netfs_stat(&netfs_n_rh_read);
-       iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages,
-                       subreq->start + subreq->transferred,
-                       subreq->len   - subreq->transferred);
-
-       cres->ops->read(cres, subreq->start, &iter, read_hole,
+       cres->ops->read(cres, subreq->start, &subreq->io_iter, read_hole,
                        netfs_cache_read_terminated, subreq);
 }
 
@@ -88,6 +78,11 @@ static void netfs_read_from_server(struct netfs_io_request *rreq,
                                   struct netfs_io_subrequest *subreq)
 {
        netfs_stat(&netfs_n_rh_download);
+       if (iov_iter_count(&subreq->io_iter) != subreq->len - subreq->transferred)
+               pr_warn("R=%08x[%u] ITER PRE-MISMATCH %zx != %zx-%zx %lx\n",
+                       rreq->debug_id, subreq->debug_index,
+                       iov_iter_count(&subreq->io_iter), subreq->len,
+                       subreq->transferred, subreq->flags);
        rreq->netfs_ops->issue_read(subreq);
 }
 
@@ -259,6 +254,30 @@ static void netfs_rreq_short_read(struct netfs_io_request *rreq,
                netfs_read_from_server(rreq, subreq);
 }
 
+/*
+ * Reset the subrequest iterator prior to resubmission.
+ */
+static void netfs_reset_subreq_iter(struct netfs_io_request *rreq,
+                                   struct netfs_io_subrequest *subreq)
+{
+       size_t remaining = subreq->len - subreq->transferred;
+       size_t count = iov_iter_count(&subreq->io_iter);
+
+       if (count == remaining)
+               return;
+
+       _debug("R=%08x[%u] ITER RESUB-MISMATCH %zx != %zx-%zx-%llx %x\n",
+              rreq->debug_id, subreq->debug_index,
+              iov_iter_count(&subreq->io_iter), subreq->transferred,
+              subreq->len, rreq->i_size,
+              subreq->io_iter.iter_type);
+
+       if (count < remaining)
+               iov_iter_revert(&subreq->io_iter, remaining - count);
+       else
+               iov_iter_advance(&subreq->io_iter, count - remaining);
+}
+
 /*
  * Resubmit any short or failed operations.  Returns true if we got the rreq
  * ref back.
@@ -287,6 +306,7 @@ static bool netfs_rreq_perform_resubmissions(struct netfs_io_request *rreq)
                        trace_netfs_sreq(subreq, netfs_sreq_trace_download_instead);
                        netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
                        atomic_inc(&rreq->nr_outstanding);
+                       netfs_reset_subreq_iter(rreq, subreq);
                        netfs_read_from_server(rreq, subreq);
                } else if (test_bit(NETFS_SREQ_SHORT_IO, &subreq->flags)) {
                        netfs_rreq_short_read(rreq, subreq);
@@ -399,9 +419,9 @@ void netfs_subreq_terminated(struct netfs_io_subrequest *subreq,
        struct netfs_io_request *rreq = subreq->rreq;
        int u;
 
-       _enter("[%u]{%llx,%lx},%zd",
-              subreq->debug_index, subreq->start, subreq->flags,
-              transferred_or_error);
+       _enter("R=%x[%x]{%llx,%lx},%zd",
+              rreq->debug_id, subreq->debug_index,
+              subreq->start, subreq->flags, transferred_or_error);
 
        switch (subreq->source) {
        case NETFS_READ_FROM_CACHE:
@@ -501,7 +521,8 @@ static enum netfs_io_source netfs_cache_prepare_read(struct netfs_io_subrequest
  */
 static enum netfs_io_source
 netfs_rreq_prepare_read(struct netfs_io_request *rreq,
-                       struct netfs_io_subrequest *subreq)
+                       struct netfs_io_subrequest *subreq,
+                       struct iov_iter *io_iter)
 {
        enum netfs_io_source source;
 
@@ -528,9 +549,14 @@ netfs_rreq_prepare_read(struct netfs_io_request *rreq,
                }
        }
 
-       if (WARN_ON(subreq->len == 0))
+       if (WARN_ON(subreq->len == 0)) {
                source = NETFS_INVALID_READ;
+               goto out;
+       }
 
+       subreq->io_iter = *io_iter;
+       iov_iter_truncate(&subreq->io_iter, subreq->len);
+       iov_iter_advance(io_iter, subreq->len);
 out:
        subreq->source = source;
        trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
@@ -541,6 +567,7 @@ out:
  * Slice off a piece of a read request and submit an I/O request for it.
  */
 static bool netfs_rreq_submit_slice(struct netfs_io_request *rreq,
+                                   struct iov_iter *io_iter,
                                    unsigned int *_debug_index)
 {
        struct netfs_io_subrequest *subreq;
@@ -565,7 +592,7 @@ static bool netfs_rreq_submit_slice(struct netfs_io_request *rreq,
         * (the starts must coincide), in which case, we go around the loop
         * again and ask it to download the next piece.
         */
-       source = netfs_rreq_prepare_read(rreq, subreq);
+       source = netfs_rreq_prepare_read(rreq, subreq, io_iter);
        if (source == NETFS_INVALID_READ)
                goto subreq_failed;
 
@@ -603,6 +630,7 @@ subreq_failed:
  */
 int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
 {
+       struct iov_iter io_iter;
        unsigned int debug_index = 0;
        int ret;
 
@@ -615,6 +643,8 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
                return -EIO;
        }
 
+       rreq->io_iter = rreq->iter;
+
        INIT_WORK(&rreq->work, netfs_rreq_work);
 
        if (sync)
@@ -624,8 +654,9 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
         * want and submit each one.
         */
        atomic_set(&rreq->nr_outstanding, 1);
+       io_iter = rreq->io_iter;
        do {
-               if (!netfs_rreq_submit_slice(rreq, &debug_index))
+               if (!netfs_rreq_submit_slice(rreq, &io_iter, &debug_index))
                        break;
 
        } while (rreq->submitted < rreq->len);
index fc6d975..3da962e 100644 (file)
@@ -150,6 +150,7 @@ struct netfs_cache_resources {
 struct netfs_io_subrequest {
        struct netfs_io_request *rreq;          /* Supervising I/O request */
        struct list_head        rreq_link;      /* Link in rreq->subrequests */
+       struct iov_iter         io_iter;        /* Iterator for this subrequest */
        loff_t                  start;          /* Where to start the I/O */
        size_t                  len;            /* Size of the I/O */
        size_t                  transferred;    /* Amount of data transferred */
@@ -186,6 +187,8 @@ struct netfs_io_request {
        struct netfs_cache_resources cache_resources;
        struct list_head        proc_link;      /* Link in netfs_iorequests */
        struct list_head        subrequests;    /* Contributory I/O operations */
+       struct iov_iter         iter;           /* Unencrypted-side iterator */
+       struct iov_iter         io_iter;        /* I/O (Encrypted-side) iterator */
        void                    *netfs_priv;    /* Private data for the netfs */
        unsigned int            debug_id;
        atomic_t                nr_outstanding; /* Number of ops in progress */