Hi David,

On Fri, Nov 08, 2024 at 05:32:29PM +0000, David Howells wrote:
...
> diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> index 264f3cb6a7dc..8ca0558570c1 100644
> --- a/fs/netfs/read_retry.c
> +++ b/fs/netfs/read_retry.c
> @@ -12,15 +12,8 @@
>  static void netfs_reissue_read(struct netfs_io_request *rreq,
>                              struct netfs_io_subrequest *subreq)
>  {
> -     struct iov_iter *io_iter = &subreq->io_iter;
> -
> -     if (iov_iter_is_folioq(io_iter)) {
> -             subreq->curr_folioq = (struct folio_queue *)io_iter->folioq;
> -             subreq->curr_folioq_slot = io_iter->folioq_slot;
> -             subreq->curr_folio_order = 
> subreq->curr_folioq->orders[subreq->curr_folioq_slot];
> -     }
> -
> -     atomic_inc(&rreq->nr_outstanding);
> +     __clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
> +     __set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
>       __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
>       netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
>       subreq->rreq->netfs_ops->issue_read(subreq);
> @@ -33,13 +26,12 @@ static void netfs_reissue_read(struct netfs_io_request 
> *rreq,
>  static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
>  {
>       struct netfs_io_subrequest *subreq;
> -     struct netfs_io_stream *stream0 = &rreq->io_streams[0];
> -     LIST_HEAD(sublist);
> -     LIST_HEAD(queue);
> +     struct netfs_io_stream *stream = &rreq->io_streams[0];
> +     struct list_head *next;
>  
>       _enter("R=%x", rreq->debug_id);
>  
> -     if (list_empty(&rreq->subrequests))
> +     if (list_empty(&stream->subrequests))
>               return;
>  
>       if (rreq->netfs_ops->retry_request)
> @@ -52,7 +44,7 @@ static void netfs_retry_read_subrequests(struct 
> netfs_io_request *rreq)
>           !test_bit(NETFS_RREQ_COPY_TO_CACHE, &rreq->flags)) {
>               struct netfs_io_subrequest *subreq;
>  
> -             list_for_each_entry(subreq, &rreq->subrequests, rreq_link) {
> +             list_for_each_entry(subreq, &stream->subrequests, rreq_link) {
>                       if (test_bit(NETFS_SREQ_FAILED, &subreq->flags))
>                               break;
>                       if (__test_and_clear_bit(NETFS_SREQ_NEED_RETRY, 
> &subreq->flags)) {
> @@ -73,48 +65,44 @@ static void netfs_retry_read_subrequests(struct 
> netfs_io_request *rreq)
>        * populating with smaller subrequests.  In the event that the subreq
>        * we just launched finishes before we insert the next subreq, it'll
>        * fill in rreq->prev_donated instead.
> -
> +      *
>        * Note: Alternatively, we could split the tail subrequest right before
>        * we reissue it and fix up the donations under lock.
>        */
> -     list_splice_init(&rreq->subrequests, &queue);
> +     next = stream->subrequests.next;
>  
>       do {
> -             struct netfs_io_subrequest *from;
> +             struct netfs_io_subrequest *subreq = NULL, *from, *to, *tmp;
>               struct iov_iter source;
>               unsigned long long start, len;
> -             size_t part, deferred_next_donated = 0;
> +             size_t part;
>               bool boundary = false;
>  
>               /* Go through the subreqs and find the next span of contiguous
>                * buffer that we then rejig (cifs, for example, needs the
>                * rsize renegotiating) and reissue.
>                */
> -             from = list_first_entry(&queue, struct netfs_io_subrequest, 
> rreq_link);
> -             list_move_tail(&from->rreq_link, &sublist);
> +             from = list_entry(next, struct netfs_io_subrequest, rreq_link);
> +             to = from;
>               start = from->start + from->transferred;
>               len   = from->len   - from->transferred;
>  
> -             _debug("from R=%08x[%x] s=%llx ctl=%zx/%zx/%zx",
> +             _debug("from R=%08x[%x] s=%llx ctl=%zx/%zx",
>                      rreq->debug_id, from->debug_index,
> -                    from->start, from->consumed, from->transferred, 
> from->len);
> +                    from->start, from->transferred, from->len);
>  
>               if (test_bit(NETFS_SREQ_FAILED, &from->flags) ||
>                   !test_bit(NETFS_SREQ_NEED_RETRY, &from->flags))
>                       goto abandon;
>  
> -             deferred_next_donated = from->next_donated;
> -             while ((subreq = list_first_entry_or_null(
> -                             &queue, struct netfs_io_subrequest, 
> rreq_link))) {
> -                     if (subreq->start != start + len ||
> -                         subreq->transferred > 0 ||
> +             list_for_each_continue(next, &stream->subrequests) {
> +                     subreq = list_entry(next, struct netfs_io_subrequest, 
> rreq_link);
> +                     if (subreq->start + subreq->transferred != start + len 
> ||
> +                         test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags) ||
>                           !test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags))
>                               break;
> -                     list_move_tail(&subreq->rreq_link, &sublist);
> -                     len += subreq->len;
> -                     deferred_next_donated = subreq->next_donated;
> -                     if (test_bit(NETFS_SREQ_BOUNDARY, &subreq->flags))
> -                             break;
> +                     to = subreq;
> +                     len += to->len;
>               }
>  
>               _debug(" - range: %llx-%llx %llx", start, start + len - 1, len);
> @@ -127,36 +115,28 @@ static void netfs_retry_read_subrequests(struct 
> netfs_io_request *rreq)
>               source.count = len;
>  
>               /* Work through the sublist. */
> -             while ((subreq = list_first_entry_or_null(
> -                             &sublist, struct netfs_io_subrequest, 
> rreq_link))) {
> -                     list_del(&subreq->rreq_link);
> -
> +             subreq = from;
> +             list_for_each_entry_from(subreq, &stream->subrequests, 
> rreq_link) {
> +                     if (!len)
> +                             break;
>                       subreq->source  = NETFS_DOWNLOAD_FROM_SERVER;
>                       subreq->start   = start - subreq->transferred;
>                       subreq->len     = len   + subreq->transferred;
> -                     stream0->sreq_max_len = subreq->len;
> -
>                       __clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
>                       __set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
> -
> -                     spin_lock(&rreq->lock);
> -                     list_add_tail(&subreq->rreq_link, &rreq->subrequests);
> -                     subreq->prev_donated += rreq->prev_donated;
> -                     rreq->prev_donated = 0;
>                       trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
> -                     spin_unlock(&rreq->lock);
> -
> -                     BUG_ON(!len);
>  
>                       /* Renegotiate max_len (rsize) */
> +                     stream->sreq_max_len = subreq->len;
>                       if (rreq->netfs_ops->prepare_read(subreq) < 0) {
>                               trace_netfs_sreq(subreq, 
> netfs_sreq_trace_reprep_failed);
>                               __set_bit(NETFS_SREQ_FAILED, &subreq->flags);
> +                             goto abandon;
>                       }
>  
> -                     part = umin(len, stream0->sreq_max_len);
> -                     if (unlikely(rreq->io_streams[0].sreq_max_segs))
> -                             part = netfs_limit_iter(&source, 0, part, 
> stream0->sreq_max_segs);
> +                     part = umin(len, stream->sreq_max_len);
> +                     if (unlikely(stream->sreq_max_segs))
> +                             part = netfs_limit_iter(&source, 0, part, 
> stream->sreq_max_segs);
>                       subreq->len = subreq->transferred + part;
>                       subreq->io_iter = source;
>                       iov_iter_truncate(&subreq->io_iter, part);
> @@ -166,58 +146,106 @@ static void netfs_retry_read_subrequests(struct 
> netfs_io_request *rreq)
>                       if (!len) {
>                               if (boundary)
>                                       __set_bit(NETFS_SREQ_BOUNDARY, 
> &subreq->flags);
> -                             subreq->next_donated = deferred_next_donated;
>                       } else {
>                               __clear_bit(NETFS_SREQ_BOUNDARY, 
> &subreq->flags);
> -                             subreq->next_donated = 0;
>                       }
>  
> +                     netfs_get_subrequest(subreq, 
> netfs_sreq_trace_get_resubmit);
>                       netfs_reissue_read(rreq, subreq);
> -                     if (!len)
> +                     if (subreq == to)
>                               break;
> -
> -                     /* If we ran out of subrequests, allocate another. */
> -                     if (list_empty(&sublist)) {
> -                             subreq = netfs_alloc_subrequest(rreq);
> -                             if (!subreq)
> -                                     goto abandon;
> -                             subreq->source = NETFS_DOWNLOAD_FROM_SERVER;
> -                             subreq->start = start;
> -
> -                             /* We get two refs, but need just one. */
> -                             netfs_put_subrequest(subreq, false, 
> netfs_sreq_trace_new);
> -                             trace_netfs_sreq(subreq, 
> netfs_sreq_trace_split);
> -                             list_add_tail(&subreq->rreq_link, &sublist);
> -                     }
>               }
>  
>               /* If we managed to use fewer subreqs, we can discard the
> -              * excess.
> +              * excess; if we used the same number, then we're done.
>                */
> -             while ((subreq = list_first_entry_or_null(
> -                             &sublist, struct netfs_io_subrequest, 
> rreq_link))) {
> -                     trace_netfs_sreq(subreq, netfs_sreq_trace_discard);
> -                     list_del(&subreq->rreq_link);
> -                     netfs_put_subrequest(subreq, false, 
> netfs_sreq_trace_put_done);
> +             if (!len) {
> +                     if (subreq == to)
> +                             continue;
> +                     list_for_each_entry_safe_from(subreq, tmp,
> +                                                   &stream->subrequests, 
> rreq_link) {
> +                             trace_netfs_sreq(subreq, 
> netfs_sreq_trace_discard);
> +                             list_del(&subreq->rreq_link);
> +                             netfs_put_subrequest(subreq, false, 
> netfs_sreq_trace_put_done);
> +                             if (subreq == to)
> +                                     break;
> +                     }
> +                     continue;
>               }
>  
> -     } while (!list_empty(&queue));
> +             /* We ran out of subrequests, so we need to allocate some more
> +              * and insert them after.
> +              */
> +             do {
> +                     subreq = netfs_alloc_subrequest(rreq);
> +                     if (!subreq) {
> +                             subreq = to;
> +                             goto abandon_after;
> +                     }
> +                     subreq->source          = NETFS_DOWNLOAD_FROM_SERVER;
> +                     subreq->start           = start;
> +                     subreq->len             = len;
> +                     subreq->debug_index     = 
> atomic_inc_return(&rreq->subreq_counter);
> +                     subreq->stream_nr       = stream->stream_nr;
> +                     __set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
> +
> +                     trace_netfs_sreq_ref(rreq->debug_id, 
> subreq->debug_index,
> +                                          refcount_read(&subreq->ref),
> +                                          netfs_sreq_trace_new);
> +                     netfs_get_subrequest(subreq, 
> netfs_sreq_trace_get_resubmit);
> +
> +                     list_add(&subreq->rreq_link, &to->rreq_link);
> +                     to = list_next_entry(to, rreq_link);
> +                     trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
> +
> +                     stream->sreq_max_len    = umin(len, rreq->rsize);
> +                     stream->sreq_max_segs   = 0;
> +                     if (unlikely(stream->sreq_max_segs))
> +                             part = netfs_limit_iter(&source, 0, part, 
> stream->sreq_max_segs);
> +
> +                     netfs_stat(&netfs_n_rh_download);
> +                     if (rreq->netfs_ops->prepare_read(subreq) < 0) {
> +                             trace_netfs_sreq(subreq, 
> netfs_sreq_trace_reprep_failed);
> +                             __set_bit(NETFS_SREQ_FAILED, &subreq->flags);
> +                             goto abandon;
> +                     }
> +
> +                     part = umin(len, stream->sreq_max_len);
> +                     subreq->len = subreq->transferred + part;
> +                     subreq->io_iter = source;
> +                     iov_iter_truncate(&subreq->io_iter, part);
> +                     iov_iter_advance(&source, part);
> +
> +                     len -= part;
> +                     start += part;
> +                     if (!len && boundary) {
> +                             __set_bit(NETFS_SREQ_BOUNDARY, &to->flags);
> +                             boundary = false;
> +                     }
> +
> +                     netfs_reissue_read(rreq, subreq);
> +             } while (len);
> +
> +     } while (!list_is_head(next, &stream->subrequests));
>  
>       return;
>  
> -     /* If we hit ENOMEM, fail all remaining subrequests */
> +     /* If we hit an error, fail all remaining incomplete subrequests */
> +abandon_after:
> +     if (list_is_last(&subreq->rreq_link, &stream->subrequests))
> +             return;

This change as commit 1bd9011ee163 ("netfs: Change the read result
collector to only use one work item") in next-20241114 causes a clang
warning:

  fs/netfs/read_retry.c:235:20: error: variable 'subreq' is uninitialized when 
used here [-Werror,-Wuninitialized]
    235 |         if (list_is_last(&subreq->rreq_link, &stream->subrequests))
        |                           ^~~~~~
  fs/netfs/read_retry.c:28:36: note: initialize the variable 'subreq' to 
silence this warning
     28 |         struct netfs_io_subrequest *subreq;
        |                                           ^
        |                                            = NULL

May be a shadowing issue, as adding KCFLAGS=-Wshadow shows:

  fs/netfs/read_retry.c:75:31: error: declaration shadows a local variable 
[-Werror,-Wshadow]
     75 |                 struct netfs_io_subrequest *subreq = NULL, *from, 
*to, *tmp;
        |                                             ^
  fs/netfs/read_retry.c:28:30: note: previous declaration is here
     28 |         struct netfs_io_subrequest *subreq;
        |                                     ^

Cheers,
Nathan

> +     subreq = list_next_entry(subreq, rreq_link);
>  abandon:
> -     list_splice_init(&sublist, &queue);
> -     list_for_each_entry(subreq, &queue, rreq_link) {
> -             if (!subreq->error)
> -                     subreq->error = -ENOMEM;
> -             __clear_bit(NETFS_SREQ_FAILED, &subreq->flags);
> +     list_for_each_entry_from(subreq, &stream->subrequests, rreq_link) {
> +             if (!subreq->error &&
> +                 !test_bit(NETFS_SREQ_FAILED, &subreq->flags) &&
> +                 !test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags))
> +                     continue;
> +             subreq->error = -ENOMEM;
> +             __set_bit(NETFS_SREQ_FAILED, &subreq->flags);
>               __clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
>               __clear_bit(NETFS_SREQ_RETRYING, &subreq->flags);
>       }
> -     spin_lock(&rreq->lock);
> -     list_splice_tail_init(&queue, &rreq->subrequests);
> -     spin_unlock(&rreq->lock);
>  }
>  
>  /*
> @@ -225,14 +253,19 @@ static void netfs_retry_read_subrequests(struct 
> netfs_io_request *rreq)
>   */
>  void netfs_retry_reads(struct netfs_io_request *rreq)
>  {
> -     trace_netfs_rreq(rreq, netfs_rreq_trace_resubmit);
> +     struct netfs_io_subrequest *subreq;
> +     struct netfs_io_stream *stream = &rreq->io_streams[0];
>  
> -     atomic_inc(&rreq->nr_outstanding);
> +     /* Wait for all outstanding I/O to quiesce before performing retries as
> +      * we may need to renegotiate the I/O sizes.
> +      */
> +     list_for_each_entry(subreq, &stream->subrequests, rreq_link) {
> +             wait_on_bit(&subreq->flags, NETFS_SREQ_IN_PROGRESS,
> +                         TASK_UNINTERRUPTIBLE);
> +     }
>  
> +     trace_netfs_rreq(rreq, netfs_rreq_trace_resubmit);
>       netfs_retry_read_subrequests(rreq);
> -
> -     if (atomic_dec_and_test(&rreq->nr_outstanding))
> -             netfs_rreq_terminated(rreq);
>  }
>  
>  /*

Reply via email to