Skip to content

Commit fe19bd3

Browse files
Hugh Dickinstorvalds
authored andcommitted
mm, futex: fix shared futex pgoff on shmem huge page
If more than one futex is placed on a shmem huge page, it can happen that waking the second wakes the first instead, and leaves the second waiting: the key's shared.pgoff is wrong. When 3.11 commit 13d60f4 ("futex: Take hugepages into account when generating futex_key"), the only shared huge pages came from hugetlbfs, and the code added to deal with its exceptional page->index was put into hugetlb source. Then that was missed when 4.8 added shmem huge pages. page_to_pgoff() is what others use for this nowadays: except that, as currently written, it gives the right answer on hugetlbfs head, but nonsense on hugetlbfs tails. Fix that by calling hugetlbfs-specific hugetlb_basepage_index() on PageHuge tails as well as on head. Yes, it's unconventional to declare hugetlb_basepage_index() there in pagemap.h, rather than in hugetlb.h; but I do not expect anything but page_to_pgoff() ever to need it. [[email protected]: give hugetlb_basepage_index() prototype the correct scope] Link: https://lkml.kernel.org/r/[email protected] Fixes: 800d8c6 ("shmem: add huge pages support") Reported-by: Neel Natu <[email protected]> Signed-off-by: Hugh Dickins <[email protected]> Reviewed-by: Matthew Wilcox (Oracle) <[email protected]> Acked-by: Thomas Gleixner <[email protected]> Cc: "Kirill A. Shutemov" <[email protected]> Cc: Zhang Yi <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Mike Kravetz <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Darren Hart <[email protected]> Cc: Davidlohr Bueso <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5fa5434 commit fe19bd3

File tree

4 files changed

+9
-28
lines changed

4 files changed

+9
-28
lines changed

include/linux/hugetlb.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -741,17 +741,6 @@ static inline int hstate_index(struct hstate *h)
741741
return h - hstates;
742742
}
743743

744-
pgoff_t __basepage_index(struct page *page);
745-
746-
/* Return page->index in PAGE_SIZE units */
747-
static inline pgoff_t basepage_index(struct page *page)
748-
{
749-
if (!PageCompound(page))
750-
return page->index;
751-
752-
return __basepage_index(page);
753-
}
754-
755744
extern int dissolve_free_huge_page(struct page *page);
756745
extern int dissolve_free_huge_pages(unsigned long start_pfn,
757746
unsigned long end_pfn);
@@ -988,11 +977,6 @@ static inline int hstate_index(struct hstate *h)
988977
return 0;
989978
}
990979

991-
static inline pgoff_t basepage_index(struct page *page)
992-
{
993-
return page->index;
994-
}
995-
996980
static inline int dissolve_free_huge_page(struct page *page)
997981
{
998982
return 0;

include/linux/pagemap.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ static inline struct page *read_mapping_page(struct address_space *mapping,
516516
}
517517

518518
/*
519-
* Get index of the page with in radix-tree
519+
* Get index of the page within radix-tree (but not for hugetlb pages).
520520
* (TODO: remove once hugetlb pages will have ->index in PAGE_SIZE)
521521
*/
522522
static inline pgoff_t page_to_index(struct page *page)
@@ -535,15 +535,16 @@ static inline pgoff_t page_to_index(struct page *page)
535535
return pgoff;
536536
}
537537

538+
extern pgoff_t hugetlb_basepage_index(struct page *page);
539+
538540
/*
539-
* Get the offset in PAGE_SIZE.
540-
* (TODO: hugepage should have ->index in PAGE_SIZE)
541+
* Get the offset in PAGE_SIZE (even for hugetlb pages).
542+
* (TODO: hugetlb pages should have ->index in PAGE_SIZE)
541543
*/
542544
static inline pgoff_t page_to_pgoff(struct page *page)
543545
{
544-
if (unlikely(PageHeadHuge(page)))
545-
return page->index << compound_order(page);
546-
546+
if (unlikely(PageHuge(page)))
547+
return hugetlb_basepage_index(page);
547548
return page_to_index(page);
548549
}
549550

kernel/futex.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
#include <linux/jhash.h>
3636
#include <linux/pagemap.h>
3737
#include <linux/syscalls.h>
38-
#include <linux/hugetlb.h>
3938
#include <linux/freezer.h>
4039
#include <linux/memblock.h>
4140
#include <linux/fault-inject.h>
@@ -650,7 +649,7 @@ static int get_futex_key(u32 __user *uaddr, bool fshared, union futex_key *key,
650649

651650
key->both.offset |= FUT_OFF_INODE; /* inode-based key */
652651
key->shared.i_seq = get_inode_sequence_number(inode);
653-
key->shared.pgoff = basepage_index(tail);
652+
key->shared.pgoff = page_to_pgoff(tail);
654653
rcu_read_unlock();
655654
}
656655

mm/hugetlb.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,15 +1588,12 @@ struct address_space *hugetlb_page_mapping_lock_write(struct page *hpage)
15881588
return NULL;
15891589
}
15901590

1591-
pgoff_t __basepage_index(struct page *page)
1591+
pgoff_t hugetlb_basepage_index(struct page *page)
15921592
{
15931593
struct page *page_head = compound_head(page);
15941594
pgoff_t index = page_index(page_head);
15951595
unsigned long compound_idx;
15961596

1597-
if (!PageHuge(page_head))
1598-
return page_index(page);
1599-
16001597
if (compound_order(page_head) >= MAX_ORDER)
16011598
compound_idx = page_to_pfn(page) - page_to_pfn(page_head);
16021599
else

0 commit comments

Comments
 (0)