Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: introdule compound_head_by_tail()

Currently, in put_compound_page(), we have

======
if (likely(!PageTail(page))) { <------ (1)
if (put_page_testzero(page)) {
/*
¦* By the time all refcounts have been released
¦* split_huge_page cannot run anymore from under us.
¦*/
if (PageHead(page))
__put_compound_page(page);
else
__put_single_page(page);
}
return;
}

/* __split_huge_page_refcount can run under us */
page_head = compound_head(page); <------------ (2)
======

if at (1) , we fail the check, this means page is *likely* a tail page.

Then at (2), as compoud_head(page) is inlined, it is :

======
static inline struct page *compound_head(struct page *page)
{
if (unlikely(PageTail(page))) { <----------- (3)
struct page *head = page->first_page;

smp_rmb();
if (likely(PageTail(page)))
return head;
}
return page;
}
======

here, the (3) unlikely in the case is a negative hint, because it is
*likely* a tail page. So the check (3) in this case is not good, so I
introduce a helper for this case.

So this patch introduces compound_head_by_tail() which deals with a
possible tail page(though it could be spilt by a racy thread), and make
compound_head() a wrapper on it.

This patch has no functional change, and it reduces the object
size slightly:
text data bss dec hex filename
11003 1328 16 12347 303b mm/swap.o.orig
10971 1328 16 12315 301b mm/swap.o.patched

I've ran "perf top -e branch-miss" to observe branch-miss in this case.
As Michael points out, it's a slow path, so only very few times this case
happens. But I grep'ed the code base, and found there still are some
other call sites could be benifited from this helper. And given that it
only bloating up the source by only 5 lines, but with a reduced object
size. I still believe this helper deserves to exsit.

Signed-off-by: Jianyu Zhan <nasa4836@gmail.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Jiang Liu <liuj97@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Wanpeng Li <liwanp@linux.vnet.ibm.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Jianyu Zhan and committed by
Linus Torvalds
d2ee40ea 4bd3e8f7

+18 -13
+17 -12
include/linux/mm.h
··· 407 407 #endif 408 408 } 409 409 410 + static inline struct page *compound_head_by_tail(struct page *tail) 411 + { 412 + struct page *head = tail->first_page; 413 + 414 + /* 415 + * page->first_page may be a dangling pointer to an old 416 + * compound page, so recheck that it is still a tail 417 + * page before returning. 418 + */ 419 + smp_rmb(); 420 + if (likely(PageTail(tail))) 421 + return head; 422 + return tail; 423 + } 424 + 410 425 static inline struct page *compound_head(struct page *page) 411 426 { 412 - if (unlikely(PageTail(page))) { 413 - struct page *head = page->first_page; 414 - 415 - /* 416 - * page->first_page may be a dangling pointer to an old 417 - * compound page, so recheck that it is still a tail 418 - * page before returning. 419 - */ 420 - smp_rmb(); 421 - if (likely(PageTail(page))) 422 - return head; 423 - } 427 + if (unlikely(PageTail(page))) 428 + return compound_head_by_tail(page); 424 429 return page; 425 430 } 426 431
+1 -1
mm/swap.c
··· 253 253 * Case 3 is possible, as we may race with 254 254 * __split_huge_page_refcount tearing down a THP page. 255 255 */ 256 - page_head = compound_head(page); 256 + page_head = compound_head_by_tail(page); 257 257 if (!__compound_tail_refcounted(page_head)) 258 258 put_unrefcounted_compound_page(page_head, page); 259 259 else