162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <unistd.h> 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <poll.h> 1062306a36Sopenharmony_ci#include <pty.h> 1162306a36Sopenharmony_ci#include <sched.h> 1262306a36Sopenharmony_ci#include <signal.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <kern_util.h> 1562306a36Sopenharmony_ci#include <init.h> 1662306a36Sopenharmony_ci#include <os.h> 1762306a36Sopenharmony_ci#include <sigio.h> 1862306a36Sopenharmony_ci#include <um_malloc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Protected by sigio_lock(), also used by sigio_cleanup, which is an 2262306a36Sopenharmony_ci * exitcall. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic int write_sigio_pid = -1; 2562306a36Sopenharmony_cistatic unsigned long write_sigio_stack; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * These arrays are initialized before the sigio thread is started, and 2962306a36Sopenharmony_ci * the descriptors closed after it is killed. So, it can't see them change. 3062306a36Sopenharmony_ci * On the UML side, they are changed under the sigio_lock. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define SIGIO_FDS_INIT {-1, -1} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int write_sigio_fds[2] = SIGIO_FDS_INIT; 3562306a36Sopenharmony_cistatic int sigio_private[2] = SIGIO_FDS_INIT; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct pollfds { 3862306a36Sopenharmony_ci struct pollfd *poll; 3962306a36Sopenharmony_ci int size; 4062306a36Sopenharmony_ci int used; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Protected by sigio_lock(). Used by the sigio thread, but the UML thread 4562306a36Sopenharmony_ci * synchronizes with it. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic struct pollfds current_poll; 4862306a36Sopenharmony_cistatic struct pollfds next_poll; 4962306a36Sopenharmony_cistatic struct pollfds all_sigio_fds; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int write_sigio_thread(void *unused) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct pollfds *fds, tmp; 5462306a36Sopenharmony_ci struct pollfd *p; 5562306a36Sopenharmony_ci int i, n, respond_fd; 5662306a36Sopenharmony_ci char c; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci os_fix_helper_signals(); 5962306a36Sopenharmony_ci fds = ¤t_poll; 6062306a36Sopenharmony_ci while (1) { 6162306a36Sopenharmony_ci n = poll(fds->poll, fds->used, -1); 6262306a36Sopenharmony_ci if (n < 0) { 6362306a36Sopenharmony_ci if (errno == EINTR) 6462306a36Sopenharmony_ci continue; 6562306a36Sopenharmony_ci printk(UM_KERN_ERR "write_sigio_thread : poll returned " 6662306a36Sopenharmony_ci "%d, errno = %d\n", n, errno); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci for (i = 0; i < fds->used; i++) { 6962306a36Sopenharmony_ci p = &fds->poll[i]; 7062306a36Sopenharmony_ci if (p->revents == 0) 7162306a36Sopenharmony_ci continue; 7262306a36Sopenharmony_ci if (p->fd == sigio_private[1]) { 7362306a36Sopenharmony_ci CATCH_EINTR(n = read(sigio_private[1], &c, 7462306a36Sopenharmony_ci sizeof(c))); 7562306a36Sopenharmony_ci if (n != sizeof(c)) 7662306a36Sopenharmony_ci printk(UM_KERN_ERR 7762306a36Sopenharmony_ci "write_sigio_thread : " 7862306a36Sopenharmony_ci "read on socket failed, " 7962306a36Sopenharmony_ci "err = %d\n", errno); 8062306a36Sopenharmony_ci tmp = current_poll; 8162306a36Sopenharmony_ci current_poll = next_poll; 8262306a36Sopenharmony_ci next_poll = tmp; 8362306a36Sopenharmony_ci respond_fd = sigio_private[1]; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci else { 8662306a36Sopenharmony_ci respond_fd = write_sigio_fds[1]; 8762306a36Sopenharmony_ci fds->used--; 8862306a36Sopenharmony_ci memmove(&fds->poll[i], &fds->poll[i + 1], 8962306a36Sopenharmony_ci (fds->used - i) * sizeof(*fds->poll)); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci CATCH_EINTR(n = write(respond_fd, &c, sizeof(c))); 9362306a36Sopenharmony_ci if (n != sizeof(c)) 9462306a36Sopenharmony_ci printk(UM_KERN_ERR "write_sigio_thread : " 9562306a36Sopenharmony_ci "write on socket failed, err = %d\n", 9662306a36Sopenharmony_ci errno); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int need_poll(struct pollfds *polls, int n) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct pollfd *new; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (n <= polls->size) 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci new = uml_kmalloc(n * sizeof(struct pollfd), UM_GFP_ATOMIC); 11162306a36Sopenharmony_ci if (new == NULL) { 11262306a36Sopenharmony_ci printk(UM_KERN_ERR "need_poll : failed to allocate new " 11362306a36Sopenharmony_ci "pollfds\n"); 11462306a36Sopenharmony_ci return -ENOMEM; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci memcpy(new, polls->poll, polls->used * sizeof(struct pollfd)); 11862306a36Sopenharmony_ci kfree(polls->poll); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci polls->poll = new; 12162306a36Sopenharmony_ci polls->size = n; 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * Must be called with sigio_lock held, because it's needed by the marked 12762306a36Sopenharmony_ci * critical section. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic void update_thread(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci unsigned long flags; 13262306a36Sopenharmony_ci int n; 13362306a36Sopenharmony_ci char c; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci flags = um_set_signals_trace(0); 13662306a36Sopenharmony_ci CATCH_EINTR(n = write(sigio_private[0], &c, sizeof(c))); 13762306a36Sopenharmony_ci if (n != sizeof(c)) { 13862306a36Sopenharmony_ci printk(UM_KERN_ERR "update_thread : write failed, err = %d\n", 13962306a36Sopenharmony_ci errno); 14062306a36Sopenharmony_ci goto fail; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci CATCH_EINTR(n = read(sigio_private[0], &c, sizeof(c))); 14462306a36Sopenharmony_ci if (n != sizeof(c)) { 14562306a36Sopenharmony_ci printk(UM_KERN_ERR "update_thread : read failed, err = %d\n", 14662306a36Sopenharmony_ci errno); 14762306a36Sopenharmony_ci goto fail; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci um_set_signals_trace(flags); 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci fail: 15362306a36Sopenharmony_ci /* Critical section start */ 15462306a36Sopenharmony_ci if (write_sigio_pid != -1) { 15562306a36Sopenharmony_ci os_kill_process(write_sigio_pid, 1); 15662306a36Sopenharmony_ci free_stack(write_sigio_stack, 0); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci write_sigio_pid = -1; 15962306a36Sopenharmony_ci close(sigio_private[0]); 16062306a36Sopenharmony_ci close(sigio_private[1]); 16162306a36Sopenharmony_ci close(write_sigio_fds[0]); 16262306a36Sopenharmony_ci close(write_sigio_fds[1]); 16362306a36Sopenharmony_ci /* Critical section end */ 16462306a36Sopenharmony_ci um_set_signals_trace(flags); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint __add_sigio_fd(int fd) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct pollfd *p; 17062306a36Sopenharmony_ci int err, i, n; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (i = 0; i < all_sigio_fds.used; i++) { 17362306a36Sopenharmony_ci if (all_sigio_fds.poll[i].fd == fd) 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci if (i == all_sigio_fds.used) 17762306a36Sopenharmony_ci return -ENOSPC; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci p = &all_sigio_fds.poll[i]; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (i = 0; i < current_poll.used; i++) { 18262306a36Sopenharmony_ci if (current_poll.poll[i].fd == fd) 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci n = current_poll.used; 18762306a36Sopenharmony_ci err = need_poll(&next_poll, n + 1); 18862306a36Sopenharmony_ci if (err) 18962306a36Sopenharmony_ci return err; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci memcpy(next_poll.poll, current_poll.poll, 19262306a36Sopenharmony_ci current_poll.used * sizeof(struct pollfd)); 19362306a36Sopenharmony_ci next_poll.poll[n] = *p; 19462306a36Sopenharmony_ci next_poll.used = n + 1; 19562306a36Sopenharmony_ci update_thread(); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciint add_sigio_fd(int fd) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci sigio_lock(); 20662306a36Sopenharmony_ci err = __add_sigio_fd(fd); 20762306a36Sopenharmony_ci sigio_unlock(); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return err; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint __ignore_sigio_fd(int fd) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct pollfd *p; 21562306a36Sopenharmony_ci int err, i, n = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * This is called from exitcalls elsewhere in UML - if 21962306a36Sopenharmony_ci * sigio_cleanup has already run, then update_thread will hang 22062306a36Sopenharmony_ci * or fail because the thread is no longer running. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci if (write_sigio_pid == -1) 22362306a36Sopenharmony_ci return -EIO; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci for (i = 0; i < current_poll.used; i++) { 22662306a36Sopenharmony_ci if (current_poll.poll[i].fd == fd) 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if (i == current_poll.used) 23062306a36Sopenharmony_ci return -ENOENT; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci err = need_poll(&next_poll, current_poll.used - 1); 23362306a36Sopenharmony_ci if (err) 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = 0; i < current_poll.used; i++) { 23762306a36Sopenharmony_ci p = ¤t_poll.poll[i]; 23862306a36Sopenharmony_ci if (p->fd != fd) 23962306a36Sopenharmony_ci next_poll.poll[n++] = *p; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci next_poll.used = current_poll.used - 1; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci update_thread(); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciint ignore_sigio_fd(int fd) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int err; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci sigio_lock(); 25362306a36Sopenharmony_ci err = __ignore_sigio_fd(fd); 25462306a36Sopenharmony_ci sigio_unlock(); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct pollfd *setup_initial_poll(int fd) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct pollfd *p; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci p = uml_kmalloc(sizeof(struct pollfd), UM_GFP_KERNEL); 26462306a36Sopenharmony_ci if (p == NULL) { 26562306a36Sopenharmony_ci printk(UM_KERN_ERR "setup_initial_poll : failed to allocate " 26662306a36Sopenharmony_ci "poll\n"); 26762306a36Sopenharmony_ci return NULL; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci *p = ((struct pollfd) { .fd = fd, 27062306a36Sopenharmony_ci .events = POLLIN, 27162306a36Sopenharmony_ci .revents = 0 }); 27262306a36Sopenharmony_ci return p; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void write_sigio_workaround(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct pollfd *p; 27862306a36Sopenharmony_ci int err; 27962306a36Sopenharmony_ci int l_write_sigio_fds[2]; 28062306a36Sopenharmony_ci int l_sigio_private[2]; 28162306a36Sopenharmony_ci int l_write_sigio_pid; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* We call this *tons* of times - and most ones we must just fail. */ 28462306a36Sopenharmony_ci sigio_lock(); 28562306a36Sopenharmony_ci l_write_sigio_pid = write_sigio_pid; 28662306a36Sopenharmony_ci sigio_unlock(); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (l_write_sigio_pid != -1) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci err = os_pipe(l_write_sigio_fds, 1, 1); 29262306a36Sopenharmony_ci if (err < 0) { 29362306a36Sopenharmony_ci printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 1 failed, " 29462306a36Sopenharmony_ci "err = %d\n", -err); 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci err = os_pipe(l_sigio_private, 1, 1); 29862306a36Sopenharmony_ci if (err < 0) { 29962306a36Sopenharmony_ci printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 2 failed, " 30062306a36Sopenharmony_ci "err = %d\n", -err); 30162306a36Sopenharmony_ci goto out_close1; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci p = setup_initial_poll(l_sigio_private[1]); 30562306a36Sopenharmony_ci if (!p) 30662306a36Sopenharmony_ci goto out_close2; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci sigio_lock(); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * Did we race? Don't try to optimize this, please, it's not so likely 31262306a36Sopenharmony_ci * to happen, and no more than once at the boot. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (write_sigio_pid != -1) 31562306a36Sopenharmony_ci goto out_free; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci current_poll = ((struct pollfds) { .poll = p, 31862306a36Sopenharmony_ci .used = 1, 31962306a36Sopenharmony_ci .size = 1 }); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (write_sigio_irq(l_write_sigio_fds[0])) 32262306a36Sopenharmony_ci goto out_clear_poll; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); 32562306a36Sopenharmony_ci memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, 32862306a36Sopenharmony_ci CLONE_FILES | CLONE_VM, 32962306a36Sopenharmony_ci &write_sigio_stack); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (write_sigio_pid < 0) 33262306a36Sopenharmony_ci goto out_clear; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci sigio_unlock(); 33562306a36Sopenharmony_ci return; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciout_clear: 33862306a36Sopenharmony_ci write_sigio_pid = -1; 33962306a36Sopenharmony_ci write_sigio_fds[0] = -1; 34062306a36Sopenharmony_ci write_sigio_fds[1] = -1; 34162306a36Sopenharmony_ci sigio_private[0] = -1; 34262306a36Sopenharmony_ci sigio_private[1] = -1; 34362306a36Sopenharmony_ciout_clear_poll: 34462306a36Sopenharmony_ci current_poll = ((struct pollfds) { .poll = NULL, 34562306a36Sopenharmony_ci .size = 0, 34662306a36Sopenharmony_ci .used = 0 }); 34762306a36Sopenharmony_ciout_free: 34862306a36Sopenharmony_ci sigio_unlock(); 34962306a36Sopenharmony_ci kfree(p); 35062306a36Sopenharmony_ciout_close2: 35162306a36Sopenharmony_ci close(l_sigio_private[0]); 35262306a36Sopenharmony_ci close(l_sigio_private[1]); 35362306a36Sopenharmony_ciout_close1: 35462306a36Sopenharmony_ci close(l_write_sigio_fds[0]); 35562306a36Sopenharmony_ci close(l_write_sigio_fds[1]); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_civoid sigio_broken(int fd) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci int err; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci write_sigio_workaround(); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci sigio_lock(); 36562306a36Sopenharmony_ci err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1); 36662306a36Sopenharmony_ci if (err) { 36762306a36Sopenharmony_ci printk(UM_KERN_ERR "maybe_sigio_broken - failed to add pollfd " 36862306a36Sopenharmony_ci "for descriptor %d\n", fd); 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci all_sigio_fds.poll[all_sigio_fds.used++] = 37362306a36Sopenharmony_ci ((struct pollfd) { .fd = fd, 37462306a36Sopenharmony_ci .events = POLLIN, 37562306a36Sopenharmony_ci .revents = 0 }); 37662306a36Sopenharmony_ciout: 37762306a36Sopenharmony_ci sigio_unlock(); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* Changed during early boot */ 38162306a36Sopenharmony_cistatic int pty_output_sigio; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_civoid maybe_sigio_broken(int fd) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci if (!isatty(fd)) 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (pty_output_sigio) 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci sigio_broken(fd); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void sigio_cleanup(void) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci if (write_sigio_pid == -1) 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci os_kill_process(write_sigio_pid, 1); 40062306a36Sopenharmony_ci free_stack(write_sigio_stack, 0); 40162306a36Sopenharmony_ci write_sigio_pid = -1; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci__uml_exitcall(sigio_cleanup); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* Used as a flag during SIGIO testing early in boot */ 40762306a36Sopenharmony_cistatic int got_sigio; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void __init handler(int sig) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci got_sigio = 1; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistruct openpty_arg { 41562306a36Sopenharmony_ci int master; 41662306a36Sopenharmony_ci int slave; 41762306a36Sopenharmony_ci int err; 41862306a36Sopenharmony_ci}; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void openpty_cb(void *arg) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct openpty_arg *info = arg; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci info->err = 0; 42562306a36Sopenharmony_ci if (openpty(&info->master, &info->slave, NULL, NULL, NULL)) 42662306a36Sopenharmony_ci info->err = -errno; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int async_pty(int master, int slave) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci int flags; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci flags = fcntl(master, F_GETFL); 43462306a36Sopenharmony_ci if (flags < 0) 43562306a36Sopenharmony_ci return -errno; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) || 43862306a36Sopenharmony_ci (fcntl(master, F_SETOWN, os_getpid()) < 0)) 43962306a36Sopenharmony_ci return -errno; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)) 44262306a36Sopenharmony_ci return -errno; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void __init check_one_sigio(void (*proc)(int, int)) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct sigaction old, new; 45062306a36Sopenharmony_ci struct openpty_arg pty = { .master = -1, .slave = -1 }; 45162306a36Sopenharmony_ci int master, slave, err; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci initial_thread_cb(openpty_cb, &pty); 45462306a36Sopenharmony_ci if (pty.err) { 45562306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n", 45662306a36Sopenharmony_ci -pty.err); 45762306a36Sopenharmony_ci return; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci master = pty.master; 46162306a36Sopenharmony_ci slave = pty.slave; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if ((master == -1) || (slave == -1)) { 46462306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio failed to allocate a " 46562306a36Sopenharmony_ci "pty\n"); 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Not now, but complain so we now where we failed. */ 47062306a36Sopenharmony_ci err = raw(master); 47162306a36Sopenharmony_ci if (err < 0) { 47262306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n", 47362306a36Sopenharmony_ci -err); 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci err = async_pty(master, slave); 47862306a36Sopenharmony_ci if (err < 0) { 47962306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, " 48062306a36Sopenharmony_ci "err = %d\n", -err); 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (sigaction(SIGIO, NULL, &old) < 0) { 48562306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, " 48662306a36Sopenharmony_ci "errno = %d\n", errno); 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci new = old; 49162306a36Sopenharmony_ci new.sa_handler = handler; 49262306a36Sopenharmony_ci if (sigaction(SIGIO, &new, NULL) < 0) { 49362306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, " 49462306a36Sopenharmony_ci "errno = %d\n", errno); 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci got_sigio = 0; 49962306a36Sopenharmony_ci (*proc)(master, slave); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci close(master); 50262306a36Sopenharmony_ci close(slave); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (sigaction(SIGIO, &old, NULL) < 0) 50562306a36Sopenharmony_ci printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, " 50662306a36Sopenharmony_ci "errno = %d\n", errno); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void tty_output(int master, int slave) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int n; 51262306a36Sopenharmony_ci char buf[512]; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci printk(UM_KERN_INFO "Checking that host ptys support output SIGIO..."); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci memset(buf, 0, sizeof(buf)); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci while (write(master, buf, sizeof(buf)) > 0) ; 51962306a36Sopenharmony_ci if (errno != EAGAIN) 52062306a36Sopenharmony_ci printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n", 52162306a36Sopenharmony_ci errno); 52262306a36Sopenharmony_ci while (((n = read(slave, buf, sizeof(buf))) > 0) && 52362306a36Sopenharmony_ci !({ barrier(); got_sigio; })) 52462306a36Sopenharmony_ci ; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (got_sigio) { 52762306a36Sopenharmony_ci printk(UM_KERN_CONT "Yes\n"); 52862306a36Sopenharmony_ci pty_output_sigio = 1; 52962306a36Sopenharmony_ci } else if (n == -EAGAIN) 53062306a36Sopenharmony_ci printk(UM_KERN_CONT "No, enabling workaround\n"); 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void __init check_sigio(void) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci if ((access("/dev/ptmx", R_OK) < 0) && 53862306a36Sopenharmony_ci (access("/dev/ptyp0", R_OK) < 0)) { 53962306a36Sopenharmony_ci printk(UM_KERN_WARNING "No pseudo-terminals available - " 54062306a36Sopenharmony_ci "skipping pty SIGIO check\n"); 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci check_one_sigio(tty_output); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* Here because it only does the SIGIO testing for now */ 54762306a36Sopenharmony_civoid __init os_check_bugs(void) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci check_sigio(); 55062306a36Sopenharmony_ci} 551