Commit | Line | Data |
---|---|---|
1da177e4 LT |
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" | |
1da177e4 LT |
21 | #include "os.h" |
22 | ||
23 | /* Changed during early boot */ | |
24 | int pty_output_sigio = 0; | |
25 | int pty_close_sigio = 0; | |
26 | ||
27 | /* Used as a flag during SIGIO testing early in boot */ | |
28 | static volatile int got_sigio = 0; | |
29 | ||
30 | void __init handler(int sig) | |
31 | { | |
32 | got_sigio = 1; | |
33 | } | |
34 | ||
35 | struct openpty_arg { | |
36 | int master; | |
37 | int slave; | |
38 | int err; | |
39 | }; | |
40 | ||
41 | static void openpty_cb(void *arg) | |
42 | { | |
43 | struct openpty_arg *info = arg; | |
44 | ||
45 | info->err = 0; | |
46 | if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) | |
47 | info->err = -errno; | |
48 | } | |
49 | ||
50 | void __init check_one_sigio(void (*proc)(int, int)) | |
51 | { | |
52 | struct sigaction old, new; | |
53 | struct openpty_arg pty = { .master = -1, .slave = -1 }; | |
54 | int master, slave, err; | |
55 | ||
56 | initial_thread_cb(openpty_cb, &pty); | |
57 | if(pty.err){ | |
58 | printk("openpty failed, errno = %d\n", -pty.err); | |
59 | return; | |
60 | } | |
61 | ||
62 | master = pty.master; | |
63 | slave = pty.slave; | |
64 | ||
65 | if((master == -1) || (slave == -1)){ | |
66 | printk("openpty failed to allocate a pty\n"); | |
67 | return; | |
68 | } | |
69 | ||
70 | /* Not now, but complain so we now where we failed. */ | |
71 | err = raw(master); | |
72 | if (err < 0) | |
73 | panic("check_sigio : __raw failed, errno = %d\n", -err); | |
74 | ||
75 | err = os_sigio_async(master, slave); | |
76 | if(err < 0) | |
77 | panic("tty_fds : sigio_async failed, err = %d\n", -err); | |
78 | ||
79 | if(sigaction(SIGIO, NULL, &old) < 0) | |
80 | panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); | |
81 | new = old; | |
82 | new.sa_handler = handler; | |
83 | if(sigaction(SIGIO, &new, NULL) < 0) | |
84 | panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); | |
85 | ||
86 | got_sigio = 0; | |
87 | (*proc)(master, slave); | |
88 | ||
89 | os_close_file(master); | |
90 | os_close_file(slave); | |
91 | ||
92 | if(sigaction(SIGIO, &old, NULL) < 0) | |
93 | panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); | |
94 | } | |
95 | ||
96 | static void tty_output(int master, int slave) | |
97 | { | |
98 | int n; | |
99 | char buf[512]; | |
100 | ||
101 | printk("Checking that host ptys support output SIGIO..."); | |
102 | ||
103 | memset(buf, 0, sizeof(buf)); | |
104 | ||
105 | while(os_write_file(master, buf, sizeof(buf)) > 0) ; | |
106 | if(errno != EAGAIN) | |
107 | panic("check_sigio : write failed, errno = %d\n", errno); | |
108 | while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; | |
109 | ||
110 | if (got_sigio) { | |
111 | printk("Yes\n"); | |
112 | pty_output_sigio = 1; | |
113 | } else if (n == -EAGAIN) { | |
114 | printk("No, enabling workaround\n"); | |
115 | } else { | |
116 | panic("check_sigio : read failed, err = %d\n", n); | |
117 | } | |
118 | } | |
119 | ||
120 | static void tty_close(int master, int slave) | |
121 | { | |
122 | printk("Checking that host ptys support SIGIO on close..."); | |
123 | ||
124 | os_close_file(slave); | |
125 | if(got_sigio){ | |
126 | printk("Yes\n"); | |
127 | pty_close_sigio = 1; | |
128 | } | |
129 | else printk("No, enabling workaround\n"); | |
130 | } | |
131 | ||
132 | void __init check_sigio(void) | |
133 | { | |
134 | if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && | |
135 | (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ | |
136 | printk("No pseudo-terminals available - skipping pty SIGIO " | |
137 | "check\n"); | |
138 | return; | |
139 | } | |
140 | check_one_sigio(tty_output); | |
141 | check_one_sigio(tty_close); | |
142 | } | |
143 | ||
144 | /* Protected by sigio_lock(), also used by sigio_cleanup, which is an | |
145 | * exitcall. | |
146 | */ | |
147 | static int write_sigio_pid = -1; | |
148 | ||
149 | /* These arrays are initialized before the sigio thread is started, and | |
150 | * the descriptors closed after it is killed. So, it can't see them change. | |
151 | * On the UML side, they are changed under the sigio_lock. | |
152 | */ | |
153 | static int write_sigio_fds[2] = { -1, -1 }; | |
154 | static int sigio_private[2] = { -1, -1 }; | |
155 | ||
156 | struct pollfds { | |
157 | struct pollfd *poll; | |
158 | int size; | |
159 | int used; | |
160 | }; | |
161 | ||
162 | /* Protected by sigio_lock(). Used by the sigio thread, but the UML thread | |
163 | * synchronizes with it. | |
164 | */ | |
165 | struct pollfds current_poll = { | |
166 | .poll = NULL, | |
167 | .size = 0, | |
168 | .used = 0 | |
169 | }; | |
170 | ||
171 | struct pollfds next_poll = { | |
172 | .poll = NULL, | |
173 | .size = 0, | |
174 | .used = 0 | |
175 | }; | |
176 | ||
177 | static int write_sigio_thread(void *unused) | |
178 | { | |
179 | struct pollfds *fds, tmp; | |
180 | struct pollfd *p; | |
181 | int i, n, respond_fd; | |
182 | char c; | |
183 | ||
cd2ee4a3 | 184 | signal(SIGWINCH, SIG_IGN); |
1da177e4 LT |
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 | } | |
b2325fe1 | 227 | kfree(next_poll.poll); |
1da177e4 LT |
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 | ||
79ae2cb8 | 342 | p = um_kmalloc_atomic(sizeof(struct pollfd)); |
1da177e4 LT |
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); |