Merge branches 'core-fixes-for-linus', 'x86-fixes-for-linus', 'timers-fixes-for-linus...
[linux-2.6.git] / kernel / futex.c
index 3019b92..b766d28 100644 (file)
@@ -233,7 +233,7 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
 {
        unsigned long address = (unsigned long)uaddr;
        struct mm_struct *mm = current->mm;
-       struct page *page;
+       struct page *page, *page_head;
        int err;
 
        /*
@@ -265,11 +265,46 @@ again:
        if (err < 0)
                return err;
 
-       page = compound_head(page);
-       lock_page(page);
-       if (!page->mapping) {
-               unlock_page(page);
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       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);
+                       /*
+                        * page_head is valid pointer but we must pin
+                        * it before taking the PG_lock and/or
+                        * PG_compound_lock. The moment we re-enable
+                        * irqs __split_huge_page_splitting() can
+                        * return and the head page can be freed from
+                        * under us. We can't take the PG_lock and/or
+                        * PG_compound_lock on a page that could be
+                        * freed from under us.
+                        */
+                       if (page != page_head) {
+                               get_page(page_head);
+                               put_page(page);
+                       }
+                       local_irq_enable();
+               } else {
+                       local_irq_enable();
+                       goto again;
+               }
+       }
+#else
+       page_head = compound_head(page);
+       if (page != page_head) {
+               get_page(page_head);
+               put_page(page);
+       }
+#endif
+
+       lock_page(page_head);
+       if (!page_head->mapping) {
+               unlock_page(page_head);
+               put_page(page_head);
                goto again;
        }
 
@@ -280,20 +315,20 @@ 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);
-       put_page(page);
+       unlock_page(page_head);
+       put_page(page_head);
        return 0;
 }
 
@@ -791,10 +826,9 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this)
        new_owner = rt_mutex_next_owner(&pi_state->pi_mutex);
 
        /*
-        * This happens when we have stolen the lock and the original
-        * pending owner did not enqueue itself back on the rt_mutex.
-        * Thats not a tragedy. We know that way, that a lock waiter
-        * is on the fly. We make the futex_q waiter the pending owner.
+        * It is possible that the next waiter (the one that brought
+        * this owner to the kernel) timed out and is no longer
+        * waiting on the lock.
         */
        if (!new_owner)
                new_owner = this->task;