197870c34SAlex Dewar // SPDX-License-Identifier: GPL-2.0 2f206aabbSJeff Dike /* 35d33e4d7SJeff Dike * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) 4f206aabbSJeff Dike */ 5f206aabbSJeff Dike 6f206aabbSJeff Dike #include <unistd.h> 7f206aabbSJeff Dike #include <errno.h> 8fee64d3cSJeff Dike #include <fcntl.h> 9fee64d3cSJeff Dike #include <poll.h> 10fee64d3cSJeff Dike #include <pty.h> 11f206aabbSJeff Dike #include <sched.h> 12fee64d3cSJeff Dike #include <signal.h> 13fee64d3cSJeff Dike #include <string.h> 1437185b33SAl Viro #include <kern_util.h> 1537185b33SAl Viro #include <init.h> 1637185b33SAl Viro #include <os.h> 1737185b33SAl Viro #include <sigio.h> 1837185b33SAl Viro #include <um_malloc.h> 19f206aabbSJeff Dike 20fee64d3cSJeff Dike /* 21fee64d3cSJeff Dike * Protected by sigio_lock(), also used by sigio_cleanup, which is an 22f206aabbSJeff Dike * exitcall. 23f206aabbSJeff Dike */ 24f206aabbSJeff Dike static int write_sigio_pid = -1; 25c4399016SJeff Dike static unsigned long write_sigio_stack; 26f206aabbSJeff Dike 27fee64d3cSJeff Dike /* 28fee64d3cSJeff Dike * These arrays are initialized before the sigio thread is started, and 29f206aabbSJeff Dike * the descriptors closed after it is killed. So, it can't see them change. 30f206aabbSJeff Dike * On the UML side, they are changed under the sigio_lock. 31f206aabbSJeff Dike */ 325f4e8fd0SJeff Dike #define SIGIO_FDS_INIT {-1, -1} 335f4e8fd0SJeff Dike 345f4e8fd0SJeff Dike static int write_sigio_fds[2] = SIGIO_FDS_INIT; 355f4e8fd0SJeff Dike static int sigio_private[2] = SIGIO_FDS_INIT; 36f206aabbSJeff Dike 37f206aabbSJeff Dike struct pollfds { 38f206aabbSJeff Dike struct pollfd *poll; 39f206aabbSJeff Dike int size; 40f206aabbSJeff Dike int used; 41f206aabbSJeff Dike }; 42f206aabbSJeff Dike 43fee64d3cSJeff Dike /* 44fee64d3cSJeff Dike * Protected by sigio_lock(). Used by the sigio thread, but the UML thread 45f206aabbSJeff Dike * synchronizes with it. 46f206aabbSJeff Dike */ 4719bdf040SJeff Dike static struct pollfds current_poll; 4819bdf040SJeff Dike static struct pollfds next_poll; 4919bdf040SJeff Dike static struct pollfds all_sigio_fds; 50f206aabbSJeff Dike 51f206aabbSJeff Dike static int write_sigio_thread(void *unused) 52f206aabbSJeff Dike { 53f206aabbSJeff Dike struct pollfds *fds, tmp; 54f206aabbSJeff Dike struct pollfd *p; 55f206aabbSJeff Dike int i, n, respond_fd; 56f206aabbSJeff Dike char c; 57f206aabbSJeff Dike 5891d44ff8SRichard Weinberger os_fix_helper_signals(); 59f206aabbSJeff Dike fds = ¤t_poll; 60f206aabbSJeff Dike while (1) { 61f206aabbSJeff Dike n = poll(fds->poll, fds->used, -1); 62f206aabbSJeff Dike if (n < 0) { 63fee64d3cSJeff Dike if (errno == EINTR) 64fee64d3cSJeff Dike continue; 65fee64d3cSJeff Dike printk(UM_KERN_ERR "write_sigio_thread : poll returned " 66fee64d3cSJeff Dike "%d, errno = %d\n", n, errno); 67f206aabbSJeff Dike } 68f206aabbSJeff Dike for (i = 0; i < fds->used; i++) { 69f206aabbSJeff Dike p = &fds->poll[i]; 70fee64d3cSJeff Dike if (p->revents == 0) 71fee64d3cSJeff Dike continue; 72f206aabbSJeff Dike if (p->fd == sigio_private[1]) { 73a61f334fSJeff Dike CATCH_EINTR(n = read(sigio_private[1], &c, 74a61f334fSJeff Dike sizeof(c))); 75f206aabbSJeff Dike if (n != sizeof(c)) 76fee64d3cSJeff Dike printk(UM_KERN_ERR 77fee64d3cSJeff Dike "write_sigio_thread : " 7819bdf040SJeff Dike "read on socket failed, " 79a61f334fSJeff Dike "err = %d\n", errno); 80f206aabbSJeff Dike tmp = current_poll; 81f206aabbSJeff Dike current_poll = next_poll; 82f206aabbSJeff Dike next_poll = tmp; 83f206aabbSJeff Dike respond_fd = sigio_private[1]; 84f206aabbSJeff Dike } 85f206aabbSJeff Dike else { 86f206aabbSJeff Dike respond_fd = write_sigio_fds[1]; 87f206aabbSJeff Dike fds->used--; 88f206aabbSJeff Dike memmove(&fds->poll[i], &fds->poll[i + 1], 89f206aabbSJeff Dike (fds->used - i) * sizeof(*fds->poll)); 90f206aabbSJeff Dike } 91f206aabbSJeff Dike 92a61f334fSJeff Dike CATCH_EINTR(n = write(respond_fd, &c, sizeof(c))); 93f206aabbSJeff Dike if (n != sizeof(c)) 94fee64d3cSJeff Dike printk(UM_KERN_ERR "write_sigio_thread : " 95fee64d3cSJeff Dike "write on socket failed, err = %d\n", 96fee64d3cSJeff Dike errno); 97f206aabbSJeff Dike } 98f206aabbSJeff Dike } 99f206aabbSJeff Dike 100f206aabbSJeff Dike return 0; 101f206aabbSJeff Dike } 102f206aabbSJeff Dike 10319bdf040SJeff Dike static int need_poll(struct pollfds *polls, int n) 104f206aabbSJeff Dike { 105838e56a1SJeff Dike struct pollfd *new; 106838e56a1SJeff Dike 107838e56a1SJeff Dike if (n <= polls->size) 10819bdf040SJeff Dike return 0; 109838e56a1SJeff Dike 11043f5b308SJeff Dike new = uml_kmalloc(n * sizeof(struct pollfd), UM_GFP_ATOMIC); 111838e56a1SJeff Dike if (new == NULL) { 112fee64d3cSJeff Dike printk(UM_KERN_ERR "need_poll : failed to allocate new " 113fee64d3cSJeff Dike "pollfds\n"); 11419bdf040SJeff Dike return -ENOMEM; 115f206aabbSJeff Dike } 116838e56a1SJeff Dike 117838e56a1SJeff Dike memcpy(new, polls->poll, polls->used * sizeof(struct pollfd)); 118838e56a1SJeff Dike kfree(polls->poll); 119838e56a1SJeff Dike 120838e56a1SJeff Dike polls->poll = new; 12119bdf040SJeff Dike polls->size = n; 12219bdf040SJeff Dike return 0; 123f206aabbSJeff Dike } 124f206aabbSJeff Dike 125fee64d3cSJeff Dike /* 126fee64d3cSJeff Dike * Must be called with sigio_lock held, because it's needed by the marked 12719bdf040SJeff Dike * critical section. 12819bdf040SJeff Dike */ 129f206aabbSJeff Dike static void update_thread(void) 130f206aabbSJeff Dike { 131f206aabbSJeff Dike unsigned long flags; 132f206aabbSJeff Dike int n; 133f206aabbSJeff Dike char c; 134f206aabbSJeff Dike 1350dafcbe1SJohannes Berg flags = set_signals_trace(0); 136fee64d3cSJeff Dike CATCH_EINTR(n = write(sigio_private[0], &c, sizeof(c))); 137f206aabbSJeff Dike if (n != sizeof(c)) { 138fee64d3cSJeff Dike printk(UM_KERN_ERR "update_thread : write failed, err = %d\n", 139fee64d3cSJeff Dike errno); 140f206aabbSJeff Dike goto fail; 141f206aabbSJeff Dike } 142f206aabbSJeff Dike 143a61f334fSJeff Dike CATCH_EINTR(n = read(sigio_private[0], &c, sizeof(c))); 144f206aabbSJeff Dike if (n != sizeof(c)) { 145fee64d3cSJeff Dike printk(UM_KERN_ERR "update_thread : read failed, err = %d\n", 146fee64d3cSJeff Dike errno); 147f206aabbSJeff Dike goto fail; 148f206aabbSJeff Dike } 149f206aabbSJeff Dike 1500dafcbe1SJohannes Berg set_signals_trace(flags); 151f206aabbSJeff Dike return; 152f206aabbSJeff Dike fail: 153f206aabbSJeff Dike /* Critical section start */ 154c4399016SJeff Dike if (write_sigio_pid != -1) { 155f206aabbSJeff Dike os_kill_process(write_sigio_pid, 1); 156c4399016SJeff Dike free_stack(write_sigio_stack, 0); 157c4399016SJeff Dike } 158f206aabbSJeff Dike write_sigio_pid = -1; 159f206aabbSJeff Dike close(sigio_private[0]); 160f206aabbSJeff Dike close(sigio_private[1]); 161f206aabbSJeff Dike close(write_sigio_fds[0]); 162f206aabbSJeff Dike close(write_sigio_fds[1]); 163f206aabbSJeff Dike /* Critical section end */ 1640dafcbe1SJohannes Berg set_signals_trace(flags); 165f206aabbSJeff Dike } 166f206aabbSJeff Dike 167*cae20ba0SJohannes Berg int __add_sigio_fd(int fd) 168f206aabbSJeff Dike { 16919bdf040SJeff Dike struct pollfd *p; 170d66c9183SJohannes Berg int err, i, n; 171f206aabbSJeff Dike 17219bdf040SJeff Dike for (i = 0; i < all_sigio_fds.used; i++) { 17319bdf040SJeff Dike if (all_sigio_fds.poll[i].fd == fd) 17419bdf040SJeff Dike break; 17519bdf040SJeff Dike } 176*cae20ba0SJohannes Berg if (i == all_sigio_fds.used) 177*cae20ba0SJohannes Berg return -ENOSPC; 17819bdf040SJeff Dike 17919bdf040SJeff Dike p = &all_sigio_fds.poll[i]; 18019bdf040SJeff Dike 181f206aabbSJeff Dike for (i = 0; i < current_poll.used; i++) { 182f206aabbSJeff Dike if (current_poll.poll[i].fd == fd) 183*cae20ba0SJohannes Berg return 0; 184f206aabbSJeff Dike } 185f206aabbSJeff Dike 186838e56a1SJeff Dike n = current_poll.used; 187838e56a1SJeff Dike err = need_poll(&next_poll, n + 1); 188f206aabbSJeff Dike if (err) 189*cae20ba0SJohannes Berg return err; 190f206aabbSJeff Dike 191838e56a1SJeff Dike memcpy(next_poll.poll, current_poll.poll, 192838e56a1SJeff Dike current_poll.used * sizeof(struct pollfd)); 193838e56a1SJeff Dike next_poll.poll[n] = *p; 194838e56a1SJeff Dike next_poll.used = n + 1; 195f206aabbSJeff Dike update_thread(); 196*cae20ba0SJohannes Berg 197*cae20ba0SJohannes Berg return 0; 198*cae20ba0SJohannes Berg } 199*cae20ba0SJohannes Berg 200*cae20ba0SJohannes Berg 201*cae20ba0SJohannes Berg int add_sigio_fd(int fd) 202*cae20ba0SJohannes Berg { 203*cae20ba0SJohannes Berg int err; 204*cae20ba0SJohannes Berg 205*cae20ba0SJohannes Berg sigio_lock(); 206*cae20ba0SJohannes Berg err = __add_sigio_fd(fd); 207f206aabbSJeff Dike sigio_unlock(); 208*cae20ba0SJohannes Berg 20919bdf040SJeff Dike return err; 210f206aabbSJeff Dike } 211f206aabbSJeff Dike 212*cae20ba0SJohannes Berg int __ignore_sigio_fd(int fd) 213f206aabbSJeff Dike { 214f206aabbSJeff Dike struct pollfd *p; 215*cae20ba0SJohannes Berg int err, i, n = 0; 216f206aabbSJeff Dike 217fee64d3cSJeff Dike /* 218fee64d3cSJeff Dike * This is called from exitcalls elsewhere in UML - if 21961232f2fSJeff Dike * sigio_cleanup has already run, then update_thread will hang 22061232f2fSJeff Dike * or fail because the thread is no longer running. 22161232f2fSJeff Dike */ 22261232f2fSJeff Dike if (write_sigio_pid == -1) 22361232f2fSJeff Dike return -EIO; 22461232f2fSJeff Dike 225f206aabbSJeff Dike for (i = 0; i < current_poll.used; i++) { 226fee64d3cSJeff Dike if (current_poll.poll[i].fd == fd) 227fee64d3cSJeff Dike break; 228f206aabbSJeff Dike } 229f206aabbSJeff Dike if (i == current_poll.used) 230*cae20ba0SJohannes Berg return -ENOENT; 231f206aabbSJeff Dike 23219bdf040SJeff Dike err = need_poll(&next_poll, current_poll.used - 1); 233f206aabbSJeff Dike if (err) 234*cae20ba0SJohannes Berg return err; 235f206aabbSJeff Dike 236f206aabbSJeff Dike for (i = 0; i < current_poll.used; i++) { 237f206aabbSJeff Dike p = ¤t_poll.poll[i]; 23819bdf040SJeff Dike if (p->fd != fd) 23919bdf040SJeff Dike next_poll.poll[n++] = *p; 240f206aabbSJeff Dike } 241838e56a1SJeff Dike next_poll.used = current_poll.used - 1; 242f206aabbSJeff Dike 243f206aabbSJeff Dike update_thread(); 244*cae20ba0SJohannes Berg 245*cae20ba0SJohannes Berg return 0; 246*cae20ba0SJohannes Berg } 247*cae20ba0SJohannes Berg 248*cae20ba0SJohannes Berg int ignore_sigio_fd(int fd) 249*cae20ba0SJohannes Berg { 250*cae20ba0SJohannes Berg int err; 251*cae20ba0SJohannes Berg 252*cae20ba0SJohannes Berg sigio_lock(); 253*cae20ba0SJohannes Berg err = __ignore_sigio_fd(fd); 254f206aabbSJeff Dike sigio_unlock(); 255*cae20ba0SJohannes Berg 25661232f2fSJeff Dike return err; 257f206aabbSJeff Dike } 258f206aabbSJeff Dike 259f206aabbSJeff Dike static struct pollfd *setup_initial_poll(int fd) 260f206aabbSJeff Dike { 261f206aabbSJeff Dike struct pollfd *p; 262f206aabbSJeff Dike 26343f5b308SJeff Dike p = uml_kmalloc(sizeof(struct pollfd), UM_GFP_KERNEL); 264f206aabbSJeff Dike if (p == NULL) { 265fee64d3cSJeff Dike printk(UM_KERN_ERR "setup_initial_poll : failed to allocate " 266fee64d3cSJeff Dike "poll\n"); 267f206aabbSJeff Dike return NULL; 268f206aabbSJeff Dike } 269f206aabbSJeff Dike *p = ((struct pollfd) { .fd = fd, 270f206aabbSJeff Dike .events = POLLIN, 271f206aabbSJeff Dike .revents = 0 }); 272f206aabbSJeff Dike return p; 273f206aabbSJeff Dike } 274f206aabbSJeff Dike 2758e64d96aSJeff Dike static void write_sigio_workaround(void) 276f206aabbSJeff Dike { 277f206aabbSJeff Dike struct pollfd *p; 278f206aabbSJeff Dike int err; 279f206aabbSJeff Dike int l_write_sigio_fds[2]; 280f206aabbSJeff Dike int l_sigio_private[2]; 281f206aabbSJeff Dike int l_write_sigio_pid; 282f206aabbSJeff Dike 283f206aabbSJeff Dike /* We call this *tons* of times - and most ones we must just fail. */ 284f206aabbSJeff Dike sigio_lock(); 285f206aabbSJeff Dike l_write_sigio_pid = write_sigio_pid; 286f206aabbSJeff Dike sigio_unlock(); 287f206aabbSJeff Dike 288f206aabbSJeff Dike if (l_write_sigio_pid != -1) 289f206aabbSJeff Dike return; 290f206aabbSJeff Dike 291f206aabbSJeff Dike err = os_pipe(l_write_sigio_fds, 1, 1); 292f206aabbSJeff Dike if (err < 0) { 293fee64d3cSJeff Dike printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 1 failed, " 294f206aabbSJeff Dike "err = %d\n", -err); 295f206aabbSJeff Dike return; 296f206aabbSJeff Dike } 297f206aabbSJeff Dike err = os_pipe(l_sigio_private, 1, 1); 298f206aabbSJeff Dike if (err < 0) { 299fee64d3cSJeff Dike printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 2 failed, " 300f206aabbSJeff Dike "err = %d\n", -err); 301f206aabbSJeff Dike goto out_close1; 302f206aabbSJeff Dike } 303f206aabbSJeff Dike 304f206aabbSJeff Dike p = setup_initial_poll(l_sigio_private[1]); 305f206aabbSJeff Dike if (!p) 306f206aabbSJeff Dike goto out_close2; 307f206aabbSJeff Dike 308f206aabbSJeff Dike sigio_lock(); 309f206aabbSJeff Dike 310fee64d3cSJeff Dike /* 311fee64d3cSJeff Dike * Did we race? Don't try to optimize this, please, it's not so likely 312fee64d3cSJeff Dike * to happen, and no more than once at the boot. 313fee64d3cSJeff Dike */ 314f206aabbSJeff Dike if (write_sigio_pid != -1) 3155f4e8fd0SJeff Dike goto out_free; 3165f4e8fd0SJeff Dike 3175f4e8fd0SJeff Dike current_poll = ((struct pollfds) { .poll = p, 3185f4e8fd0SJeff Dike .used = 1, 3195f4e8fd0SJeff Dike .size = 1 }); 3205f4e8fd0SJeff Dike 3215f4e8fd0SJeff Dike if (write_sigio_irq(l_write_sigio_fds[0])) 3225f4e8fd0SJeff Dike goto out_clear_poll; 3235f4e8fd0SJeff Dike 3245f4e8fd0SJeff Dike memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); 3255f4e8fd0SJeff Dike memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); 326f206aabbSJeff Dike 327f206aabbSJeff Dike write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, 328c4399016SJeff Dike CLONE_FILES | CLONE_VM, 329c4399016SJeff Dike &write_sigio_stack); 330f206aabbSJeff Dike 331f206aabbSJeff Dike if (write_sigio_pid < 0) 332f206aabbSJeff Dike goto out_clear; 333f206aabbSJeff Dike 334f206aabbSJeff Dike sigio_unlock(); 335f206aabbSJeff Dike return; 336f206aabbSJeff Dike 337f206aabbSJeff Dike out_clear: 338f206aabbSJeff Dike write_sigio_pid = -1; 3395f4e8fd0SJeff Dike write_sigio_fds[0] = -1; 3405f4e8fd0SJeff Dike write_sigio_fds[1] = -1; 3415f4e8fd0SJeff Dike sigio_private[0] = -1; 3425f4e8fd0SJeff Dike sigio_private[1] = -1; 3435f4e8fd0SJeff Dike out_clear_poll: 3445f4e8fd0SJeff Dike current_poll = ((struct pollfds) { .poll = NULL, 3455f4e8fd0SJeff Dike .size = 0, 3465f4e8fd0SJeff Dike .used = 0 }); 347f206aabbSJeff Dike out_free: 3485f4e8fd0SJeff Dike sigio_unlock(); 349e6fb54abSPaolo 'Blaisorblade' Giarrusso kfree(p); 350f206aabbSJeff Dike out_close2: 351f206aabbSJeff Dike close(l_sigio_private[0]); 352f206aabbSJeff Dike close(l_sigio_private[1]); 353f206aabbSJeff Dike out_close1: 354f206aabbSJeff Dike close(l_write_sigio_fds[0]); 355f206aabbSJeff Dike close(l_write_sigio_fds[1]); 356f206aabbSJeff Dike } 357f206aabbSJeff Dike 3582fccfcc0SJohannes Berg void sigio_broken(int fd) 3598e64d96aSJeff Dike { 36019bdf040SJeff Dike int err; 36119bdf040SJeff Dike 3628e64d96aSJeff Dike write_sigio_workaround(); 36319bdf040SJeff Dike 36419bdf040SJeff Dike sigio_lock(); 36519bdf040SJeff Dike err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1); 36604a51e66SJeff Dike if (err) { 367fee64d3cSJeff Dike printk(UM_KERN_ERR "maybe_sigio_broken - failed to add pollfd " 368fee64d3cSJeff Dike "for descriptor %d\n", fd); 36919bdf040SJeff Dike goto out; 37004a51e66SJeff Dike } 371838e56a1SJeff Dike 37219bdf040SJeff Dike all_sigio_fds.poll[all_sigio_fds.used++] = 37319bdf040SJeff Dike ((struct pollfd) { .fd = fd, 3742fccfcc0SJohannes Berg .events = POLLIN, 37519bdf040SJeff Dike .revents = 0 }); 37619bdf040SJeff Dike out: 37719bdf040SJeff Dike sigio_unlock(); 3788e64d96aSJeff Dike } 3798e64d96aSJeff Dike 3805d33e4d7SJeff Dike /* Changed during early boot */ 3815d33e4d7SJeff Dike static int pty_output_sigio; 3825d33e4d7SJeff Dike 3832fccfcc0SJohannes Berg void maybe_sigio_broken(int fd) 3845d33e4d7SJeff Dike { 3855d33e4d7SJeff Dike if (!isatty(fd)) 3865d33e4d7SJeff Dike return; 3875d33e4d7SJeff Dike 3882fccfcc0SJohannes Berg if (pty_output_sigio) 3895d33e4d7SJeff Dike return; 3905d33e4d7SJeff Dike 3912fccfcc0SJohannes Berg sigio_broken(fd); 3925d33e4d7SJeff Dike } 3935d33e4d7SJeff Dike 39429ac1c21SJeff Dike static void sigio_cleanup(void) 395f206aabbSJeff Dike { 396c4399016SJeff Dike if (write_sigio_pid == -1) 397c4399016SJeff Dike return; 398c4399016SJeff Dike 399f206aabbSJeff Dike os_kill_process(write_sigio_pid, 1); 400c4399016SJeff Dike free_stack(write_sigio_stack, 0); 401f206aabbSJeff Dike write_sigio_pid = -1; 402f206aabbSJeff Dike } 40329ac1c21SJeff Dike 40429ac1c21SJeff Dike __uml_exitcall(sigio_cleanup); 405c65badbdSJeff Dike 406c65badbdSJeff Dike /* Used as a flag during SIGIO testing early in boot */ 4075d33e4d7SJeff Dike static int got_sigio; 408c65badbdSJeff Dike 409c65badbdSJeff Dike static void __init handler(int sig) 410c65badbdSJeff Dike { 411c65badbdSJeff Dike got_sigio = 1; 412c65badbdSJeff Dike } 413c65badbdSJeff Dike 414c65badbdSJeff Dike struct openpty_arg { 415c65badbdSJeff Dike int master; 416c65badbdSJeff Dike int slave; 417c65badbdSJeff Dike int err; 418c65badbdSJeff Dike }; 419c65badbdSJeff Dike 420c65badbdSJeff Dike static void openpty_cb(void *arg) 421c65badbdSJeff Dike { 422c65badbdSJeff Dike struct openpty_arg *info = arg; 423c65badbdSJeff Dike 424c65badbdSJeff Dike info->err = 0; 425c65badbdSJeff Dike if (openpty(&info->master, &info->slave, NULL, NULL, NULL)) 426c65badbdSJeff Dike info->err = -errno; 427c65badbdSJeff Dike } 428c65badbdSJeff Dike 429c65badbdSJeff Dike static int async_pty(int master, int slave) 430c65badbdSJeff Dike { 431c65badbdSJeff Dike int flags; 432c65badbdSJeff Dike 433c65badbdSJeff Dike flags = fcntl(master, F_GETFL); 434c65badbdSJeff Dike if (flags < 0) 435c65badbdSJeff Dike return -errno; 436c65badbdSJeff Dike 437c65badbdSJeff Dike if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) || 438c65badbdSJeff Dike (fcntl(master, F_SETOWN, os_getpid()) < 0)) 439c65badbdSJeff Dike return -errno; 440c65badbdSJeff Dike 441c65badbdSJeff Dike if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)) 442c65badbdSJeff Dike return -errno; 443c65badbdSJeff Dike 444c0a9290eSWANG Cong return 0; 445c65badbdSJeff Dike } 446c65badbdSJeff Dike 447c65badbdSJeff Dike static void __init check_one_sigio(void (*proc)(int, int)) 448c65badbdSJeff Dike { 449c65badbdSJeff Dike struct sigaction old, new; 450c65badbdSJeff Dike struct openpty_arg pty = { .master = -1, .slave = -1 }; 451c65badbdSJeff Dike int master, slave, err; 452c65badbdSJeff Dike 453c65badbdSJeff Dike initial_thread_cb(openpty_cb, &pty); 454c65badbdSJeff Dike if (pty.err) { 455fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n", 456fee64d3cSJeff Dike -pty.err); 457c65badbdSJeff Dike return; 458c65badbdSJeff Dike } 459c65badbdSJeff Dike 460c65badbdSJeff Dike master = pty.master; 461c65badbdSJeff Dike slave = pty.slave; 462c65badbdSJeff Dike 463c65badbdSJeff Dike if ((master == -1) || (slave == -1)) { 464fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio failed to allocate a " 465fee64d3cSJeff Dike "pty\n"); 466c65badbdSJeff Dike return; 467c65badbdSJeff Dike } 468c65badbdSJeff Dike 469c65badbdSJeff Dike /* Not now, but complain so we now where we failed. */ 470c65badbdSJeff Dike err = raw(master); 471fee64d3cSJeff Dike if (err < 0) { 472fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n", 473fee64d3cSJeff Dike -err); 474fee64d3cSJeff Dike return; 475fee64d3cSJeff Dike } 476c65badbdSJeff Dike 477c65badbdSJeff Dike err = async_pty(master, slave); 478fee64d3cSJeff Dike if (err < 0) { 479fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, " 480fee64d3cSJeff Dike "err = %d\n", -err); 481fee64d3cSJeff Dike return; 482fee64d3cSJeff Dike } 483c65badbdSJeff Dike 484fee64d3cSJeff Dike if (sigaction(SIGIO, NULL, &old) < 0) { 485fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, " 486fee64d3cSJeff Dike "errno = %d\n", errno); 487fee64d3cSJeff Dike return; 488fee64d3cSJeff Dike } 489fee64d3cSJeff Dike 490c65badbdSJeff Dike new = old; 491c65badbdSJeff Dike new.sa_handler = handler; 492fee64d3cSJeff Dike if (sigaction(SIGIO, &new, NULL) < 0) { 493fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, " 494fee64d3cSJeff Dike "errno = %d\n", errno); 495fee64d3cSJeff Dike return; 496fee64d3cSJeff Dike } 497c65badbdSJeff Dike 498c65badbdSJeff Dike got_sigio = 0; 499c65badbdSJeff Dike (*proc)(master, slave); 500c65badbdSJeff Dike 501c65badbdSJeff Dike close(master); 502c65badbdSJeff Dike close(slave); 503c65badbdSJeff Dike 504c65badbdSJeff Dike if (sigaction(SIGIO, &old, NULL) < 0) 505fee64d3cSJeff Dike printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, " 506fee64d3cSJeff Dike "errno = %d\n", errno); 507c65badbdSJeff Dike } 508c65badbdSJeff Dike 509c65badbdSJeff Dike static void tty_output(int master, int slave) 510c65badbdSJeff Dike { 511c65badbdSJeff Dike int n; 512c65badbdSJeff Dike char buf[512]; 513c65badbdSJeff Dike 514fee64d3cSJeff Dike printk(UM_KERN_INFO "Checking that host ptys support output SIGIO..."); 515c65badbdSJeff Dike 516c65badbdSJeff Dike memset(buf, 0, sizeof(buf)); 517c65badbdSJeff Dike 518a61f334fSJeff Dike while (write(master, buf, sizeof(buf)) > 0) ; 519c65badbdSJeff Dike if (errno != EAGAIN) 520fee64d3cSJeff Dike printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n", 521fee64d3cSJeff Dike errno); 5225d33e4d7SJeff Dike while (((n = read(slave, buf, sizeof(buf))) > 0) && 5235d33e4d7SJeff Dike !({ barrier(); got_sigio; })) 524fee64d3cSJeff Dike ; 525c65badbdSJeff Dike 526c65badbdSJeff Dike if (got_sigio) { 527fee64d3cSJeff Dike printk(UM_KERN_CONT "Yes\n"); 528c65badbdSJeff Dike pty_output_sigio = 1; 529fee64d3cSJeff Dike } else if (n == -EAGAIN) 530fee64d3cSJeff Dike printk(UM_KERN_CONT "No, enabling workaround\n"); 531fee64d3cSJeff Dike else 532fee64d3cSJeff Dike printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n); 533c65badbdSJeff Dike } 534c65badbdSJeff Dike 53599764fa4SWANG Cong static void __init check_sigio(void) 536c65badbdSJeff Dike { 537fee64d3cSJeff Dike if ((access("/dev/ptmx", R_OK) < 0) && 538fee64d3cSJeff Dike (access("/dev/ptyp0", R_OK) < 0)) { 539fee64d3cSJeff Dike printk(UM_KERN_WARNING "No pseudo-terminals available - " 540fee64d3cSJeff Dike "skipping pty SIGIO check\n"); 541c65badbdSJeff Dike return; 542c65badbdSJeff Dike } 543c65badbdSJeff Dike check_one_sigio(tty_output); 544c65badbdSJeff Dike } 545c65badbdSJeff Dike 546c65badbdSJeff Dike /* Here because it only does the SIGIO testing for now */ 547c65badbdSJeff Dike void __init os_check_bugs(void) 548c65badbdSJeff Dike { 549c65badbdSJeff Dike check_sigio(); 550c65badbdSJeff Dike } 551