Unverified Commit b72e591f authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Christian Brauner
Browse files

fs/buffer: remove batching from async read



block_read_full_folio() currently puts all !uptodate buffers into
an array allocated on the stack, then iterates over it twice, first
locking the buffers and then submitting them for read.  We want to
remove this array because it occupies too much stack space on
configurations with a larger PAGE_SIZE (eg 512 bytes with 8 byte
pointers and a 64KiB PAGE_SIZE).

We cannot simply submit buffer heads as we find them as the completion
handler needs to be able to tell when all reads are finished, so it can
end the folio read.  So we keep one buffer in reserve (using the 'prev'
variable) until the end of the function.

Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatar"Matthew Wilcox (Oracle)" <willy@infradead.org>
Signed-off-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
Link: https://lore.kernel.org/r/20250221223823.1680616-3-mcgrof@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 753aadeb
Loading
Loading
Loading
Loading
+21 −30
Original line number Diff line number Diff line
@@ -2361,9 +2361,8 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
{
	struct inode *inode = folio->mapping->host;
	sector_t iblock, lblock;
	struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
	struct buffer_head *bh, *head, *prev = NULL;
	size_t blocksize;
	int nr, i;
	int fully_mapped = 1;
	bool page_error = false;
	loff_t limit = i_size_read(inode);
@@ -2380,7 +2379,6 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
	iblock = div_u64(folio_pos(folio), blocksize);
	lblock = div_u64(limit + blocksize - 1, blocksize);
	bh = head;
	nr = 0;

	do {
		if (buffer_uptodate(bh))
@@ -2410,40 +2408,33 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
			if (buffer_uptodate(bh))
				continue;
		}
		arr[nr++] = bh;

		lock_buffer(bh);
		if (buffer_uptodate(bh)) {
			unlock_buffer(bh);
			continue;
		}

		mark_buffer_async_read(bh);
		if (prev)
			submit_bh(REQ_OP_READ, prev);
		prev = bh;
	} while (iblock++, (bh = bh->b_this_page) != head);

	if (fully_mapped)
		folio_set_mappedtodisk(folio);

	if (!nr) {
	/*
		 * All buffers are uptodate or get_block() returned an
		 * error when trying to map them - we can finish the read.
	 * All buffers are uptodate or get_block() returned an error
	 * when trying to map them - we must finish the read because
	 * end_buffer_async_read() will never be called on any buffer
	 * in this folio.
	 */
	if (prev)
		submit_bh(REQ_OP_READ, prev);
	else
		folio_end_read(folio, !page_error);
		return 0;
	}

	/* Stage two: lock the buffers */
	for (i = 0; i < nr; i++) {
		bh = arr[i];
		lock_buffer(bh);
		mark_buffer_async_read(bh);
	}

	/*
	 * Stage 3: start the IO.  Check for uptodateness
	 * inside the buffer lock in case another process reading
	 * the underlying blockdev brought it uptodate (the sct fix).
	 */
	for (i = 0; i < nr; i++) {
		bh = arr[i];
		if (buffer_uptodate(bh))
			end_buffer_async_read(bh, 1);
		else
			submit_bh(REQ_OP_READ, bh);
	}
	return 0;
}
EXPORT_SYMBOL(block_read_full_folio);