diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/um/kernel/sigio_user.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/um/kernel/sigio_user.c')
-rw-r--r-- | arch/um/kernel/sigio_user.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c new file mode 100644 index 000000000000..668df13d8c9d --- /dev/null +++ b/arch/um/kernel/sigio_user.c | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <termios.h> | ||
9 | #include <pty.h> | ||
10 | #include <signal.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <sched.h> | ||
14 | #include <sys/socket.h> | ||
15 | #include <sys/poll.h> | ||
16 | #include "init.h" | ||
17 | #include "user.h" | ||
18 | #include "kern_util.h" | ||
19 | #include "user_util.h" | ||
20 | #include "sigio.h" | ||
21 | #include "helper.h" | ||
22 | #include "os.h" | ||
23 | |||
24 | /* Changed during early boot */ | ||
25 | int pty_output_sigio = 0; | ||
26 | int pty_close_sigio = 0; | ||
27 | |||
28 | /* Used as a flag during SIGIO testing early in boot */ | ||
29 | static volatile int got_sigio = 0; | ||
30 | |||
31 | void __init handler(int sig) | ||
32 | { | ||
33 | got_sigio = 1; | ||
34 | } | ||
35 | |||
36 | struct openpty_arg { | ||
37 | int master; | ||
38 | int slave; | ||
39 | int err; | ||
40 | }; | ||
41 | |||
42 | static void openpty_cb(void *arg) | ||
43 | { | ||
44 | struct openpty_arg *info = arg; | ||
45 | |||
46 | info->err = 0; | ||
47 | if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) | ||
48 | info->err = -errno; | ||
49 | } | ||
50 | |||
51 | void __init check_one_sigio(void (*proc)(int, int)) | ||
52 | { | ||
53 | struct sigaction old, new; | ||
54 | struct openpty_arg pty = { .master = -1, .slave = -1 }; | ||
55 | int master, slave, err; | ||
56 | |||
57 | initial_thread_cb(openpty_cb, &pty); | ||
58 | if(pty.err){ | ||
59 | printk("openpty failed, errno = %d\n", -pty.err); | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | master = pty.master; | ||
64 | slave = pty.slave; | ||
65 | |||
66 | if((master == -1) || (slave == -1)){ | ||
67 | printk("openpty failed to allocate a pty\n"); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | /* Not now, but complain so we now where we failed. */ | ||
72 | err = raw(master); | ||
73 | if (err < 0) | ||
74 | panic("check_sigio : __raw failed, errno = %d\n", -err); | ||
75 | |||
76 | err = os_sigio_async(master, slave); | ||
77 | if(err < 0) | ||
78 | panic("tty_fds : sigio_async failed, err = %d\n", -err); | ||
79 | |||
80 | if(sigaction(SIGIO, NULL, &old) < 0) | ||
81 | panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); | ||
82 | new = old; | ||
83 | new.sa_handler = handler; | ||
84 | if(sigaction(SIGIO, &new, NULL) < 0) | ||
85 | panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); | ||
86 | |||
87 | got_sigio = 0; | ||
88 | (*proc)(master, slave); | ||
89 | |||
90 | os_close_file(master); | ||
91 | os_close_file(slave); | ||
92 | |||
93 | if(sigaction(SIGIO, &old, NULL) < 0) | ||
94 | panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); | ||
95 | } | ||
96 | |||
97 | static void tty_output(int master, int slave) | ||
98 | { | ||
99 | int n; | ||
100 | char buf[512]; | ||
101 | |||
102 | printk("Checking that host ptys support output SIGIO..."); | ||
103 | |||
104 | memset(buf, 0, sizeof(buf)); | ||
105 | |||
106 | while(os_write_file(master, buf, sizeof(buf)) > 0) ; | ||
107 | if(errno != EAGAIN) | ||
108 | panic("check_sigio : write failed, errno = %d\n", errno); | ||
109 | while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; | ||
110 | |||
111 | if (got_sigio) { | ||
112 | printk("Yes\n"); | ||
113 | pty_output_sigio = 1; | ||
114 | } else if (n == -EAGAIN) { | ||
115 | printk("No, enabling workaround\n"); | ||
116 | } else { | ||
117 | panic("check_sigio : read failed, err = %d\n", n); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void tty_close(int master, int slave) | ||
122 | { | ||
123 | printk("Checking that host ptys support SIGIO on close..."); | ||
124 | |||
125 | os_close_file(slave); | ||
126 | if(got_sigio){ | ||
127 | printk("Yes\n"); | ||
128 | pty_close_sigio = 1; | ||
129 | } | ||
130 | else printk("No, enabling workaround\n"); | ||
131 | } | ||
132 | |||
133 | void __init check_sigio(void) | ||
134 | { | ||
135 | if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && | ||
136 | (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ | ||
137 | printk("No pseudo-terminals available - skipping pty SIGIO " | ||
138 | "check\n"); | ||
139 | return; | ||
140 | } | ||
141 | check_one_sigio(tty_output); | ||
142 | check_one_sigio(tty_close); | ||
143 | } | ||
144 | |||
145 | /* Protected by sigio_lock(), also used by sigio_cleanup, which is an | ||
146 | * exitcall. | ||
147 | */ | ||
148 | static int write_sigio_pid = -1; | ||
149 | |||
150 | /* These arrays are initialized before the sigio thread is started, and | ||
151 | * the descriptors closed after it is killed. So, it can't see them change. | ||
152 | * On the UML side, they are changed under the sigio_lock. | ||
153 | */ | ||
154 | static int write_sigio_fds[2] = { -1, -1 }; | ||
155 | static int sigio_private[2] = { -1, -1 }; | ||
156 | |||
157 | struct pollfds { | ||
158 | struct pollfd *poll; | ||
159 | int size; | ||
160 | int used; | ||
161 | }; | ||
162 | |||
163 | /* Protected by sigio_lock(). Used by the sigio thread, but the UML thread | ||
164 | * synchronizes with it. | ||
165 | */ | ||
166 | struct pollfds current_poll = { | ||
167 | .poll = NULL, | ||
168 | .size = 0, | ||
169 | .used = 0 | ||
170 | }; | ||
171 | |||
172 | struct pollfds next_poll = { | ||
173 | .poll = NULL, | ||
174 | .size = 0, | ||
175 | .used = 0 | ||
176 | }; | ||
177 | |||
178 | static int write_sigio_thread(void *unused) | ||
179 | { | ||
180 | struct pollfds *fds, tmp; | ||
181 | struct pollfd *p; | ||
182 | int i, n, respond_fd; | ||
183 | char c; | ||
184 | |||
185 | fds = ¤t_poll; | ||
186 | while(1){ | ||
187 | n = poll(fds->poll, fds->used, -1); | ||
188 | if(n < 0){ | ||
189 | if(errno == EINTR) continue; | ||
190 | printk("write_sigio_thread : poll returned %d, " | ||
191 | "errno = %d\n", n, errno); | ||
192 | } | ||
193 | for(i = 0; i < fds->used; i++){ | ||
194 | p = &fds->poll[i]; | ||
195 | if(p->revents == 0) continue; | ||
196 | if(p->fd == sigio_private[1]){ | ||
197 | n = os_read_file(sigio_private[1], &c, sizeof(c)); | ||
198 | if(n != sizeof(c)) | ||
199 | printk("write_sigio_thread : " | ||
200 | "read failed, err = %d\n", -n); | ||
201 | tmp = current_poll; | ||
202 | current_poll = next_poll; | ||
203 | next_poll = tmp; | ||
204 | respond_fd = sigio_private[1]; | ||
205 | } | ||
206 | else { | ||
207 | respond_fd = write_sigio_fds[1]; | ||
208 | fds->used--; | ||
209 | memmove(&fds->poll[i], &fds->poll[i + 1], | ||
210 | (fds->used - i) * sizeof(*fds->poll)); | ||
211 | } | ||
212 | |||
213 | n = os_write_file(respond_fd, &c, sizeof(c)); | ||
214 | if(n != sizeof(c)) | ||
215 | printk("write_sigio_thread : write failed, " | ||
216 | "err = %d\n", -n); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static int need_poll(int n) | ||
222 | { | ||
223 | if(n <= next_poll.size){ | ||
224 | next_poll.used = n; | ||
225 | return(0); | ||
226 | } | ||
227 | if(next_poll.poll != NULL) kfree(next_poll.poll); | ||
228 | next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd)); | ||
229 | if(next_poll.poll == NULL){ | ||
230 | printk("need_poll : failed to allocate new pollfds\n"); | ||
231 | next_poll.size = 0; | ||
232 | next_poll.used = 0; | ||
233 | return(-1); | ||
234 | } | ||
235 | next_poll.size = n; | ||
236 | next_poll.used = n; | ||
237 | return(0); | ||
238 | } | ||
239 | |||
240 | /* Must be called with sigio_lock held, because it's needed by the marked | ||
241 | * critical section. */ | ||
242 | static void update_thread(void) | ||
243 | { | ||
244 | unsigned long flags; | ||
245 | int n; | ||
246 | char c; | ||
247 | |||
248 | flags = set_signals(0); | ||
249 | n = os_write_file(sigio_private[0], &c, sizeof(c)); | ||
250 | if(n != sizeof(c)){ | ||
251 | printk("update_thread : write failed, err = %d\n", -n); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | n = os_read_file(sigio_private[0], &c, sizeof(c)); | ||
256 | if(n != sizeof(c)){ | ||
257 | printk("update_thread : read failed, err = %d\n", -n); | ||
258 | goto fail; | ||
259 | } | ||
260 | |||
261 | set_signals(flags); | ||
262 | return; | ||
263 | fail: | ||
264 | /* Critical section start */ | ||
265 | if(write_sigio_pid != -1) | ||
266 | os_kill_process(write_sigio_pid, 1); | ||
267 | write_sigio_pid = -1; | ||
268 | os_close_file(sigio_private[0]); | ||
269 | os_close_file(sigio_private[1]); | ||
270 | os_close_file(write_sigio_fds[0]); | ||
271 | os_close_file(write_sigio_fds[1]); | ||
272 | /* Critical section end */ | ||
273 | set_signals(flags); | ||
274 | } | ||
275 | |||
276 | int add_sigio_fd(int fd, int read) | ||
277 | { | ||
278 | int err = 0, i, n, events; | ||
279 | |||
280 | sigio_lock(); | ||
281 | for(i = 0; i < current_poll.used; i++){ | ||
282 | if(current_poll.poll[i].fd == fd) | ||
283 | goto out; | ||
284 | } | ||
285 | |||
286 | n = current_poll.used + 1; | ||
287 | err = need_poll(n); | ||
288 | if(err) | ||
289 | goto out; | ||
290 | |||
291 | for(i = 0; i < current_poll.used; i++) | ||
292 | next_poll.poll[i] = current_poll.poll[i]; | ||
293 | |||
294 | if(read) events = POLLIN; | ||
295 | else events = POLLOUT; | ||
296 | |||
297 | next_poll.poll[n - 1] = ((struct pollfd) { .fd = fd, | ||
298 | .events = events, | ||
299 | .revents = 0 }); | ||
300 | update_thread(); | ||
301 | out: | ||
302 | sigio_unlock(); | ||
303 | return(err); | ||
304 | } | ||
305 | |||
306 | int ignore_sigio_fd(int fd) | ||
307 | { | ||
308 | struct pollfd *p; | ||
309 | int err = 0, i, n = 0; | ||
310 | |||
311 | sigio_lock(); | ||
312 | for(i = 0; i < current_poll.used; i++){ | ||
313 | if(current_poll.poll[i].fd == fd) break; | ||
314 | } | ||
315 | if(i == current_poll.used) | ||
316 | goto out; | ||
317 | |||
318 | err = need_poll(current_poll.used - 1); | ||
319 | if(err) | ||
320 | goto out; | ||
321 | |||
322 | for(i = 0; i < current_poll.used; i++){ | ||
323 | p = ¤t_poll.poll[i]; | ||
324 | if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i]; | ||
325 | } | ||
326 | if(n == i){ | ||
327 | printk("ignore_sigio_fd : fd %d not found\n", fd); | ||
328 | err = -1; | ||
329 | goto out; | ||
330 | } | ||
331 | |||
332 | update_thread(); | ||
333 | out: | ||
334 | sigio_unlock(); | ||
335 | return(err); | ||
336 | } | ||
337 | |||
338 | static int setup_initial_poll(int fd) | ||
339 | { | ||
340 | struct pollfd *p; | ||
341 | |||
342 | p = um_kmalloc(sizeof(struct pollfd)); | ||
343 | if(p == NULL){ | ||
344 | printk("setup_initial_poll : failed to allocate poll\n"); | ||
345 | return(-1); | ||
346 | } | ||
347 | *p = ((struct pollfd) { .fd = fd, | ||
348 | .events = POLLIN, | ||
349 | .revents = 0 }); | ||
350 | current_poll = ((struct pollfds) { .poll = p, | ||
351 | .used = 1, | ||
352 | .size = 1 }); | ||
353 | return(0); | ||
354 | } | ||
355 | |||
356 | void write_sigio_workaround(void) | ||
357 | { | ||
358 | unsigned long stack; | ||
359 | int err; | ||
360 | |||
361 | sigio_lock(); | ||
362 | if(write_sigio_pid != -1) | ||
363 | goto out; | ||
364 | |||
365 | err = os_pipe(write_sigio_fds, 1, 1); | ||
366 | if(err < 0){ | ||
367 | printk("write_sigio_workaround - os_pipe 1 failed, " | ||
368 | "err = %d\n", -err); | ||
369 | goto out; | ||
370 | } | ||
371 | err = os_pipe(sigio_private, 1, 1); | ||
372 | if(err < 0){ | ||
373 | printk("write_sigio_workaround - os_pipe 2 failed, " | ||
374 | "err = %d\n", -err); | ||
375 | goto out_close1; | ||
376 | } | ||
377 | if(setup_initial_poll(sigio_private[1])) | ||
378 | goto out_close2; | ||
379 | |||
380 | write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, | ||
381 | CLONE_FILES | CLONE_VM, &stack, 0); | ||
382 | |||
383 | if(write_sigio_pid < 0) goto out_close2; | ||
384 | |||
385 | if(write_sigio_irq(write_sigio_fds[0])) | ||
386 | goto out_kill; | ||
387 | |||
388 | out: | ||
389 | sigio_unlock(); | ||
390 | return; | ||
391 | |||
392 | out_kill: | ||
393 | os_kill_process(write_sigio_pid, 1); | ||
394 | write_sigio_pid = -1; | ||
395 | out_close2: | ||
396 | os_close_file(sigio_private[0]); | ||
397 | os_close_file(sigio_private[1]); | ||
398 | out_close1: | ||
399 | os_close_file(write_sigio_fds[0]); | ||
400 | os_close_file(write_sigio_fds[1]); | ||
401 | sigio_unlock(); | ||
402 | } | ||
403 | |||
404 | int read_sigio_fd(int fd) | ||
405 | { | ||
406 | int n; | ||
407 | char c; | ||
408 | |||
409 | n = os_read_file(fd, &c, sizeof(c)); | ||
410 | if(n != sizeof(c)){ | ||
411 | if(n < 0) { | ||
412 | printk("read_sigio_fd - read failed, err = %d\n", -n); | ||
413 | return(n); | ||
414 | } | ||
415 | else { | ||
416 | printk("read_sigio_fd - short read, bytes = %d\n", n); | ||
417 | return(-EIO); | ||
418 | } | ||
419 | } | ||
420 | return(n); | ||
421 | } | ||
422 | |||
423 | static void sigio_cleanup(void) | ||
424 | { | ||
425 | if (write_sigio_pid != -1) { | ||
426 | os_kill_process(write_sigio_pid, 1); | ||
427 | write_sigio_pid = -1; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | __uml_exitcall(sigio_cleanup); | ||