aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhang Yi <wetpzy@gmail.com>2013-06-25 09:19:31 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-06-25 17:11:19 -0400
commit13d60f4b6ab5b702dc8d2ee20999f98a93728aec (patch)
tree06e7e68536e0f2d035f3e02f3c019c9abb728e33
parent0c1061733aa0303e6536c0bc7f86d68f5eb55446 (diff)
futex: Take hugepages into account when generating futex_key
The futex_keys of process shared futexes are generated from the page offset, the mapping host and the mapping index of the futex user space address. This should result in an unique identifier for each futex. Though this is not true when futexes are located in different subpages of an hugepage. The reason is, that the mapping index for all those futexes evaluates to the index of the base page of the hugetlbfs mapping. So a futex at offset 0 of the hugepage mapping and another one at offset PAGE_SIZE of the same hugepage mapping have identical futex_keys. This happens because the futex code blindly uses page->index. Steps to reproduce the bug: 1. Map a file from hugetlbfs. Initialize pthread_mutex1 at offset 0 and pthread_mutex2 at offset PAGE_SIZE of the hugetlbfs mapping. The mutexes must be initialized as PTHREAD_PROCESS_SHARED because PTHREAD_PROCESS_PRIVATE mutexes are not affected by this issue as their keys solely depend on the user space address. 2. Lock mutex1 and mutex2 3. Create thread1 and in the thread function lock mutex1, which results in thread1 blocking on the locked mutex1. 4. Create thread2 and in the thread function lock mutex2, which results in thread2 blocking on the locked mutex2. 5. Unlock mutex2. Despite the fact that mutex2 got unlocked, thread2 still blocks on mutex2 because the futex_key points to mutex1. To solve this issue we need to take the normal page index of the page which contains the futex into account, if the futex is in an hugetlbfs mapping. In other words, we calculate the normal page mapping index of the subpage in the hugetlbfs mapping. Mappings which are not based on hugetlbfs are not affected and still use page->index. Thanks to Mel Gorman who provided a patch for adding proper evaluation functions to the hugetlbfs code to avoid exposing hugetlbfs specific details to the futex code. [ tglx: Massaged changelog ] Signed-off-by: Zhang Yi <zhang.yi20@zte.com.cn> Reviewed-by: Jiang Biao <jiang.biao2@zte.com.cn> Tested-by: Ma Chenggong <ma.chenggong@zte.com.cn> Reviewed-by: 'Mel Gorman' <mgorman@suse.de> Acked-by: 'Darren Hart' <dvhart@linux.intel.com> Cc: 'Peter Zijlstra' <peterz@infradead.org> Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/000101ce71a6%24a83c5880%24f8b50980%24@com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--include/linux/hugetlb.h16
-rw-r--r--kernel/futex.c3
-rw-r--r--mm/hugetlb.c17
3 files changed, 35 insertions, 1 deletions
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 6b4890fa57e7..feaf0c7fb7d8 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -358,6 +358,17 @@ static inline int hstate_index(struct hstate *h)
358 return h - hstates; 358 return h - hstates;
359} 359}
360 360
361pgoff_t __basepage_index(struct page *page);
362
363/* Return page->index in PAGE_SIZE units */
364static inline pgoff_t basepage_index(struct page *page)
365{
366 if (!PageCompound(page))
367 return page->index;
368
369 return __basepage_index(page);
370}
371
361#else /* CONFIG_HUGETLB_PAGE */ 372#else /* CONFIG_HUGETLB_PAGE */
362struct hstate {}; 373struct hstate {};
363#define alloc_huge_page_node(h, nid) NULL 374#define alloc_huge_page_node(h, nid) NULL
@@ -378,6 +389,11 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
378} 389}
379#define hstate_index_to_shift(index) 0 390#define hstate_index_to_shift(index) 0
380#define hstate_index(h) 0 391#define hstate_index(h) 0
392
393static inline pgoff_t basepage_index(struct page *page)
394{
395 return page->index;
396}
381#endif /* CONFIG_HUGETLB_PAGE */ 397#endif /* CONFIG_HUGETLB_PAGE */
382 398
383#endif /* _LINUX_HUGETLB_H */ 399#endif /* _LINUX_HUGETLB_H */
diff --git a/kernel/futex.c b/kernel/futex.c
index b26dcfc02c94..49dacfb45745 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -61,6 +61,7 @@
61#include <linux/nsproxy.h> 61#include <linux/nsproxy.h>
62#include <linux/ptrace.h> 62#include <linux/ptrace.h>
63#include <linux/sched/rt.h> 63#include <linux/sched/rt.h>
64#include <linux/hugetlb.h>
64 65
65#include <asm/futex.h> 66#include <asm/futex.h>
66 67
@@ -365,7 +366,7 @@ again:
365 } else { 366 } else {
366 key->both.offset |= FUT_OFF_INODE; /* inode-based key */ 367 key->both.offset |= FUT_OFF_INODE; /* inode-based key */
367 key->shared.inode = page_head->mapping->host; 368 key->shared.inode = page_head->mapping->host;
368 key->shared.pgoff = page_head->index; 369 key->shared.pgoff = basepage_index(page);
369 } 370 }
370 371
371 get_futex_key_refs(key); 372 get_futex_key_refs(key);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f8feeeca6686..aea87ced4268 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -690,6 +690,23 @@ int PageHuge(struct page *page)
690} 690}
691EXPORT_SYMBOL_GPL(PageHuge); 691EXPORT_SYMBOL_GPL(PageHuge);
692 692
693pgoff_t __basepage_index(struct page *page)
694{
695 struct page *page_head = compound_head(page);
696 pgoff_t index = page_index(page_head);
697 unsigned long compound_idx;
698
699 if (!PageHuge(page_head))
700 return page_index(page);
701
702 if (compound_order(page_head) >= MAX_ORDER)
703 compound_idx = page_to_pfn(page) - page_to_pfn(page_head);
704 else
705 compound_idx = page - page_head;
706
707 return (index << compound_order(page_head)) + compound_idx;
708}
709
693static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid) 710static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
694{ 711{
695 struct page *page; 712 struct page *page;