diff options
author | Darren Hart <dvhltc@us.ibm.com> | 2009-03-12 03:56:13 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-12 06:20:57 -0400 |
commit | e4dc5b7a36a49eff97050894cf1b3a9a02523717 (patch) | |
tree | 91a0aceb21b1869385507c2631bf97fbf180697b /kernel/futex.c | |
parent | e8f6386c01a5699c115bdad10271a24076364c97 (diff) |
futex: clean up fault logic
Impact: cleanup
Older versions of the futex code held the mmap_sem which had to
be dropped in order to call get_user(), so a two-pronged fault
handling mechanism was employed to handle faults of the atomic
operations. The mmap_sem is no longer held, so get_user()
should be adequate. This patch greatly simplifies the logic and
improves legibility.
Build and boot tested on a 4 way Intel x86_64 workstation.
Passes basic pthread_mutex and PI tests out of
ltp/testcases/realtime.
Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
LKML-Reference: <20090312075612.9856.48612.stgit@Aeon>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/futex.c')
-rw-r--r-- | kernel/futex.c | 126 |
1 files changed, 36 insertions, 90 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index c980a556f82c..9c97f67d298e 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -298,41 +298,6 @@ static int get_futex_value_locked(u32 *dest, u32 __user *from) | |||
298 | return ret ? -EFAULT : 0; | 298 | return ret ? -EFAULT : 0; |
299 | } | 299 | } |
300 | 300 | ||
301 | /* | ||
302 | * Fault handling. | ||
303 | */ | ||
304 | static int futex_handle_fault(unsigned long address, int attempt) | ||
305 | { | ||
306 | struct vm_area_struct * vma; | ||
307 | struct mm_struct *mm = current->mm; | ||
308 | int ret = -EFAULT; | ||
309 | |||
310 | if (attempt > 2) | ||
311 | return ret; | ||
312 | |||
313 | down_read(&mm->mmap_sem); | ||
314 | vma = find_vma(mm, address); | ||
315 | if (vma && address >= vma->vm_start && | ||
316 | (vma->vm_flags & VM_WRITE)) { | ||
317 | int fault; | ||
318 | fault = handle_mm_fault(mm, vma, address, 1); | ||
319 | if (unlikely((fault & VM_FAULT_ERROR))) { | ||
320 | #if 0 | ||
321 | /* XXX: let's do this when we verify it is OK */ | ||
322 | if (ret & VM_FAULT_OOM) | ||
323 | ret = -ENOMEM; | ||
324 | #endif | ||
325 | } else { | ||
326 | ret = 0; | ||
327 | if (fault & VM_FAULT_MAJOR) | ||
328 | current->maj_flt++; | ||
329 | else | ||
330 | current->min_flt++; | ||
331 | } | ||
332 | } | ||
333 | up_read(&mm->mmap_sem); | ||
334 | return ret; | ||
335 | } | ||
336 | 301 | ||
337 | /* | 302 | /* |
338 | * PI code: | 303 | * PI code: |
@@ -760,9 +725,9 @@ futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2, | |||
760 | struct futex_hash_bucket *hb1, *hb2; | 725 | struct futex_hash_bucket *hb1, *hb2; |
761 | struct plist_head *head; | 726 | struct plist_head *head; |
762 | struct futex_q *this, *next; | 727 | struct futex_q *this, *next; |
763 | int ret, op_ret, attempt = 0; | 728 | int ret, op_ret; |
764 | 729 | ||
765 | retryfull: | 730 | retry: |
766 | ret = get_futex_key(uaddr1, fshared, &key1); | 731 | ret = get_futex_key(uaddr1, fshared, &key1); |
767 | if (unlikely(ret != 0)) | 732 | if (unlikely(ret != 0)) |
768 | goto out; | 733 | goto out; |
@@ -773,9 +738,8 @@ retryfull: | |||
773 | hb1 = hash_futex(&key1); | 738 | hb1 = hash_futex(&key1); |
774 | hb2 = hash_futex(&key2); | 739 | hb2 = hash_futex(&key2); |
775 | 740 | ||
776 | retry: | ||
777 | double_lock_hb(hb1, hb2); | 741 | double_lock_hb(hb1, hb2); |
778 | 742 | retry_private: | |
779 | op_ret = futex_atomic_op_inuser(op, uaddr2); | 743 | op_ret = futex_atomic_op_inuser(op, uaddr2); |
780 | if (unlikely(op_ret < 0)) { | 744 | if (unlikely(op_ret < 0)) { |
781 | u32 dummy; | 745 | u32 dummy; |
@@ -796,28 +760,16 @@ retry: | |||
796 | goto out_put_keys; | 760 | goto out_put_keys; |
797 | } | 761 | } |
798 | 762 | ||
799 | /* | ||
800 | * futex_atomic_op_inuser needs to both read and write | ||
801 | * *(int __user *)uaddr2, but we can't modify it | ||
802 | * non-atomically. Therefore, if get_user below is not | ||
803 | * enough, we need to handle the fault ourselves, while | ||
804 | * still holding the mmap_sem. | ||
805 | */ | ||
806 | if (attempt++) { | ||
807 | ret = futex_handle_fault((unsigned long)uaddr2, | ||
808 | attempt); | ||
809 | if (ret) | ||
810 | goto out_put_keys; | ||
811 | goto retry; | ||
812 | } | ||
813 | |||
814 | ret = get_user(dummy, uaddr2); | 763 | ret = get_user(dummy, uaddr2); |
815 | if (ret) | 764 | if (ret) |
816 | goto out_put_keys; | 765 | goto out_put_keys; |
817 | 766 | ||
767 | if (!fshared) | ||
768 | goto retry_private; | ||
769 | |||
818 | put_futex_key(fshared, &key2); | 770 | put_futex_key(fshared, &key2); |
819 | put_futex_key(fshared, &key1); | 771 | put_futex_key(fshared, &key1); |
820 | goto retryfull; | 772 | goto retry; |
821 | } | 773 | } |
822 | 774 | ||
823 | head = &hb1->chain; | 775 | head = &hb1->chain; |
@@ -877,6 +829,7 @@ retry: | |||
877 | hb1 = hash_futex(&key1); | 829 | hb1 = hash_futex(&key1); |
878 | hb2 = hash_futex(&key2); | 830 | hb2 = hash_futex(&key2); |
879 | 831 | ||
832 | retry_private: | ||
880 | double_lock_hb(hb1, hb2); | 833 | double_lock_hb(hb1, hb2); |
881 | 834 | ||
882 | if (likely(cmpval != NULL)) { | 835 | if (likely(cmpval != NULL)) { |
@@ -887,15 +840,16 @@ retry: | |||
887 | if (unlikely(ret)) { | 840 | if (unlikely(ret)) { |
888 | double_unlock_hb(hb1, hb2); | 841 | double_unlock_hb(hb1, hb2); |
889 | 842 | ||
890 | put_futex_key(fshared, &key2); | ||
891 | put_futex_key(fshared, &key1); | ||
892 | |||
893 | ret = get_user(curval, uaddr1); | 843 | ret = get_user(curval, uaddr1); |
844 | if (ret) | ||
845 | goto out_put_keys; | ||
894 | 846 | ||
895 | if (!ret) | 847 | if (!fshared) |
896 | goto retry; | 848 | goto retry_private; |
897 | 849 | ||
898 | goto out_put_keys; | 850 | put_futex_key(fshared, &key2); |
851 | put_futex_key(fshared, &key1); | ||
852 | goto retry; | ||
899 | } | 853 | } |
900 | if (curval != *cmpval) { | 854 | if (curval != *cmpval) { |
901 | ret = -EAGAIN; | 855 | ret = -EAGAIN; |
@@ -1070,7 +1024,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, | |||
1070 | struct futex_pi_state *pi_state = q->pi_state; | 1024 | struct futex_pi_state *pi_state = q->pi_state; |
1071 | struct task_struct *oldowner = pi_state->owner; | 1025 | struct task_struct *oldowner = pi_state->owner; |
1072 | u32 uval, curval, newval; | 1026 | u32 uval, curval, newval; |
1073 | int ret, attempt = 0; | 1027 | int ret; |
1074 | 1028 | ||
1075 | /* Owner died? */ | 1029 | /* Owner died? */ |
1076 | if (!pi_state->owner) | 1030 | if (!pi_state->owner) |
@@ -1141,7 +1095,7 @@ retry: | |||
1141 | handle_fault: | 1095 | handle_fault: |
1142 | spin_unlock(q->lock_ptr); | 1096 | spin_unlock(q->lock_ptr); |
1143 | 1097 | ||
1144 | ret = futex_handle_fault((unsigned long)uaddr, attempt++); | 1098 | ret = get_user(uval, uaddr); |
1145 | 1099 | ||
1146 | spin_lock(q->lock_ptr); | 1100 | spin_lock(q->lock_ptr); |
1147 | 1101 | ||
@@ -1190,6 +1144,7 @@ retry: | |||
1190 | if (unlikely(ret != 0)) | 1144 | if (unlikely(ret != 0)) |
1191 | goto out; | 1145 | goto out; |
1192 | 1146 | ||
1147 | retry_private: | ||
1193 | hb = queue_lock(&q); | 1148 | hb = queue_lock(&q); |
1194 | 1149 | ||
1195 | /* | 1150 | /* |
@@ -1216,13 +1171,16 @@ retry: | |||
1216 | 1171 | ||
1217 | if (unlikely(ret)) { | 1172 | if (unlikely(ret)) { |
1218 | queue_unlock(&q, hb); | 1173 | queue_unlock(&q, hb); |
1219 | put_futex_key(fshared, &q.key); | ||
1220 | 1174 | ||
1221 | ret = get_user(uval, uaddr); | 1175 | ret = get_user(uval, uaddr); |
1176 | if (ret) | ||
1177 | goto out_put_key; | ||
1222 | 1178 | ||
1223 | if (!ret) | 1179 | if (!fshared) |
1224 | goto retry; | 1180 | goto retry_private; |
1225 | goto out; | 1181 | |
1182 | put_futex_key(fshared, &q.key); | ||
1183 | goto retry; | ||
1226 | } | 1184 | } |
1227 | ret = -EWOULDBLOCK; | 1185 | ret = -EWOULDBLOCK; |
1228 | if (unlikely(uval != val)) { | 1186 | if (unlikely(uval != val)) { |
@@ -1356,7 +1314,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared, | |||
1356 | struct futex_hash_bucket *hb; | 1314 | struct futex_hash_bucket *hb; |
1357 | u32 uval, newval, curval; | 1315 | u32 uval, newval, curval; |
1358 | struct futex_q q; | 1316 | struct futex_q q; |
1359 | int ret, lock_taken, ownerdied = 0, attempt = 0; | 1317 | int ret, lock_taken, ownerdied = 0; |
1360 | 1318 | ||
1361 | if (refill_pi_state_cache()) | 1319 | if (refill_pi_state_cache()) |
1362 | return -ENOMEM; | 1320 | return -ENOMEM; |
@@ -1376,7 +1334,7 @@ retry: | |||
1376 | if (unlikely(ret != 0)) | 1334 | if (unlikely(ret != 0)) |
1377 | goto out; | 1335 | goto out; |
1378 | 1336 | ||
1379 | retry_unlocked: | 1337 | retry_private: |
1380 | hb = queue_lock(&q); | 1338 | hb = queue_lock(&q); |
1381 | 1339 | ||
1382 | retry_locked: | 1340 | retry_locked: |
@@ -1601,18 +1559,15 @@ uaddr_faulted: | |||
1601 | */ | 1559 | */ |
1602 | queue_unlock(&q, hb); | 1560 | queue_unlock(&q, hb); |
1603 | 1561 | ||
1604 | if (attempt++) { | ||
1605 | ret = futex_handle_fault((unsigned long)uaddr, attempt); | ||
1606 | if (ret) | ||
1607 | goto out_put_key; | ||
1608 | goto retry_unlocked; | ||
1609 | } | ||
1610 | |||
1611 | ret = get_user(uval, uaddr); | 1562 | ret = get_user(uval, uaddr); |
1612 | if (!ret) | 1563 | if (ret) |
1613 | goto retry_unlocked; | 1564 | goto out_put_key; |
1614 | 1565 | ||
1615 | goto out_put_key; | 1566 | if (!fshared) |
1567 | goto retry_private; | ||
1568 | |||
1569 | put_futex_key(fshared, &q.key); | ||
1570 | goto retry; | ||
1616 | } | 1571 | } |
1617 | 1572 | ||
1618 | 1573 | ||
@@ -1628,7 +1583,7 @@ static int futex_unlock_pi(u32 __user *uaddr, int fshared) | |||
1628 | u32 uval; | 1583 | u32 uval; |
1629 | struct plist_head *head; | 1584 | struct plist_head *head; |
1630 | union futex_key key = FUTEX_KEY_INIT; | 1585 | union futex_key key = FUTEX_KEY_INIT; |
1631 | int ret, attempt = 0; | 1586 | int ret; |
1632 | 1587 | ||
1633 | retry: | 1588 | retry: |
1634 | if (get_user(uval, uaddr)) | 1589 | if (get_user(uval, uaddr)) |
@@ -1644,7 +1599,6 @@ retry: | |||
1644 | goto out; | 1599 | goto out; |
1645 | 1600 | ||
1646 | hb = hash_futex(&key); | 1601 | hb = hash_futex(&key); |
1647 | retry_unlocked: | ||
1648 | spin_lock(&hb->lock); | 1602 | spin_lock(&hb->lock); |
1649 | 1603 | ||
1650 | /* | 1604 | /* |
@@ -1709,17 +1663,9 @@ pi_faulted: | |||
1709 | * we have to drop the mmap_sem in order to call get_user(). | 1663 | * we have to drop the mmap_sem in order to call get_user(). |
1710 | */ | 1664 | */ |
1711 | spin_unlock(&hb->lock); | 1665 | spin_unlock(&hb->lock); |
1712 | 1666 | put_futex_key(fshared, &key); | |
1713 | if (attempt++) { | ||
1714 | ret = futex_handle_fault((unsigned long)uaddr, attempt); | ||
1715 | if (ret) | ||
1716 | goto out; | ||
1717 | uval = 0; | ||
1718 | goto retry_unlocked; | ||
1719 | } | ||
1720 | 1667 | ||
1721 | ret = get_user(uval, uaddr); | 1668 | ret = get_user(uval, uaddr); |
1722 | put_futex_key(fshared, &key); | ||
1723 | if (!ret) | 1669 | if (!ret) |
1724 | goto retry; | 1670 | goto retry; |
1725 | 1671 | ||