diff mbox

[1/4] btrfs: Refactor loop in btrfs_release_extent_buffer_page

Message ID 1530106705-27186-2-git-send-email-nborisov@suse.com (mailing list archive)
State New, archived
Headers show

Commit Message

Nikolay Borisov June 27, 2018, 1:38 p.m. UTC
The purpose of the function is to free all the pages comprising an
extent buffer. This can be achieved with a simple for loop rather than
the slitghly more involved 'do {} while' construct. So rewrite the
loop using a 'for' construct. Additionally we can never have an
extent_buffer that is 0 pages so remove the check for index == 0. No
functional changes.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/extent_io.c | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

Comments

David Sterba June 29, 2018, 12:35 p.m. UTC | #1
On Wed, Jun 27, 2018 at 04:38:22PM +0300, Nikolay Borisov wrote:
> The purpose of the function is to free all the pages comprising an
> extent buffer. This can be achieved with a simple for loop rather than
> the slitghly more involved 'do {} while' construct. So rewrite the
> loop using a 'for' construct. Additionally we can never have an
> extent_buffer that is 0 pages so remove the check for index == 0. No
> functional changes.

The reversed loop makes sense, the first page is special and used for
locking the whole extent buffer's pages, as can be seen eg.
897ca6e9b4fef86d5dfb6b31 or 4f2de97acee6532b36dd6e99 . Also see current
alloc_extent_buffer for the locking and managing the private bit.

So you can still rewrite it as a for loop but would have to preserve the
logic or provide the reason that it's correct to iterate from 0 to
num_pages. There are some subltle races regarding pages and extents and
their presence in various trees, so I'd rather be careful here.

> Signed-off-by: Nikolay Borisov <nborisov@suse.com>
> ---
>  fs/btrfs/extent_io.c | 13 ++++---------
>  1 file changed, 4 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index cce6087d6880..4180a3b7e725 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -4641,19 +4641,14 @@ int extent_buffer_under_io(struct extent_buffer *eb)
>   */
>  static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
>  {
> -	unsigned long index;
> -	struct page *page;
> +	int i;
>  	int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
>  
>  	BUG_ON(extent_buffer_under_io(eb));
>  
> -	index = num_extent_pages(eb->start, eb->len);
> -	if (index == 0)
> -		return;

This check does seem to be redundant.

> +	for (i = 0; i < num_extent_pages(eb->start, eb->len); i++) {

I think that the num_extent_pages(...) would be better put to a
temporary variable so it's not evaluated each time the loop termination
condition is checked.

> +		struct page *page = eb->pages[i];
>  
> -	do {
> -		index--;
> -		page = eb->pages[index];
>  		if (!page)
>  			continue;
>  		if (mapped)
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nikolay Borisov July 2, 2018, 10:03 a.m. UTC | #2
On 29.06.2018 15:35, David Sterba wrote:
> On Wed, Jun 27, 2018 at 04:38:22PM +0300, Nikolay Borisov wrote:
>> The purpose of the function is to free all the pages comprising an
>> extent buffer. This can be achieved with a simple for loop rather than
>> the slitghly more involved 'do {} while' construct. So rewrite the
>> loop using a 'for' construct. Additionally we can never have an
>> extent_buffer that is 0 pages so remove the check for index == 0. No
>> functional changes.
> 
> The reversed loop makes sense, the first page is special and used for
> locking the whole extent buffer's pages, as can be seen eg.
> 897ca6e9b4fef86d5dfb6b31 or 4f2de97acee6532b36dd6e99 . Also see current
> alloc_extent_buffer for the locking and managing the private bit.

Turns out  page[0] is not special and it's not used for any special
locking whatsoever. In csum_dirty_buffer this page is used so that when
we submit a multipage eb then we only perform csum calculation once, i.e
when the first page (page[0]) is submitted.
btrfs_release_extent_buffer_page OTOH no longer takes an index but just
an EB and iterates all the pages.

The fact that page0 is unlocked last in alloc_extent_buffer seems to be
an artefact of the code refactoring rather than a deliberate behavior.
Furthermore I've been running tests with sequential unlocking (and
complete removal of the SetPageChecked/ClearPageChecked function from
alloc_extent_buffer) of the pages and didn't observe any problems
whatsoever on both 4g and 1g configuration ( the latter is more likely
to trigger premature page eviction hence trigger any lurking races
between alloc_extent_buffer and btree_releasepage. I will be sending a
patch after I do a bit more testing but so far the results are good.

> 
> So you can still rewrite it as a for loop but would have to preserve the
> logic or provide the reason that it's correct to iterate from 0 to
> num_pages. There are some subltle races regarding pages and extents and
> their presence in various trees, so I'd rather be careful here.
> 
>> Signed-off-by: Nikolay Borisov <nborisov@suse.com>
>> ---
>>  fs/btrfs/extent_io.c | 13 ++++---------
>>  1 file changed, 4 insertions(+), 9 deletions(-)
>>
>> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
>> index cce6087d6880..4180a3b7e725 100644
>> --- a/fs/btrfs/extent_io.c
>> +++ b/fs/btrfs/extent_io.c
>> @@ -4641,19 +4641,14 @@ int extent_buffer_under_io(struct extent_buffer *eb)
>>   */
>>  static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
>>  {
>> -	unsigned long index;
>> -	struct page *page;
>> +	int i;
>>  	int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
>>  
>>  	BUG_ON(extent_buffer_under_io(eb));
>>  
>> -	index = num_extent_pages(eb->start, eb->len);
>> -	if (index == 0)
>> -		return;
> 
> This check does seem to be redundant.
> 
>> +	for (i = 0; i < num_extent_pages(eb->start, eb->len); i++) {
> 
> I think that the num_extent_pages(...) would be better put to a
> temporary variable so it's not evaluated each time the loop termination
> condition is checked.
> 
>> +		struct page *page = eb->pages[i];
>>  
>> -	do {
>> -		index--;
>> -		page = eb->pages[index];
>>  		if (!page)
>>  			continue;
>>  		if (mapped)
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Sterba July 19, 2018, 3:19 p.m. UTC | #3
On Mon, Jul 02, 2018 at 01:03:41PM +0300, Nikolay Borisov wrote:
> 
> 
> On 29.06.2018 15:35, David Sterba wrote:
> > On Wed, Jun 27, 2018 at 04:38:22PM +0300, Nikolay Borisov wrote:
> >> The purpose of the function is to free all the pages comprising an
> >> extent buffer. This can be achieved with a simple for loop rather than
> >> the slitghly more involved 'do {} while' construct. So rewrite the
> >> loop using a 'for' construct. Additionally we can never have an
> >> extent_buffer that is 0 pages so remove the check for index == 0. No
> >> functional changes.
> > 
> > The reversed loop makes sense, the first page is special and used for
> > locking the whole extent buffer's pages, as can be seen eg.
> > 897ca6e9b4fef86d5dfb6b31 or 4f2de97acee6532b36dd6e99 . Also see current
> > alloc_extent_buffer for the locking and managing the private bit.
> 
> Turns out  page[0] is not special and it's not used for any special
> locking whatsoever. In csum_dirty_buffer this page is used so that when
> we submit a multipage eb then we only perform csum calculation once, i.e
> when the first page (page[0]) is submitted.
> btrfs_release_extent_buffer_page OTOH no longer takes an index but just
> an EB and iterates all the pages.
> 
> The fact that page0 is unlocked last in alloc_extent_buffer seems to be
> an artefact of the code refactoring rather than a deliberate behavior.
> Furthermore I've been running tests with sequential unlocking (and
> complete removal of the SetPageChecked/ClearPageChecked function from
> alloc_extent_buffer) of the pages and didn't observe any problems
> whatsoever on both 4g and 1g configuration ( the latter is more likely
> to trigger premature page eviction hence trigger any lurking races
> between alloc_extent_buffer and btree_releasepage. I will be sending a
> patch after I do a bit more testing but so far the results are good.

With more patch archeology I agree that the page zero is not used in any
sense that would require the reversed loop. Patch added to misc-next.
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index cce6087d6880..4180a3b7e725 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -4641,19 +4641,14 @@  int extent_buffer_under_io(struct extent_buffer *eb)
  */
 static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
 {
-	unsigned long index;
-	struct page *page;
+	int i;
 	int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
 
 	BUG_ON(extent_buffer_under_io(eb));
 
-	index = num_extent_pages(eb->start, eb->len);
-	if (index == 0)
-		return;
+	for (i = 0; i < num_extent_pages(eb->start, eb->len); i++) {
+		struct page *page = eb->pages[i];
 
-	do {
-		index--;
-		page = eb->pages[index];
 		if (!page)
 			continue;
 		if (mapped)
@@ -4685,7 +4680,7 @@  static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
 
 		/* One for when we allocated the page */
 		put_page(page);
-	} while (index != 0);
+	}
 }
 
 /*