linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: Lukas Czerner <lczerner@redhat.com>
Cc: Hugh Dickins <hughd@google.com>,
	linux-kernel@vger.kernel.org, xfs@oss.sgi.com,
	linux-mm@kvack.org, linux-fsdevel@vger.kernel.org,
	linux-ext4@vger.kernel.org
Subject: Re: [PATCH 10/18] mm: teach truncate_inode_pages_range() to handle non page aligned ranges
Date: Fri, 1 Feb 2013 15:15:02 -0800	[thread overview]
Message-ID: <20130201151502.59398b29.akpm@linux-foundation.org> (raw)
In-Reply-To: <1359715424-32318-11-git-send-email-lczerner@redhat.com>

On Fri,  1 Feb 2013 11:43:36 +0100
Lukas Czerner <lczerner@redhat.com> wrote:

> This commit changes truncate_inode_pages_range() so it can handle non
> page aligned regions of the truncate. Currently we can hit BUG_ON when
> the end of the range is not page aligned, but we can handle unaligned
> start of the range.
> 
> Being able to handle non page aligned regions of the page can help file
> system punch_hole implementations and save some work, because once we're
> holding the page we might as well deal with it right away.
> 
> In previous commits we've changed ->invalidatepage() prototype to accept
> 'length' argument to be able to specify range to invalidate. No we can
> use that new ability in truncate_inode_pages_range().

The change seems sensible.

> This was based on the code provided by Hugh Dickins

Despite this ;)

> changes to make use of do_invalidatepage_range().
>
> ...
>
>  void truncate_inode_pages_range(struct address_space *mapping,
>  				loff_t lstart, loff_t lend)
>  {
> -	const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
> -	const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
> +	pgoff_t start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
> +	pgoff_t end = (lend + 1) >> PAGE_CACHE_SHIFT;
> +	unsigned int partial_start = lstart & (PAGE_CACHE_SIZE - 1);
> +	unsigned int partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1);
>  	struct pagevec pvec;
>  	pgoff_t index;
> -	pgoff_t end;
>  	int i;

This is starting to get pretty hairy.  Some of these "end" variables
are inclusive and some are exclusive.

Can we improve things?  We can drop all this tiresome
intialisation-at-declaration-site stuff and do:

	pgoff_t start;			/* inclusive */
	pgoff_t end;			/* exclusive */
	unsigned int partial_start;	/* inclusive */
	unsigned int partial_end;	/* exclusive */
	struct pagevec pvec;
	pgoff_t index;
	int i;

	start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
	end = (lend + 1) >> PAGE_CACHE_SHIFT;
	partial_start = lstart & (PAGE_CACHE_SIZE - 1);
	partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1);

And lo, I see that the "inclusive" thing only applies to incoming arg
`lend'.  I seem to recall that being my handiwork and somehow I seem to
not have documented the reason: it was so that we can pass
lend=0xffffffff into truncate_inode_pages_range) to indicate "end of
file".

Your code handles this in a rather nasty fashion.  It permits the above
overflow to occur then later fixes it up with an explicit test for -1. 
And it then sets `end' (which is a pgoff_t!) to -1.

I guess this works, but let's make it clearer, with something like:

	if (lend == -1) {
		/*
		 * Nice explanation goes here
		 */
		end = -1;
	} else {
		end = (lend + 1) >> PAGE_CACHE_SHIFT;
	}


>  	cleancache_invalidate_inode(mapping);
>  	if (mapping->nrpages == 0)
>  		return;
>  
> -	BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1));
> -	end = (lend >> PAGE_CACHE_SHIFT);
> +	if (lend == -1)
> +		end = -1;	/* unsigned, so actually very big */
>  
>  	pagevec_init(&pvec, 0);
>  	index = start;
> -	while (index <= end && pagevec_lookup(&pvec, mapping, index,
> -			min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
> +	while (index < end && pagevec_lookup(&pvec, mapping, index,
> +			min(end - index, (pgoff_t)PAGEVEC_SIZE))) {

Here, my brain burst.  You've effectively added 1 to (end - index).  Is
that correct?

>  		mem_cgroup_uncharge_start();
>  		for (i = 0; i < pagevec_count(&pvec); i++) {
>  			struct page *page = pvec.pages[i];
>  
>  			/* We rely upon deletion not changing page->index */
>  			index = page->index;
> -			if (index > end)
> +			if (index >= end)

hm.  This change implies that the patch changed `end' from inclusive to
exclusive.  But the patch didn't do that.

>  				break;
>  
>  			if (!trylock_page(page))
> @@ -250,27 +247,51 @@ void truncate_inode_pages_range(struct address_space *mapping,
>  		index++;
>  	}
>  
> -	if (partial) {
> +	if (partial_start) {
>  		struct page *page = find_lock_page(mapping, start - 1);
>  		if (page) {
> +			unsigned int top = PAGE_CACHE_SIZE;
> +			if (start > end) {

How can this be true?

> +				top = partial_end;
> +				partial_end = 0;
> +			}
> +			wait_on_page_writeback(page);
> +			zero_user_segment(page, partial_start, top);
> +			cleancache_invalidate_page(mapping, page);
> +			if (page_has_private(page))
> +				do_invalidatepage(page, partial_start,
> +						  top - partial_start);
> +			unlock_page(page);
> +			page_cache_release(page);
> +		}
> +	}
> +	if (partial_end) {
> +		struct page *page = find_lock_page(mapping, end);
> +		if (page) {
>  			wait_on_page_writeback(page);
> -			truncate_partial_page(page, partial);
> +			zero_user_segment(page, 0, partial_end);
> +			cleancache_invalidate_page(mapping, page);
> +			if (page_has_private(page))
> +				do_invalidatepage(page, 0,
> +						  partial_end);
>  			unlock_page(page);
>  			page_cache_release(page);
>  		}
>  	}
> +	if (start >= end)
> +		return;

Again, how can start be greater than end??

I suspect a lot of the confustion and churn in here is due to `end'
being kinda-exclusive.  If `lend' was 4094 then `end' is zero.  But if
`lend' was 4095' then `end' is 1.  So even though `end' refers to the same
page, it has a different value!

Would the code be simpler and clearer if we were to make `end' "pgoff_t
of the last-affected page", and document it as such?


_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

  reply	other threads:[~2013-02-01 23:15 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-01 10:43 [RFC] mm: change invalidatepage prototype to accept length Lukas Czerner
2013-02-01 10:43 ` [PATCH 01/18] " Lukas Czerner
2013-02-01 10:43 ` [PATCH 02/18] jbd2: change jbd2_journal_invalidatepage " Lukas Czerner
2013-02-01 10:43 ` [PATCH 03/18] ext4: use ->invalidatepage() length argument Lukas Czerner
2013-02-01 10:43 ` [PATCH 04/18] jbd: change journal_invalidatepage() to accept length Lukas Czerner
2013-02-01 10:43 ` [PATCH 05/18] xfs: use ->invalidatepage() length argument Lukas Czerner
2013-02-01 10:43 ` [PATCH 06/18] ocfs2: " Lukas Czerner
2013-02-01 10:43 ` [PATCH 07/18] ceph: " Lukas Czerner
2013-02-01 10:43 ` [PATCH 08/18] gfs2: " Lukas Czerner
2013-02-01 10:43 ` [PATCH 09/18] reiserfs: " Lukas Czerner
2013-02-01 10:43 ` [PATCH 10/18] mm: teach truncate_inode_pages_range() to handle non page aligned ranges Lukas Czerner
2013-02-01 23:15   ` Andrew Morton [this message]
2013-02-04 14:51     ` Lukáš Czerner
2013-02-04 20:51       ` Andrew Morton
2013-02-05  7:14         ` Lukáš Czerner
2013-02-01 10:43 ` [PATCH 11/18] Revert "ext4: remove no longer used functions in inode.c" Lukas Czerner
2013-02-01 10:43 ` [PATCH 12/18] Revert "ext4: fix fsx truncate failure" Lukas Czerner
2013-02-01 10:43 ` [PATCH 13/18] ext4: use ext4_zero_partial_blocks in punch_hole Lukas Czerner
2013-02-01 10:43 ` [PATCH 14/18] ext4: remove unused discard_partial_page_buffers Lukas Czerner
2013-02-01 10:43 ` [PATCH 15/18] ext4: remove unused code from ext4_remove_blocks() Lukas Czerner
2013-02-01 10:43 ` [PATCH 16/18] ext4: update ext4_ext_remove_space trace point Lukas Czerner
2013-02-01 10:43 ` [PATCH 17/18] ext4: make punch hole code path work with bigalloc Lukas Czerner
2013-02-01 10:43 ` [PATCH 18/18] ext4: Allow punch hole with bigalloc enabled Lukas Czerner

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=20130201151502.59398b29.akpm@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=hughd@google.com \
    --cc=lczerner@redhat.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=xfs@oss.sgi.com \
    /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).