diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2006-01-19 05:42:49 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-01-19 05:42:49 -0500 |
commit | 2d7d5f05111a9d913131a2764d8b20157f8f758d (patch) | |
tree | 792deb7a3b9f72894d16affff1569a15b35e428b /arch/sparc/kernel/signal.c | |
parent | f7111ceb5266750db2a1d193b98fb6a3d9b5a56a (diff) |
[SPARC]: Add support for *at(), ppoll, and pselect syscalls.
This also includes by necessity _TIF_RESTORE_SIGMASK support,
which actually resulted in a lot of cleanups.
The sparc signal handling code is quite a mess and I should
clean it up some day.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/signal.c')
-rw-r--r-- | arch/sparc/kernel/signal.c | 117 |
1 files changed, 31 insertions, 86 deletions
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 5f34d7dc2b89..0748d8147bbf 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c | |||
@@ -35,9 +35,6 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr, | |||
35 | void *fpqueue, unsigned long *fpqdepth); | 35 | void *fpqueue, unsigned long *fpqdepth); |
36 | extern void fpload(unsigned long *fpregs, unsigned long *fsr); | 36 | extern void fpload(unsigned long *fpregs, unsigned long *fsr); |
37 | 37 | ||
38 | asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, | ||
39 | unsigned long orig_o0, int restart_syscall); | ||
40 | |||
41 | /* Signal frames: the original one (compatible with SunOS): | 38 | /* Signal frames: the original one (compatible with SunOS): |
42 | * | 39 | * |
43 | * Set up a signal frame... Make the stack look the way SunOS | 40 | * Set up a signal frame... Make the stack look the way SunOS |
@@ -95,98 +92,30 @@ struct rt_signal_frame { | |||
95 | #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) | 92 | #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) |
96 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) | 93 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) |
97 | 94 | ||
98 | /* | 95 | static int _sigpause_common(old_sigset_t set) |
99 | * atomically swap in the new signal mask, and wait for a signal. | ||
100 | * This is really tricky on the Sparc, watch out... | ||
101 | */ | ||
102 | asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs) | ||
103 | { | 96 | { |
104 | sigset_t saveset; | ||
105 | |||
106 | set &= _BLOCKABLE; | 97 | set &= _BLOCKABLE; |
107 | spin_lock_irq(¤t->sighand->siglock); | 98 | spin_lock_irq(¤t->sighand->siglock); |
108 | saveset = current->blocked; | 99 | current->saved_sigmask = current->blocked; |
109 | siginitset(¤t->blocked, set); | 100 | siginitset(¤t->blocked, set); |
110 | recalc_sigpending(); | 101 | recalc_sigpending(); |
111 | spin_unlock_irq(¤t->sighand->siglock); | 102 | spin_unlock_irq(¤t->sighand->siglock); |
112 | 103 | ||
113 | regs->pc = regs->npc; | 104 | current->state = TASK_INTERRUPTIBLE; |
114 | regs->npc += 4; | 105 | schedule(); |
115 | 106 | set_thread_flag(TIF_RESTORE_SIGMASK); | |
116 | /* Condition codes and return value where set here for sigpause, | ||
117 | * and so got used by setup_frame, which again causes sigreturn() | ||
118 | * to return -EINTR. | ||
119 | */ | ||
120 | while (1) { | ||
121 | current->state = TASK_INTERRUPTIBLE; | ||
122 | schedule(); | ||
123 | /* | ||
124 | * Return -EINTR and set condition code here, | ||
125 | * so the interrupted system call actually returns | ||
126 | * these. | ||
127 | */ | ||
128 | regs->psr |= PSR_C; | ||
129 | regs->u_regs[UREG_I0] = EINTR; | ||
130 | if (do_signal(&saveset, regs, 0, 0)) | ||
131 | return; | ||
132 | } | ||
133 | } | ||
134 | 107 | ||
135 | asmlinkage void do_sigpause(unsigned int set, struct pt_regs *regs) | 108 | return -ERESTARTNOHAND; |
136 | { | ||
137 | _sigpause_common(set, regs); | ||
138 | } | 109 | } |
139 | 110 | ||
140 | asmlinkage void do_sigsuspend (struct pt_regs *regs) | 111 | asmlinkage int sys_sigpause(unsigned int set) |
141 | { | 112 | { |
142 | _sigpause_common(regs->u_regs[UREG_I0], regs); | 113 | return _sigpause_common(set); |
143 | } | 114 | } |
144 | 115 | ||
145 | asmlinkage void do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, | 116 | asmlinkage int sys_sigsuspend(old_sigset_t set) |
146 | struct pt_regs *regs) | ||
147 | { | 117 | { |
148 | sigset_t oldset, set; | 118 | return _sigpause_common(set); |
149 | |||
150 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
151 | if (sigsetsize != sizeof(sigset_t)) { | ||
152 | regs->psr |= PSR_C; | ||
153 | regs->u_regs[UREG_I0] = EINVAL; | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | if (copy_from_user(&set, uset, sizeof(set))) { | ||
158 | regs->psr |= PSR_C; | ||
159 | regs->u_regs[UREG_I0] = EFAULT; | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
164 | spin_lock_irq(¤t->sighand->siglock); | ||
165 | oldset = current->blocked; | ||
166 | current->blocked = set; | ||
167 | recalc_sigpending(); | ||
168 | spin_unlock_irq(¤t->sighand->siglock); | ||
169 | |||
170 | regs->pc = regs->npc; | ||
171 | regs->npc += 4; | ||
172 | |||
173 | /* Condition codes and return value where set here for sigpause, | ||
174 | * and so got used by setup_frame, which again causes sigreturn() | ||
175 | * to return -EINTR. | ||
176 | */ | ||
177 | while (1) { | ||
178 | current->state = TASK_INTERRUPTIBLE; | ||
179 | schedule(); | ||
180 | /* | ||
181 | * Return -EINTR and set condition code here, | ||
182 | * so the interrupted system call actually returns | ||
183 | * these. | ||
184 | */ | ||
185 | regs->psr |= PSR_C; | ||
186 | regs->u_regs[UREG_I0] = EINTR; | ||
187 | if (do_signal(&oldset, regs, 0, 0)) | ||
188 | return; | ||
189 | } | ||
190 | } | 119 | } |
191 | 120 | ||
192 | static inline int | 121 | static inline int |
@@ -1067,13 +996,13 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, | |||
1067 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 996 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
1068 | * mistake. | 997 | * mistake. |
1069 | */ | 998 | */ |
1070 | asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, | 999 | asmlinkage void do_signal(struct pt_regs * regs, unsigned long orig_i0, int restart_syscall) |
1071 | unsigned long orig_i0, int restart_syscall) | ||
1072 | { | 1000 | { |
1073 | siginfo_t info; | 1001 | siginfo_t info; |
1074 | struct sparc_deliver_cookie cookie; | 1002 | struct sparc_deliver_cookie cookie; |
1075 | struct k_sigaction ka; | 1003 | struct k_sigaction ka; |
1076 | int signr; | 1004 | int signr; |
1005 | sigset_t *oldset; | ||
1077 | 1006 | ||
1078 | /* | 1007 | /* |
1079 | * XXX Disable svr4 signal handling until solaris emulation works. | 1008 | * XXX Disable svr4 signal handling until solaris emulation works. |
@@ -1089,7 +1018,9 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, | |||
1089 | cookie.restart_syscall = restart_syscall; | 1018 | cookie.restart_syscall = restart_syscall; |
1090 | cookie.orig_i0 = orig_i0; | 1019 | cookie.orig_i0 = orig_i0; |
1091 | 1020 | ||
1092 | if (!oldset) | 1021 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
1022 | oldset = ¤t->saved_sigmask; | ||
1023 | else | ||
1093 | oldset = ¤t->blocked; | 1024 | oldset = ¤t->blocked; |
1094 | 1025 | ||
1095 | signr = get_signal_to_deliver(&info, &ka, regs, &cookie); | 1026 | signr = get_signal_to_deliver(&info, &ka, regs, &cookie); |
@@ -1098,7 +1029,14 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, | |||
1098 | syscall_restart(cookie.orig_i0, regs, &ka.sa); | 1029 | syscall_restart(cookie.orig_i0, regs, &ka.sa); |
1099 | handle_signal(signr, &ka, &info, oldset, | 1030 | handle_signal(signr, &ka, &info, oldset, |
1100 | regs, svr4_signal); | 1031 | regs, svr4_signal); |
1101 | return 1; | 1032 | /* a signal was successfully delivered; the saved |
1033 | * sigmask will have been stored in the signal frame, | ||
1034 | * and will be restored by sigreturn, so we can simply | ||
1035 | * clear the TIF_RESTORE_SIGMASK flag. | ||
1036 | */ | ||
1037 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
1038 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
1039 | return; | ||
1102 | } | 1040 | } |
1103 | if (cookie.restart_syscall && | 1041 | if (cookie.restart_syscall && |
1104 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || | 1042 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || |
@@ -1115,7 +1053,14 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, | |||
1115 | regs->pc -= 4; | 1053 | regs->pc -= 4; |
1116 | regs->npc -= 4; | 1054 | regs->npc -= 4; |
1117 | } | 1055 | } |
1118 | return 0; | 1056 | |
1057 | /* if there's no signal to deliver, we just put the saved sigmask | ||
1058 | * back | ||
1059 | */ | ||
1060 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
1061 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
1062 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
1063 | } | ||
1119 | } | 1064 | } |
1120 | 1065 | ||
1121 | asmlinkage int | 1066 | asmlinkage int |