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
next prev parent 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).