diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 6b5818d6de32..b4732af5abea 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -146,10 +146,15 @@ static inline struct page *compound_head(struct page *page) unsigned long head = READ_ONCE(page->compound_head); if (unlikely(head & 1)) - return (struct page *) (head - 1); + return (struct page *) (head & ~3UL); return page; } +static __always_inline bool PageLastTail(struct page *page) +{ + return (READ_ONCE(page->compound_head) & 3) == 3; +} + static __always_inline int PageTail(struct page *page) { return READ_ONCE(page->compound_head) & 1; @@ -488,9 +493,13 @@ static inline void set_page_writeback_keepwrite(struct page *page) __PAGEFLAG(Head, head, PF_ANY) CLEARPAGEFLAG(Head, head, PF_ANY) -static __always_inline void set_compound_head(struct page *page, struct page *head) +static __always_inline +void set_compound_head(struct page *page, struct page *head, bool last) { - WRITE_ONCE(page->compound_head, (unsigned long)head + 1); + unsigned long flags = 1; /* PageTail */ + if (last) + flags |= 2; /* PageLastTail */ + WRITE_ONCE(page->compound_head, (unsigned long)head | flags); } static __always_inline void clear_compound_head(struct page *page) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index e5828875f7bb..b915af9a924d 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1314,7 +1314,7 @@ static void prep_compound_gigantic_page(struct page *page, unsigned int order) */ __ClearPageReserved(p); set_page_count(p, 0); - set_compound_head(p, page); + set_compound_head(p, page, i == (nr_pages - 1)); } atomic_set(compound_mapcount_ptr(page), -1); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f3d603cef2c0..857c6b27ab14 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -591,7 +591,7 @@ void prep_compound_page(struct page *page, unsigned int order) struct page *p = page + i; set_page_count(p, 0); p->mapping = TAIL_MAPPING; - set_compound_head(p, page); + set_compound_head(p, page, i == (nr_pages - 1)); } atomic_set(compound_mapcount_ptr(page), -1); }