[PATCH 04 of 31] update futex compound knowledge

Andrea Arcangeli
2010-01-25T17:30:15Z

Futex code is smarter than most other gup_fast O_DIRECT code and knows about
the compound internals. However now doing a put_page(head_page) will not
release the pin on the tail page taken by gup-fast, leading to all sort of
refcounting bugchecks. Getting a stable head_page is a little tricky.

Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
---

diff --git a/kernel/futex.c b/kernel/futex.c
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -218,7 +218,7 @@ get_futex_key(u32 __user *uaddr, int fsh
{
unsigned long address = (unsigned long)uaddr;
struct mm_struct *mm = current->mm;
- struct page *page;
+ struct page *page, *page_head;
int err;

/*
@@ -250,10 +250,32 @@ again:
if (err < 0)
return err;

- page = compound_head(page);
- lock_page(page);
- if (!page->mapping) {
- unlock_page(page);
+ page_head = page;
+ if (unlikely(PageTail(page))) {
+ put_page(page);
+ /* serialize against __split_huge_page_splitting() */
+ local_irq_disable();
+ if (likely(__get_user_pages_fast(address, 1, 1, &page) == 1)) {
+ page_head = compound_head(page);
+ local_irq_enable();
+ } else {
+ local_irq_enable();
+ goto again;
+ }
+ }
+
+ lock_page(page_head);
+ if (unlikely(page_head != page)) {
+ compound_lock(page_head);
+ if (unlikely(!PageTail(page))) {
+ compound_unlock(page_head);
+ unlock_page(page_head);
+ put_page(page);
+ goto again;
+ }
+ }
+ if (!page_head->mapping) {
+ unlock_page(page_head);
put_page(page);
goto again;
}
@@ -265,19 +287,21 @@ again:
* it's a read-only handle, it's expected that futexes attach to
* the object not the particular process.
*/
- if (PageAnon(page)) {
+ if (PageAnon(page_head)) {
key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
key->private.mm = mm;
key->private.address = address;
} else {
key->both.offset |= FUT_OFF_INODE; /* inode-based key */
- key->shared.inode = page->mapping->host;
- key->shared.pgoff = page->index;
+ key->shared.inode = page_head->mapping->host;
+ key->shared.pgoff = page_head->index;
}

get_futex_key_refs(key);

- unlock_page(page);
+ if (unlikely(PageTail(page)))
+ compound_unlock(page_head);
+ unlock_page(page_head);
put_page(page);
return 0;