File Index
Files and directories
Sign in
FILE

sigio.c

File preview with syntax highlighting.

linux-6.19.14/arch/um/os-Linux/sigio.c C
  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 <sys/epoll.h>
 15#include <asm/unistd.h>
 16#include <kern_util.h>
 17#include <init.h>
 18#include <os.h>
 19#include <sigio.h>
 20#include <um_malloc.h>
 21
 22/*
 23 * Protected by sigio_lock(), also used by sigio_cleanup, which is an
 24 * exitcall.
 25 */
 26static struct os_helper_thread *write_sigio_td;
 27
 28static int epollfd = -1;
 29
 30#define MAX_EPOLL_EVENTS 64
 31
 32static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
 33
 34static void *write_sigio_thread(void *unused)
 35{
 36	int pid = getpid();
 37	int r;
 38
 39	os_fix_helper_thread_signals();
 40
 41	while (1) {
 42		r = epoll_wait(epollfd, epoll_events, MAX_EPOLL_EVENTS, -1);
 43		if (r < 0) {
 44			if (errno == EINTR)
 45				continue;
 46			printk(UM_KERN_ERR "%s: epoll_wait failed, errno = %d\n",
 47			       __func__, errno);
 48		}
 49
 50		CATCH_EINTR(r = syscall(__NR_tgkill, pid, pid, SIGIO));
 51		if (r < 0)
 52			printk(UM_KERN_ERR "%s: tgkill failed, errno = %d\n",
 53			       __func__, errno);
 54	}
 55
 56	return NULL;
 57}
 58
 59int __add_sigio_fd(int fd)
 60{
 61	struct epoll_event event = {
 62		.data.fd = fd,
 63		.events = EPOLLIN | EPOLLET,
 64	};
 65	int r;
 66
 67	CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event));
 68	return r < 0 ? -errno : 0;
 69}
 70
 71int add_sigio_fd(int fd)
 72{
 73	int err;
 74
 75	sigio_lock();
 76	err = __add_sigio_fd(fd);
 77	sigio_unlock();
 78
 79	return err;
 80}
 81
 82int __ignore_sigio_fd(int fd)
 83{
 84	struct epoll_event event;
 85	int r;
 86
 87	CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event));
 88	return r < 0 ? -errno : 0;
 89}
 90
 91int ignore_sigio_fd(int fd)
 92{
 93	int err;
 94
 95	sigio_lock();
 96	err = __ignore_sigio_fd(fd);
 97	sigio_unlock();
 98
 99	return err;
100}
101
102static void write_sigio_workaround(void)
103{
104	int err;
105
106	sigio_lock();
107	if (write_sigio_td)
108		goto out;
109
110	epollfd = epoll_create(MAX_EPOLL_EVENTS);
111	if (epollfd < 0) {
112		printk(UM_KERN_ERR "%s: epoll_create failed, errno = %d\n",
113		       __func__, errno);
114		goto out;
115	}
116
117	err = os_run_helper_thread(&write_sigio_td, write_sigio_thread, NULL);
118	if (err < 0) {
119		printk(UM_KERN_ERR "%s: os_run_helper_thread failed, errno = %d\n",
120		       __func__, -err);
121		close(epollfd);
122		epollfd = -1;
123		goto out;
124	}
125
126out:
127	sigio_unlock();
128}
129
130void sigio_broken(void)
131{
132	write_sigio_workaround();
133}
134
135/* Changed during early boot */
136static int pty_output_sigio;
137
138void maybe_sigio_broken(int fd)
139{
140	if (!isatty(fd))
141		return;
142
143	if (pty_output_sigio)
144		return;
145
146	sigio_broken();
147}
148
149static void sigio_cleanup(void)
150{
151	if (!write_sigio_td)
152		return;
153
154	os_kill_helper_thread(write_sigio_td);
155	write_sigio_td = NULL;
156}
157
158__uml_exitcall(sigio_cleanup);
159
160/* Used as a flag during SIGIO testing early in boot */
161static int got_sigio;
162
163static void __init handler(int sig)
164{
165	got_sigio = 1;
166}
167
168struct openpty_arg {
169	int master;
170	int slave;
171	int err;
172};
173
174static void openpty_cb(void *arg)
175{
176	struct openpty_arg *info = arg;
177
178	info->err = 0;
179	if (openpty(&info->master, &info->slave, NULL, NULL, NULL))
180		info->err = -errno;
181}
182
183static int async_pty(int master, int slave)
184{
185	int flags;
186
187	flags = fcntl(master, F_GETFL);
188	if (flags < 0)
189		return -errno;
190
191	if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
192	    (fcntl(master, F_SETOWN, os_getpid()) < 0))
193		return -errno;
194
195	if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0))
196		return -errno;
197
198	return 0;
199}
200
201static void __init check_one_sigio(void (*proc)(int, int))
202{
203	struct sigaction old, new;
204	struct openpty_arg pty = { .master = -1, .slave = -1 };
205	int master, slave, err;
206
207	initial_thread_cb(openpty_cb, &pty);
208	if (pty.err) {
209		printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n",
210		       -pty.err);
211		return;
212	}
213
214	master = pty.master;
215	slave = pty.slave;
216
217	if ((master == -1) || (slave == -1)) {
218		printk(UM_KERN_ERR "check_one_sigio failed to allocate a "
219		       "pty\n");
220		return;
221	}
222
223	/* Not now, but complain so we now where we failed. */
224	err = raw(master);
225	if (err < 0) {
226		printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n",
227		      -err);
228		return;
229	}
230
231	err = async_pty(master, slave);
232	if (err < 0) {
233		printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, "
234		       "err = %d\n", -err);
235		return;
236	}
237
238	if (sigaction(SIGIO, NULL, &old) < 0) {
239		printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, "
240		       "errno = %d\n", errno);
241		return;
242	}
243
244	new = old;
245	new.sa_handler = handler;
246	if (sigaction(SIGIO, &new, NULL) < 0) {
247		printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, "
248		       "errno = %d\n", errno);
249		return;
250	}
251
252	got_sigio = 0;
253	(*proc)(master, slave);
254
255	close(master);
256	close(slave);
257
258	if (sigaction(SIGIO, &old, NULL) < 0)
259		printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, "
260		       "errno = %d\n", errno);
261}
262
263static void tty_output(int master, int slave)
264{
265	int n;
266	char buf[512];
267
268	printk(UM_KERN_INFO "Checking that host ptys support output SIGIO...");
269
270	memset(buf, 0, sizeof(buf));
271
272	while (write(master, buf, sizeof(buf)) > 0) ;
273	if (errno != EAGAIN)
274		printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
275		       errno);
276	while (((n = read(slave, buf, sizeof(buf))) > 0) &&
277	       !({ barrier(); got_sigio; }))
278		;
279
280	if (got_sigio) {
281		printk(UM_KERN_CONT "Yes\n");
282		pty_output_sigio = 1;
283	} else if (n == -EAGAIN)
284		printk(UM_KERN_CONT "No, enabling workaround\n");
285	else
286		printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n);
287}
288
289static void __init check_sigio(void)
290{
291	if ((access("/dev/ptmx", R_OK) < 0) &&
292	    (access("/dev/ptyp0", R_OK) < 0)) {
293		printk(UM_KERN_WARNING "No pseudo-terminals available - "
294		       "skipping pty SIGIO check\n");
295		return;
296	}
297	check_one_sigio(tty_output);
298}
299
300/* Here because it only does the SIGIO testing for now */
301void __init os_check_bugs(void)
302{
303	check_sigio();
304}
~/ Read-only file preview.