diff options
Diffstat (limited to 'kernel/futex.c')
| -rw-r--r-- | kernel/futex.c | 201 |
1 files changed, 76 insertions, 125 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 438701adce2..6b50a024bca 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
| @@ -114,7 +114,9 @@ struct futex_q { | |||
| 114 | }; | 114 | }; |
| 115 | 115 | ||
| 116 | /* | 116 | /* |
| 117 | * Split the global futex_lock into every hash list lock. | 117 | * Hash buckets are shared by all the futex_keys that hash to the same |
| 118 | * location. Each key may have multiple futex_q structures, one for each task | ||
| 119 | * waiting on a futex. | ||
| 118 | */ | 120 | */ |
| 119 | struct futex_hash_bucket { | 121 | struct futex_hash_bucket { |
| 120 | spinlock_t lock; | 122 | spinlock_t lock; |
| @@ -189,8 +191,7 @@ static void drop_futex_key_refs(union futex_key *key) | |||
| 189 | /** | 191 | /** |
| 190 | * get_futex_key - Get parameters which are the keys for a futex. | 192 | * get_futex_key - Get parameters which are the keys for a futex. |
| 191 | * @uaddr: virtual address of the futex | 193 | * @uaddr: virtual address of the futex |
| 192 | * @shared: NULL for a PROCESS_PRIVATE futex, | 194 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED |
| 193 | * ¤t->mm->mmap_sem for a PROCESS_SHARED futex | ||
| 194 | * @key: address where result is stored. | 195 | * @key: address where result is stored. |
| 195 | * | 196 | * |
| 196 | * Returns a negative error code or 0 | 197 | * Returns a negative error code or 0 |
| @@ -200,9 +201,7 @@ static void drop_futex_key_refs(union futex_key *key) | |||
| 200 | * offset_within_page). For private mappings, it's (uaddr, current->mm). | 201 | * offset_within_page). For private mappings, it's (uaddr, current->mm). |
| 201 | * We can usually work out the index without swapping in the page. | 202 | * We can usually work out the index without swapping in the page. |
| 202 | * | 203 | * |
| 203 | * fshared is NULL for PROCESS_PRIVATE futexes | 204 | * lock_page() might sleep, the caller should not hold a spinlock. |
| 204 | * For other futexes, it points to ¤t->mm->mmap_sem and | ||
| 205 | * caller must have taken the reader lock. but NOT any spinlocks. | ||
| 206 | */ | 205 | */ |
| 207 | static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | 206 | static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) |
| 208 | { | 207 | { |
| @@ -299,41 +298,6 @@ static int get_futex_value_locked(u32 *dest, u32 __user *from) | |||
| 299 | return ret ? -EFAULT : 0; | 298 | return ret ? -EFAULT : 0; |
| 300 | } | 299 | } |
| 301 | 300 | ||
| 302 | /* | ||
| 303 | * Fault handling. | ||
| 304 | */ | ||
| 305 | static int futex_handle_fault(unsigned long address, int attempt) | ||
| 306 | { | ||
| 307 | struct vm_area_struct * vma; | ||
| 308 | struct mm_struct *mm = current->mm; | ||
| 309 | int ret = -EFAULT; | ||
| 310 | |||
| 311 | if (attempt > 2) | ||
| 312 | return ret; | ||
| 313 | |||
| 314 | down_read(&mm->mmap_sem); | ||
| 315 | vma = find_vma(mm, address); | ||
| 316 | if (vma && address >= vma->vm_start && | ||
| 317 | (vma->vm_flags & VM_WRITE)) { | ||
| 318 | int fault; | ||
| 319 | fault = handle_mm_fault(mm, vma, address, 1); | ||
| 320 | if (unlikely((fault & VM_FAULT_ERROR))) { | ||
| 321 | #if 0 | ||
| 322 | /* XXX: let's do this when we verify it is OK */ | ||
| 323 | if (ret & VM_FAULT_OOM) | ||
| 324 | ret = -ENOMEM; | ||
| 325 | #endif | ||
| 326 | } else { | ||
| 327 | ret = 0; | ||
| 328 | if (fault & VM_FAULT_MAJOR) | ||
| 329 | current->maj_flt++; | ||
| 330 | else | ||
| 331 | current->min_flt++; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | up_read(&mm->mmap_sem); | ||
| 335 | return ret; | ||
| 336 | } | ||
| 337 | 301 | ||
| 338 | /* | 302 | /* |
| 339 | * PI code: | 303 | * PI code: |
| @@ -589,10 +553,9 @@ static void wake_futex(struct futex_q *q) | |||
| 589 | * The waiting task can free the futex_q as soon as this is written, | 553 | * The waiting task can free the futex_q as soon as this is written, |
| 590 | * without taking any locks. This must come last. | 554 | * without taking any locks. This must come last. |
| 591 | * | 555 | * |
| 592 | * A memory barrier is required here to prevent the following store | 556 | * A memory barrier is required here to prevent the following store to |
| 593 | * to lock_ptr from getting ahead of the wakeup. Clearing the lock | 557 | * lock_ptr from getting ahead of the wakeup. Clearing the lock at the |
| 594 | * at the end of wake_up_all() does not prevent this store from | 558 | * end of wake_up() does not prevent this store from moving. |
| 595 | * moving. | ||
| 596 | */ | 559 | */ |
| 597 | smp_wmb(); | 560 | smp_wmb(); |
| 598 | q->lock_ptr = NULL; | 561 | q->lock_ptr = NULL; |
| @@ -692,9 +655,16 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2) | |||
| 692 | } | 655 | } |
| 693 | } | 656 | } |
| 694 | 657 | ||
| 658 | static inline void | ||
| 659 | double_unlock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2) | ||
| 660 | { | ||
| 661 | spin_unlock(&hb1->lock); | ||
| 662 | if (hb1 != hb2) | ||
| 663 | spin_unlock(&hb2->lock); | ||
| 664 | } | ||
| 665 | |||
| 695 | /* | 666 | /* |
| 696 | * Wake up all waiters hashed on the physical page that is mapped | 667 | * Wake up waiters matching bitset queued on this futex (uaddr). |
| 697 | * to this virtual address: | ||
| 698 | */ | 668 | */ |
| 699 | static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset) | 669 | static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset) |
| 700 | { | 670 | { |
| @@ -750,9 +720,9 @@ futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2, | |||
| 750 | struct futex_hash_bucket *hb1, *hb2; | 720 | struct futex_hash_bucket *hb1, *hb2; |
| 751 | struct plist_head *head; | 721 | struct plist_head *head; |
| 752 | struct futex_q *this, *next; | 722 | struct futex_q *this, *next; |
| 753 | int ret, op_ret, attempt = 0; | 723 | int ret, op_ret; |
| 754 | 724 | ||
| 755 | retryfull: | 725 | retry: |
| 756 | ret = get_futex_key(uaddr1, fshared, &key1); | 726 | ret = get_futex_key(uaddr1, fshared, &key1); |
| 757 | if (unlikely(ret != 0)) | 727 | if (unlikely(ret != 0)) |
| 758 | goto out; | 728 | goto out; |
| @@ -763,16 +733,13 @@ retryfull: | |||
| 763 | hb1 = hash_futex(&key1); | 733 | hb1 = hash_futex(&key1); |
| 764 | hb2 = hash_futex(&key2); | 734 | hb2 = hash_futex(&key2); |
| 765 | 735 | ||
| 766 | retry: | ||
| 767 | double_lock_hb(hb1, hb2); | 736 | double_lock_hb(hb1, hb2); |
| 768 | 737 | retry_private: | |
| 769 | op_ret = futex_atomic_op_inuser(op, uaddr2); | 738 | op_ret = futex_atomic_op_inuser(op, uaddr2); |
| 770 | if (unlikely(op_ret < 0)) { | 739 | if (unlikely(op_ret < 0)) { |
| 771 | u32 dummy; | 740 | u32 dummy; |
| 772 | 741 | ||
| 773 | spin_unlock(&hb1->lock); | 742 | double_unlock_hb(hb1, hb2); |
| 774 | if (hb1 != hb2) | ||
| 775 | spin_unlock(&hb2->lock); | ||
| 776 | 743 | ||
| 777 | #ifndef CONFIG_MMU | 744 | #ifndef CONFIG_MMU |
| 778 | /* | 745 | /* |
| @@ -788,26 +755,16 @@ retry: | |||
| 788 | goto out_put_keys; | 755 | goto out_put_keys; |
| 789 | } | 756 | } |
| 790 | 757 | ||
| 791 | /* | ||
| 792 | * futex_atomic_op_inuser needs to both read and write | ||
| 793 | * *(int __user *)uaddr2, but we can't modify it | ||
| 794 | * non-atomically. Therefore, if get_user below is not | ||
| 795 | * enough, we need to handle the fault ourselves, while | ||
| 796 | * still holding the mmap_sem. | ||
| 797 | */ | ||
| 798 | if (attempt++) { | ||
| 799 | ret = futex_handle_fault((unsigned long)uaddr2, | ||
| 800 | attempt); | ||
| 801 | if (ret) | ||
| 802 | goto out_put_keys; | ||
| 803 | goto retry; | ||
| 804 | } | ||
| 805 | |||
| 806 | ret = get_user(dummy, uaddr2); | 758 | ret = get_user(dummy, uaddr2); |
| 807 | if (ret) | 759 | if (ret) |
| 808 | return ret; | 760 | goto out_put_keys; |
| 761 | |||
| 762 | if (!fshared) | ||
| 763 | goto retry_private; | ||
| 809 | 764 | ||
| 810 | goto retryfull; | 765 | put_futex_key(fshared, &key2); |
| 766 | put_futex_key(fshared, &key1); | ||
| 767 | goto retry; | ||
| 811 | } | 768 | } |
| 812 | 769 | ||
| 813 | head = &hb1->chain; | 770 | head = &hb1->chain; |
| @@ -834,9 +791,7 @@ retry: | |||
| 834 | ret += op_ret; | 791 | ret += op_ret; |
| 835 | } | 792 | } |
| 836 | 793 | ||
| 837 | spin_unlock(&hb1->lock); | 794 | double_unlock_hb(hb1, hb2); |
| 838 | if (hb1 != hb2) | ||
| 839 | spin_unlock(&hb2->lock); | ||
| 840 | out_put_keys: | 795 | out_put_keys: |
| 841 | put_futex_key(fshared, &key2); | 796 | put_futex_key(fshared, &key2); |
| 842 | out_put_key1: | 797 | out_put_key1: |
| @@ -869,6 +824,7 @@ retry: | |||
| 869 | hb1 = hash_futex(&key1); | 824 | hb1 = hash_futex(&key1); |
| 870 | hb2 = hash_futex(&key2); | 825 | hb2 = hash_futex(&key2); |
| 871 | 826 | ||
| 827 | retry_private: | ||
| 872 | double_lock_hb(hb1, hb2); | 828 | double_lock_hb(hb1, hb2); |
| 873 | 829 | ||
| 874 | if (likely(cmpval != NULL)) { | 830 | if (likely(cmpval != NULL)) { |
| @@ -877,16 +833,18 @@ retry: | |||
| 877 | ret = get_futex_value_locked(&curval, uaddr1); | 833 | ret = get_futex_value_locked(&curval, uaddr1); |
| 878 | 834 | ||
| 879 | |||
