diff options
author | Nick Piggin <npiggin@suse.de> | 2007-05-08 03:26:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:03 -0400 |
commit | 72c1bbf308c75a136803d2d76d0e18258be14c7a (patch) | |
tree | 647fe566121ba004590e5b639ae791ec4af7955d | |
parent | 9adef58b1d4fbb58d7daed931b6790c5a3b7543a (diff) |
futex: restartable futex_wait
LTP test sigaction_16_24 fails, because it expects sem_wait to be restarted
if SA_RESTART is set. sem_wait is implemented with futex_wait, that
currently doesn't support being restarted. Ulrich confirms that the call
should be restartable.
Implement a restart_block method to handle the relative timeout, and allow
restarts.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Ulrich Drepper <drepper@redhat.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Acked-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/futex.c | 56 |
1 files changed, 51 insertions, 5 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 7ae2f50641ed..600bc9d801f2 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -980,12 +980,15 @@ static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) | |||
980 | drop_futex_key_refs(&q->key); | 980 | drop_futex_key_refs(&q->key); |
981 | } | 981 | } |
982 | 982 | ||
983 | static int futex_wait(u32 __user *uaddr, u32 val, unsigned long time) | 983 | static long futex_wait_restart(struct restart_block *restart); |
984 | static int futex_wait_abstime(u32 __user *uaddr, u32 val, | ||
985 | int timed, unsigned long abs_time) | ||
984 | { | 986 | { |
985 | struct task_struct *curr = current; | 987 | struct task_struct *curr = current; |
986 | DECLARE_WAITQUEUE(wait, curr); | 988 | DECLARE_WAITQUEUE(wait, curr); |
987 | struct futex_hash_bucket *hb; | 989 | struct futex_hash_bucket *hb; |
988 | struct futex_q q; | 990 | struct futex_q q; |
991 | unsigned long time_left = 0; | ||
989 | u32 uval; | 992 | u32 uval; |
990 | int ret; | 993 | int ret; |
991 | 994 | ||
@@ -1065,8 +1068,21 @@ static int futex_wait(u32 __user *uaddr, u32 val, unsigned long time) | |||
1065 | * !list_empty() is safe here without any lock. | 1068 | * !list_empty() is safe here without any lock. |
1066 | * q.lock_ptr != 0 is not safe, because of ordering against wakeup. | 1069 | * q.lock_ptr != 0 is not safe, because of ordering against wakeup. |
1067 | */ | 1070 | */ |
1068 | if (likely(!list_empty(&q.list))) | 1071 | time_left = 0; |
1069 | time = schedule_timeout(time); | 1072 | if (likely(!list_empty(&q.list))) { |
1073 | unsigned long rel_time; | ||
1074 | |||
1075 | if (timed) { | ||
1076 | unsigned long now = jiffies; | ||
1077 | if (time_after(now, abs_time)) | ||
1078 | rel_time = 0; | ||
1079 | else | ||
1080 | rel_time = abs_time - now; | ||
1081 | } else | ||
1082 | rel_time = MAX_SCHEDULE_TIMEOUT; | ||
1083 | |||
1084 | time_left = schedule_timeout(rel_time); | ||
1085 | } | ||
1070 | __set_current_state(TASK_RUNNING); | 1086 | __set_current_state(TASK_RUNNING); |
1071 | 1087 | ||
1072 | /* | 1088 | /* |
@@ -1077,13 +1093,25 @@ static int futex_wait(u32 __user *uaddr, u32 val, unsigned long time) | |||
1077 | /* If we were woken (and unqueued), we succeeded, whatever. */ | 1093 | /* If we were woken (and unqueued), we succeeded, whatever. */ |
1078 | if (!unqueue_me(&q)) | 1094 | if (!unqueue_me(&q)) |
1079 | return 0; | 1095 | return 0; |
1080 | if (time == 0) | 1096 | if (time_left == 0) |
1081 | return -ETIMEDOUT; | 1097 | return -ETIMEDOUT; |
1098 | |||
1082 | /* | 1099 | /* |
1083 | * We expect signal_pending(current), but another thread may | 1100 | * We expect signal_pending(current), but another thread may |
1084 | * have handled it for us already. | 1101 | * have handled it for us already. |
1085 | */ | 1102 | */ |
1086 | return -EINTR; | 1103 | if (time_left == MAX_SCHEDULE_TIMEOUT) |
1104 | return -ERESTARTSYS; | ||
1105 | else { | ||
1106 | struct restart_block *restart; | ||
1107 | restart = ¤t_thread_info()->restart_block; | ||
1108 | restart->fn = futex_wait_restart; | ||
1109 | restart->arg0 = (unsigned long)uaddr; | ||
1110 | restart->arg1 = (unsigned long)val; | ||
1111 | restart->arg2 = (unsigned long)timed; | ||
1112 | restart->arg3 = abs_time; | ||
1113 | return -ERESTART_RESTARTBLOCK; | ||
1114 | } | ||
1087 | 1115 | ||
1088 | out_unlock_release_sem: | 1116 | out_unlock_release_sem: |
1089 | queue_unlock(&q, hb); | 1117 | queue_unlock(&q, hb); |
@@ -1093,6 +1121,24 @@ static int futex_wait(u32 __user *uaddr, u32 val, unsigned long time) | |||
1093 | return ret; | 1121 | return ret; |
1094 | } | 1122 | } |
1095 | 1123 | ||
1124 | static int futex_wait(u32 __user *uaddr, u32 val, unsigned long rel_time) | ||
1125 | { | ||
1126 | int timed = (rel_time != MAX_SCHEDULE_TIMEOUT); | ||
1127 | return futex_wait_abstime(uaddr, val, timed, jiffies+rel_time); | ||
1128 | } | ||
1129 | |||
1130 | static long futex_wait_restart(struct restart_block *restart) | ||
1131 | { | ||
1132 | u32 __user *uaddr = (u32 __user *)restart->arg0; | ||
1133 | u32 val = (u32)restart->arg1; | ||
1134 | int timed = (int)restart->arg2; | ||
1135 | unsigned long abs_time = restart->arg3; | ||
1136 | |||
1137 | restart->fn = do_no_restart_syscall; | ||
1138 | return (long)futex_wait_abstime(uaddr, val, timed, abs_time); | ||
1139 | } | ||
1140 | |||
1141 | |||
1096 | /* | 1142 | /* |
1097 | * Userspace tried a 0 -> TID atomic transition of the futex value | 1143 | * Userspace tried a 0 -> TID atomic transition of the futex value |
1098 | * and failed. The kernel side here does the whole locking operation: | 1144 | * and failed. The kernel side here does the whole locking operation: |