Skip to content

Commit c8b90d4

Browse files
dhowellsbrauner
authored andcommitted
netfs: Fix non-contiguous donation between completed reads
When a read subrequest finishes, if it doesn't have sufficient coverage to complete the folio(s) covering either side of it, it will donate the excess coverage to the adjacent subrequests on either side, offloading responsibility for unlocking the folio(s) covered to them. Now, preference is given to donating down to a lower file offset over donating up because that check is done first - but there's no check that the lower subreq is actually contiguous, and so we can end up donating incorrectly. The scenario seen[1] is that an 8MiB readahead request spanning four 2MiB folios is split into eight 1MiB subreqs (numbered 1 through 8). These terminate in the order 1,6,2,5,3,7,4,8. What happens is: - 1 donates to 2 - 6 donates to 5 - 2 completes, unlocking the first folio (with 1). - 5 completes, unlocking the third folio (with 6). - 3 donates to 4 - 7 donates to 4 incorrectly - 4 completes, unlocking the second folio (with 3), but can't use the excess from 7. - 8 donates to 4, also incorrectly. Fix this by preventing downward donation if the subreqs are not contiguous (in the example above, 7 donates to 4 across the gap left by 5 and 6). Reported-by: Shyam Prasad N <[email protected]> Closes: https://lore.kernel.org/r/CANT5p=qBwjBm-D8soFVVtswGEfmMtQXVW83=TNfUtvyHeFQZBA@mail.gmail.com/ Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected]/ [1] Link: https://lore.kernel.org/r/[email protected] cc: Steve French <[email protected]> cc: Paulo Alcantara <[email protected]> cc: Jeff Layton <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 973b710 commit c8b90d4

File tree

1 file changed

+5
-4
lines changed

1 file changed

+5
-4
lines changed

fs/netfs/read_collect.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,17 @@ static bool netfs_consume_read_data(struct netfs_io_subrequest *subreq, bool was
247247

248248
/* Deal with the trickiest case: that this subreq is in the middle of a
249249
* folio, not touching either edge, but finishes first. In such a
250-
* case, we donate to the previous subreq, if there is one, so that the
251-
* donation is only handled when that completes - and remove this
252-
* subreq from the list.
250+
* case, we donate to the previous subreq, if there is one and if it is
251+
* contiguous, so that the donation is only handled when that completes
252+
* - and remove this subreq from the list.
253253
*
254254
* If the previous subreq finished first, we will have acquired their
255255
* donation and should be able to unlock folios and/or donate nextwards.
256256
*/
257257
if (!subreq->consumed &&
258258
!prev_donated &&
259-
!list_is_first(&subreq->rreq_link, &rreq->subrequests)) {
259+
!list_is_first(&subreq->rreq_link, &rreq->subrequests) &&
260+
subreq->start == prev->start + prev->len) {
260261
prev = list_prev_entry(subreq, rreq_link);
261262
WRITE_ONCE(prev->next_donated, prev->next_donated + subreq->len);
262263
subreq->start += subreq->len;

0 commit comments

Comments
 (0)