linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: Kent Overstreet <kent.overstreet@gmail.com>
Cc: linux-kernel@vger.kernel.org, viro@zeniv.linux.org.uk,
	linux-mm@kvack.org, linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH v2 2/2] fs: generic_file_buffered_read() now uses find_get_pages_contig
Date: Wed, 17 Jun 2020 18:05:58 -0700	[thread overview]
Message-ID: <20200617180558.9722e7337cbe3b88c4767126@linux-foundation.org> (raw)
In-Reply-To: <20200610013642.4171512-2-kent.overstreet@gmail.com>

On Tue,  9 Jun 2020 21:36:42 -0400 Kent Overstreet <kent.overstreet@gmail.com> wrote:

> Convert generic_file_buffered_read() to get pages to read from in
> batches, and then copy data to userspace from many pages at once - in
> particular, we now don't touch any cachelines that might be contended
> while we're in the loop to copy data to userspace.
> 
> This is is a performance improvement on workloads that do buffered reads
> with large blocksizes, and a very large performance improvement if that
> file is also being accessed concurrently by different threads.
> 
> On smaller reads (512 bytes), there's a very small performance
> improvement (1%, within the margin of error).
> 

checkpatch goes fairly crazy over this one, mostly legitimate.

> @@ -2255,6 +2194,79 @@ generic_file_buffered_read_no_cached_page(struct kiocb *iocb,
>  	return generic_file_buffered_read_readpage(filp, mapping, page);
>  }
>  
> +static int generic_file_buffered_read_get_pages(struct kiocb *iocb,
> +						struct iov_iter *iter,
> +						struct page **pages,
> +						unsigned nr)
> +{
> +	struct file *filp = iocb->ki_filp;
> +	struct address_space *mapping = filp->f_mapping;
> +	struct file_ra_state *ra = &filp->f_ra;
> +	pgoff_t index = iocb->ki_pos >> PAGE_SHIFT;
> +	pgoff_t last_index = (iocb->ki_pos + iter->count + PAGE_SIZE-1) >> PAGE_SHIFT;
> +	int i, j, ret, err = 0;
> +
> +	nr = min_t(unsigned long, last_index - index, nr);
> +find_page:
> +	if (fatal_signal_pending(current))
> +		return -EINTR;
> +
> +	ret = find_get_pages_contig(mapping, index, nr, pages);
> +	if (ret)
> +		goto got_pages;
> +
> +	if (iocb->ki_flags & IOCB_NOWAIT)
> +		return -EAGAIN;
> +
> +	page_cache_sync_readahead(mapping, ra, filp, index, last_index - index);
> +
> +	ret = find_get_pages_contig(mapping, index, nr, pages);
> +	if (ret)
> +		goto got_pages;
> +
> +	pages[0] = generic_file_buffered_read_no_cached_page(iocb, iter);
> +	err = PTR_ERR_OR_ZERO(pages[0]);
> +	ret = !IS_ERR_OR_NULL(pages[0]);

what?

> +got_pages:
> +	for (i = 0; i < ret; i++) {

Comparing i with ret here just hurts my brain.  Two lines ago ret was a
boolean, now it's a scalar.

> +		struct page *page = pages[i];
> +		pgoff_t pg_index = index +i;
> +		loff_t pg_pos = max(iocb->ki_pos,
> +				    (loff_t) pg_index << PAGE_SHIFT);

hm.  I guess we can't use max_t here because we need to cast the
pgoff_t before the << to avoid overflows on 32-bit.  Perhaps this could
be cleaned up by using additional suitably typed and named locals.

> +		loff_t pg_count = iocb->ki_pos + iter->count - pg_pos;
> +
> +		if (PageReadahead(page))
> +			page_cache_async_readahead(mapping, ra, filp, page,
> +					pg_index, last_index - pg_index);
> +
> +		if (!PageUptodate(page)) {
> +			if (iocb->ki_flags & IOCB_NOWAIT) {
> +				for (j = i; j < ret; j++)
> +					put_page(pages[j]);
> +				ret = i;
> +				err = -EAGAIN;
> +				break;
> +			}
> +
> +			page = generic_file_buffered_read_pagenotuptodate(filp,
> +						iter, page, pg_pos, pg_count);
> +			if (IS_ERR_OR_NULL(page)) {
> +				for (j = i + 1; j < ret; j++)
> +					put_page(pages[j]);
> +				ret = i;
> +				err = PTR_ERR_OR_ZERO(page);
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (likely(ret))
> +		return ret;
> +	if (err)
> +		return err;
> +	goto find_page;
> +}
> +
>  /**
>   * generic_file_buffered_read - generic file read routine
>   * @iocb:	the iocb to read
> @@ -2275,86 +2287,108 @@ static ssize_t generic_file_buffered_read(struct kiocb *iocb,
>  		struct iov_iter *iter, ssize_t written)
>  {
>  	struct file *filp = iocb->ki_filp;
> +	struct file_ra_state *ra = &filp->f_ra;
>  	struct address_space *mapping = filp->f_mapping;
>  	struct inode *inode = mapping->host;
> -	struct file_ra_state *ra = &filp->f_ra;
>  	size_t orig_count = iov_iter_count(iter);
> -	pgoff_t last_index;
> -	int error = 0;
> +	struct page *page_array[8], **pages;
> +	unsigned nr_pages = ARRAY_SIZE(page_array);
> +	unsigned read_nr_pages = ((iocb->ki_pos + iter->count + PAGE_SIZE-1) >> PAGE_SHIFT) -
> +		(iocb->ki_pos >> PAGE_SHIFT);
> +	int i, pg_nr, error = 0;
> +	bool writably_mapped;
> +	loff_t isize, end_offset;
>  
>  	if (unlikely(iocb->ki_pos >= inode->i_sb->s_maxbytes))
>  		return 0;
>  	iov_iter_truncate(iter, inode->i_sb->s_maxbytes);
>  
> -	last_index = (iocb->ki_pos + iter->count + PAGE_SIZE-1) >> PAGE_SHIFT;
> -
> -	for (;;) {
> -		pgoff_t index = iocb->ki_pos >> PAGE_SHIFT;
> -		struct page *page;
> +	if (read_nr_pages > nr_pages &&
> +	    (pages = kmalloc_array(read_nr_pages, sizeof(void *), GFP_KERNEL)))

I agree with checkpatch!

> +		nr_pages = read_nr_pages;
> +	else
> +		pages = page_array;
>  
> +	do {
>  		cond_resched();
>
> ...
>

Please, can we make all this code nice to read?


  reply	other threads:[~2020-06-18  1:06 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-10  0:10 [PATCH 0/2] generic_file_buffered_read() refactoring & optimization Kent Overstreet
2020-06-10  0:10 ` [PATCH 1/2] fs: Break generic_file_buffered_read up into multiple functions Kent Overstreet
2020-06-10  0:10 ` [PATCH 2/2] fs: generic_file_buffered_read() now uses find_get_pages_contig Kent Overstreet
2020-06-10  0:47   ` Matthew Wilcox
2020-06-10  1:08     ` Kent Overstreet
2020-06-10  1:38   ` Matthew Wilcox
2020-06-10  1:46     ` Kent Overstreet
2020-06-10  1:36 ` [PATCH v2 1/2] fs: Break generic_file_buffered_read up into multiple functions Kent Overstreet
2020-06-10  1:36 ` [PATCH v2 2/2] fs: generic_file_buffered_read() now uses find_get_pages_contig Kent Overstreet
2020-06-18  1:05   ` Andrew Morton [this message]
2020-06-19  3:20     ` [PATCH v3 0/2] generic_file_buffered_read() refactoring & optimization Kent Overstreet
2020-06-19 12:59       ` Christoph Hellwig
2020-06-19 18:44         ` Kent Overstreet
2020-06-19  3:20     ` [PATCH v3 1/2] fs: Break generic_file_buffered_read up into multiple functions Kent Overstreet
2020-06-19  3:20     ` [PATCH v3 2/2] fs: generic_file_buffered_read() now uses find_get_pages_contig Kent Overstreet
2020-06-30  0:12 ` Fixup patch for [PATCH 0/2] generic_file_buffered_read() refactoring & optimization Kent Overstreet
2020-10-25 21:29 [PATCH v2 0/2] generic_file_buffered_read() improvements Kent Overstreet
2020-10-25 21:29 ` [PATCH v2 2/2] fs: generic_file_buffered_read() now uses find_get_pages_contig Kent Overstreet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200617180558.9722e7337cbe3b88c4767126@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=kent.overstreet@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).