Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/um/os-Linux/sigio.c
26424 views
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
*/
26
static struct os_helper_thread *write_sigio_td;
27
28
static int epollfd = -1;
29
30
#define MAX_EPOLL_EVENTS 64
31
32
static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
33
34
static 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
59
int __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
71
int 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
82
int __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
91
int 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
102
static 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
126
out:
127
sigio_unlock();
128
}
129
130
void sigio_broken(void)
131
{
132
write_sigio_workaround();
133
}
134
135
/* Changed during early boot */
136
static int pty_output_sigio;
137
138
void 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
149
static 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 */
161
static int got_sigio;
162
163
static void __init handler(int sig)
164
{
165
got_sigio = 1;
166
}
167
168
struct openpty_arg {
169
int master;
170
int slave;
171
int err;
172
};
173
174
static 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
183
static 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
201
static 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
263
static 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
289
static 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 */
301
void __init os_check_bugs(void)
302
{
303
check_sigio();
304
}
305
306