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