1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
4 | */ |
5 | |
6 | #include <unistd.h> |
7 | #include <errno.h> |
8 | #include <fcntl.h> |
9 | #include <poll.h> |
10 | #include <pty.h> |
11 | #include <sched.h> |
12 | #include <signal.h> |
13 | #include <string.h> |
14 | #include <kern_util.h> |
15 | #include <init.h> |
16 | #include <os.h> |
17 | #include <sigio.h> |
18 | #include <um_malloc.h> |
19 | |
20 | /* |
21 | * Protected by sigio_lock(), also used by sigio_cleanup, which is an |
22 | * exitcall. |
23 | */ |
24 | static int write_sigio_pid = -1; |
25 | static unsigned long write_sigio_stack; |
26 | |
27 | /* |
28 | * These arrays are initialized before the sigio thread is started, and |
29 | * the descriptors closed after it is killed. So, it can't see them change. |
30 | * On the UML side, they are changed under the sigio_lock. |
31 | */ |
32 | #define SIGIO_FDS_INIT {-1, -1} |
33 | |
34 | static int write_sigio_fds[2] = SIGIO_FDS_INIT; |
35 | static int sigio_private[2] = SIGIO_FDS_INIT; |
36 | |
37 | struct pollfds { |
38 | struct pollfd *poll; |
39 | int size; |
40 | int used; |
41 | }; |
42 | |
43 | /* |
44 | * Protected by sigio_lock(). Used by the sigio thread, but the UML thread |
45 | * synchronizes with it. |
46 | */ |
47 | static struct pollfds current_poll; |
48 | static struct pollfds next_poll; |
49 | static struct pollfds all_sigio_fds; |
50 | |
51 | static int write_sigio_thread(void *unused) |
52 | { |
53 | struct pollfds *fds, tmp; |
54 | struct pollfd *p; |
55 | int i, n, respond_fd; |
56 | char c; |
57 | |
58 | os_fix_helper_signals(); |
59 | fds = ¤t_poll; |
60 | while (1) { |
61 | n = poll(fds->poll, fds->used, -1); |
62 | if (n < 0) { |
63 | if (errno == EINTR) |
64 | continue; |
65 | printk(UM_KERN_ERR "write_sigio_thread : poll returned " |
66 | "%d, errno = %d\n" , n, errno); |
67 | } |
68 | for (i = 0; i < fds->used; i++) { |
69 | p = &fds->poll[i]; |
70 | if (p->revents == 0) |
71 | continue; |
72 | if (p->fd == sigio_private[1]) { |
73 | CATCH_EINTR(n = read(sigio_private[1], &c, |
74 | sizeof(c))); |
75 | if (n != sizeof(c)) |
76 | printk(UM_KERN_ERR |
77 | "write_sigio_thread : " |
78 | "read on socket failed, " |
79 | "err = %d\n" , errno); |
80 | tmp = current_poll; |
81 | current_poll = next_poll; |
82 | next_poll = tmp; |
83 | respond_fd = sigio_private[1]; |
84 | } |
85 | else { |
86 | respond_fd = write_sigio_fds[1]; |
87 | fds->used--; |
88 | memmove(&fds->poll[i], &fds->poll[i + 1], |
89 | (fds->used - i) * sizeof(*fds->poll)); |
90 | } |
91 | |
92 | CATCH_EINTR(n = write(respond_fd, &c, sizeof(c))); |
93 | if (n != sizeof(c)) |
94 | printk(UM_KERN_ERR "write_sigio_thread : " |
95 | "write on socket failed, err = %d\n" , |
96 | errno); |
97 | } |
98 | } |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | static int need_poll(struct pollfds *polls, int n) |
104 | { |
105 | struct pollfd *new; |
106 | |
107 | if (n <= polls->size) |
108 | return 0; |
109 | |
110 | new = uml_kmalloc(n * sizeof(struct pollfd), UM_GFP_ATOMIC); |
111 | if (new == NULL) { |
112 | printk(UM_KERN_ERR "need_poll : failed to allocate new " |
113 | "pollfds\n" ); |
114 | return -ENOMEM; |
115 | } |
116 | |
117 | memcpy(new, polls->poll, polls->used * sizeof(struct pollfd)); |
118 | kfree(polls->poll); |
119 | |
120 | polls->poll = new; |
121 | polls->size = n; |
122 | return 0; |
123 | } |
124 | |
125 | /* |
126 | * Must be called with sigio_lock held, because it's needed by the marked |
127 | * critical section. |
128 | */ |
129 | static void update_thread(void) |
130 | { |
131 | unsigned long flags; |
132 | int n; |
133 | char c; |
134 | |
135 | flags = um_set_signals_trace(0); |
136 | CATCH_EINTR(n = write(sigio_private[0], &c, sizeof(c))); |
137 | if (n != sizeof(c)) { |
138 | printk(UM_KERN_ERR "update_thread : write failed, err = %d\n" , |
139 | errno); |
140 | goto fail; |
141 | } |
142 | |
143 | CATCH_EINTR(n = read(sigio_private[0], &c, sizeof(c))); |
144 | if (n != sizeof(c)) { |
145 | printk(UM_KERN_ERR "update_thread : read failed, err = %d\n" , |
146 | errno); |
147 | goto fail; |
148 | } |
149 | |
150 | um_set_signals_trace(flags); |
151 | return; |
152 | fail: |
153 | /* Critical section start */ |
154 | if (write_sigio_pid != -1) { |
155 | os_kill_process(write_sigio_pid, 1); |
156 | free_stack(write_sigio_stack, 0); |
157 | } |
158 | write_sigio_pid = -1; |
159 | close(sigio_private[0]); |
160 | close(sigio_private[1]); |
161 | close(write_sigio_fds[0]); |
162 | close(write_sigio_fds[1]); |
163 | /* Critical section end */ |
164 | um_set_signals_trace(flags); |
165 | } |
166 | |
167 | int __add_sigio_fd(int fd) |
168 | { |
169 | struct pollfd *p; |
170 | int err, i, n; |
171 | |
172 | for (i = 0; i < all_sigio_fds.used; i++) { |
173 | if (all_sigio_fds.poll[i].fd == fd) |
174 | break; |
175 | } |
176 | if (i == all_sigio_fds.used) |
177 | return -ENOSPC; |
178 | |
179 | p = &all_sigio_fds.poll[i]; |
180 | |
181 | for (i = 0; i < current_poll.used; i++) { |
182 | if (current_poll.poll[i].fd == fd) |
183 | return 0; |
184 | } |
185 | |
186 | n = current_poll.used; |
187 | err = need_poll(polls: &next_poll, n: n + 1); |
188 | if (err) |
189 | return err; |
190 | |
191 | memcpy(next_poll.poll, current_poll.poll, |
192 | current_poll.used * sizeof(struct pollfd)); |
193 | next_poll.poll[n] = *p; |
194 | next_poll.used = n + 1; |
195 | update_thread(); |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | |
201 | int add_sigio_fd(int fd) |
202 | { |
203 | int err; |
204 | |
205 | sigio_lock(); |
206 | err = __add_sigio_fd(fd); |
207 | sigio_unlock(); |
208 | |
209 | return err; |
210 | } |
211 | |
212 | int __ignore_sigio_fd(int fd) |
213 | { |
214 | struct pollfd *p; |
215 | int err, i, n = 0; |
216 | |
217 | /* |
218 | * This is called from exitcalls elsewhere in UML - if |
219 | * sigio_cleanup has already run, then update_thread will hang |
220 | * or fail because the thread is no longer running. |
221 | */ |
222 | if (write_sigio_pid == -1) |
223 | return -EIO; |
224 | |
225 | for (i = 0; i < current_poll.used; i++) { |
226 | if (current_poll.poll[i].fd == fd) |
227 | break; |
228 | } |
229 | if (i == current_poll.used) |
230 | return -ENOENT; |
231 | |
232 | err = need_poll(polls: &next_poll, n: current_poll.used - 1); |
233 | if (err) |
234 | return err; |
235 | |
236 | for (i = 0; i < current_poll.used; i++) { |
237 | p = ¤t_poll.poll[i]; |
238 | if (p->fd != fd) |
239 | next_poll.poll[n++] = *p; |
240 | } |
241 | next_poll.used = current_poll.used - 1; |
242 | |
243 | update_thread(); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | int ignore_sigio_fd(int fd) |
249 | { |
250 | int err; |
251 | |
252 | sigio_lock(); |
253 | err = __ignore_sigio_fd(fd); |
254 | sigio_unlock(); |
255 | |
256 | return err; |
257 | } |
258 | |
259 | static struct pollfd *setup_initial_poll(int fd) |
260 | { |
261 | struct pollfd *p; |
262 | |
263 | p = uml_kmalloc(sizeof(struct pollfd), UM_GFP_KERNEL); |
264 | if (p == NULL) { |
265 | printk(UM_KERN_ERR "setup_initial_poll : failed to allocate " |
266 | "poll\n" ); |
267 | return NULL; |
268 | } |
269 | *p = ((struct pollfd) { .fd = fd, |
270 | .events = POLLIN, |
271 | .revents = 0 }); |
272 | return p; |
273 | } |
274 | |
275 | static void write_sigio_workaround(void) |
276 | { |
277 | struct pollfd *p; |
278 | int err; |
279 | int l_write_sigio_fds[2]; |
280 | int l_sigio_private[2]; |
281 | int l_write_sigio_pid; |
282 | |
283 | /* We call this *tons* of times - and most ones we must just fail. */ |
284 | sigio_lock(); |
285 | l_write_sigio_pid = write_sigio_pid; |
286 | sigio_unlock(); |
287 | |
288 | if (l_write_sigio_pid != -1) |
289 | return; |
290 | |
291 | err = os_pipe(l_write_sigio_fds, 1, 1); |
292 | if (err < 0) { |
293 | printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 1 failed, " |
294 | "err = %d\n" , -err); |
295 | return; |
296 | } |
297 | err = os_pipe(l_sigio_private, 1, 1); |
298 | if (err < 0) { |
299 | printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 2 failed, " |
300 | "err = %d\n" , -err); |
301 | goto out_close1; |
302 | } |
303 | |
304 | p = setup_initial_poll(l_sigio_private[1]); |
305 | if (!p) |
306 | goto out_close2; |
307 | |
308 | sigio_lock(); |
309 | |
310 | /* |
311 | * Did we race? Don't try to optimize this, please, it's not so likely |
312 | * to happen, and no more than once at the boot. |
313 | */ |
314 | if (write_sigio_pid != -1) |
315 | goto out_free; |
316 | |
317 | current_poll = ((struct pollfds) { .poll = p, |
318 | .used = 1, |
319 | .size = 1 }); |
320 | |
321 | if (write_sigio_irq(l_write_sigio_fds[0])) |
322 | goto out_clear_poll; |
323 | |
324 | memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); |
325 | memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); |
326 | |
327 | write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, |
328 | CLONE_FILES | CLONE_VM, |
329 | &write_sigio_stack); |
330 | |
331 | if (write_sigio_pid < 0) |
332 | goto out_clear; |
333 | |
334 | sigio_unlock(); |
335 | return; |
336 | |
337 | out_clear: |
338 | write_sigio_pid = -1; |
339 | write_sigio_fds[0] = -1; |
340 | write_sigio_fds[1] = -1; |
341 | sigio_private[0] = -1; |
342 | sigio_private[1] = -1; |
343 | out_clear_poll: |
344 | current_poll = ((struct pollfds) { .poll = NULL, |
345 | .size = 0, |
346 | .used = 0 }); |
347 | out_free: |
348 | sigio_unlock(); |
349 | kfree(p); |
350 | out_close2: |
351 | close(l_sigio_private[0]); |
352 | close(l_sigio_private[1]); |
353 | out_close1: |
354 | close(l_write_sigio_fds[0]); |
355 | close(l_write_sigio_fds[1]); |
356 | } |
357 | |
358 | void sigio_broken(int fd) |
359 | { |
360 | int err; |
361 | |
362 | write_sigio_workaround(); |
363 | |
364 | sigio_lock(); |
365 | err = need_poll(polls: &all_sigio_fds, n: all_sigio_fds.used + 1); |
366 | if (err) { |
367 | printk(UM_KERN_ERR "maybe_sigio_broken - failed to add pollfd " |
368 | "for descriptor %d\n" , fd); |
369 | goto out; |
370 | } |
371 | |
372 | all_sigio_fds.poll[all_sigio_fds.used++] = |
373 | ((struct pollfd) { .fd = fd, |
374 | .events = POLLIN, |
375 | .revents = 0 }); |
376 | out: |
377 | sigio_unlock(); |
378 | } |
379 | |
380 | /* Changed during early boot */ |
381 | static int pty_output_sigio; |
382 | |
383 | void maybe_sigio_broken(int fd) |
384 | { |
385 | if (!isatty(fd)) |
386 | return; |
387 | |
388 | if (pty_output_sigio) |
389 | return; |
390 | |
391 | sigio_broken(fd); |
392 | } |
393 | |
394 | static void sigio_cleanup(void) |
395 | { |
396 | if (write_sigio_pid == -1) |
397 | return; |
398 | |
399 | os_kill_process(write_sigio_pid, 1); |
400 | free_stack(write_sigio_stack, 0); |
401 | write_sigio_pid = -1; |
402 | } |
403 | |
404 | __uml_exitcall(sigio_cleanup); |
405 | |
406 | /* Used as a flag during SIGIO testing early in boot */ |
407 | static int got_sigio; |
408 | |
409 | static void __init handler(int sig) |
410 | { |
411 | got_sigio = 1; |
412 | } |
413 | |
414 | struct openpty_arg { |
415 | int master; |
416 | int slave; |
417 | int err; |
418 | }; |
419 | |
420 | static void openpty_cb(void *arg) |
421 | { |
422 | struct openpty_arg *info = arg; |
423 | |
424 | info->err = 0; |
425 | if (openpty(&info->master, &info->slave, NULL, NULL, NULL)) |
426 | info->err = -errno; |
427 | } |
428 | |
429 | static int async_pty(int master, int slave) |
430 | { |
431 | int flags; |
432 | |
433 | flags = fcntl(master, F_GETFL); |
434 | if (flags < 0) |
435 | return -errno; |
436 | |
437 | if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) || |
438 | (fcntl(master, F_SETOWN, os_getpid()) < 0)) |
439 | return -errno; |
440 | |
441 | if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)) |
442 | return -errno; |
443 | |
444 | return 0; |
445 | } |
446 | |
447 | static void __init check_one_sigio(void (*proc)(int, int)) |
448 | { |
449 | struct sigaction old, new; |
450 | struct openpty_arg pty = { .master = -1, .slave = -1 }; |
451 | int master, slave, err; |
452 | |
453 | initial_thread_cb(openpty_cb, &pty); |
454 | if (pty.err) { |
455 | printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n" , |
456 | -pty.err); |
457 | return; |
458 | } |
459 | |
460 | master = pty.master; |
461 | slave = pty.slave; |
462 | |
463 | if ((master == -1) || (slave == -1)) { |
464 | printk(UM_KERN_ERR "check_one_sigio failed to allocate a " |
465 | "pty\n" ); |
466 | return; |
467 | } |
468 | |
469 | /* Not now, but complain so we now where we failed. */ |
470 | err = raw(master); |
471 | if (err < 0) { |
472 | printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n" , |
473 | -err); |
474 | return; |
475 | } |
476 | |
477 | err = async_pty(master, slave); |
478 | if (err < 0) { |
479 | printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, " |
480 | "err = %d\n" , -err); |
481 | return; |
482 | } |
483 | |
484 | if (sigaction(SIGIO, NULL, &old) < 0) { |
485 | printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, " |
486 | "errno = %d\n" , errno); |
487 | return; |
488 | } |
489 | |
490 | new = old; |
491 | new.sa_handler = handler; |
492 | if (sigaction(SIGIO, &new, NULL) < 0) { |
493 | printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, " |
494 | "errno = %d\n" , errno); |
495 | return; |
496 | } |
497 | |
498 | got_sigio = 0; |
499 | (*proc)(master, slave); |
500 | |
501 | close(master); |
502 | close(slave); |
503 | |
504 | if (sigaction(SIGIO, &old, NULL) < 0) |
505 | printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, " |
506 | "errno = %d\n" , errno); |
507 | } |
508 | |
509 | static void tty_output(int master, int slave) |
510 | { |
511 | int n; |
512 | char buf[512]; |
513 | |
514 | printk(UM_KERN_INFO "Checking that host ptys support output SIGIO..." ); |
515 | |
516 | memset(buf, 0, sizeof(buf)); |
517 | |
518 | while (write(master, buf, sizeof(buf)) > 0) ; |
519 | if (errno != EAGAIN) |
520 | printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n" , |
521 | errno); |
522 | while (((n = read(slave, buf, sizeof(buf))) > 0) && |
523 | !({ barrier(); got_sigio; })) |
524 | ; |
525 | |
526 | if (got_sigio) { |
527 | printk(UM_KERN_CONT "Yes\n" ); |
528 | pty_output_sigio = 1; |
529 | } else if (n == -EAGAIN) |
530 | printk(UM_KERN_CONT "No, enabling workaround\n" ); |
531 | else |
532 | printk(UM_KERN_CONT "tty_output : read failed, err = %d\n" , n); |
533 | } |
534 | |
535 | static void __init check_sigio(void) |
536 | { |
537 | if ((access("/dev/ptmx" , R_OK) < 0) && |
538 | (access("/dev/ptyp0" , R_OK) < 0)) { |
539 | printk(UM_KERN_WARNING "No pseudo-terminals available - " |
540 | "skipping pty SIGIO check\n" ); |
541 | return; |
542 | } |
543 | check_one_sigio(tty_output); |
544 | } |
545 | |
546 | /* Here because it only does the SIGIO testing for now */ |
547 | void __init os_check_bugs(void) |
548 | { |
549 | check_sigio(); |
550 | } |
551 | |