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