diff options
author | Alexander Kanavin <alexander.kanavin@linux.intel.com> | 2017-09-22 18:18:46 +0300 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-09-25 14:14:17 +0100 |
commit | 68fb77d2f6057a1c2c2a299bb18915c40d65e756 (patch) | |
tree | 07645af8451e99f68d1fa4f1548886c923be0e3c /meta/recipes-devtools | |
parent | 4862e21fa2e673ac62db4f8a5c5b1acbecb3a9ec (diff) | |
download | poky-68fb77d2f6057a1c2c2a299bb18915c40d65e756.tar.gz |
pseudo: use epoll API on Linux
The idea came up here:
https://bugzilla.yoctoproject.org/show_bug.cgi?id=11309
and here:
http://lists.openembedded.org/pipermail/openembedded-core/2017-August/141491.html
(From OE-Core rev: 7fb4661b4e4c839b60975c3b8b0b163e1f84ab2e)
Signed-off-by: Alexander Kanavin <alexander.kanavin@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools')
-rw-r--r-- | meta/recipes-devtools/pseudo/files/0001-Use-epoll-API-on-Linux.patch | 292 | ||||
-rw-r--r-- | meta/recipes-devtools/pseudo/pseudo_1.8.2.bb | 1 |
2 files changed, 293 insertions, 0 deletions
diff --git a/meta/recipes-devtools/pseudo/files/0001-Use-epoll-API-on-Linux.patch b/meta/recipes-devtools/pseudo/files/0001-Use-epoll-API-on-Linux.patch new file mode 100644 index 0000000000..42557b17a7 --- /dev/null +++ b/meta/recipes-devtools/pseudo/files/0001-Use-epoll-API-on-Linux.patch | |||
@@ -0,0 +1,292 @@ | |||
1 | From 9e407e0be01695e7b927f5820ade87ee9602c248 Mon Sep 17 00:00:00 2001 | ||
2 | From: Alexander Kanavin <alex.kanavin@gmail.com> | ||
3 | Date: Fri, 15 Sep 2017 17:00:14 +0300 | ||
4 | Subject: [PATCH] Use epoll API on Linux | ||
5 | |||
6 | Also a couple of other modifications due to epoll having | ||
7 | a different approach to how the working set of fds is defined | ||
8 | and used: | ||
9 | 1) open_client() returns an index into the array of clients | ||
10 | 2) close_client() has a protection against being called twice | ||
11 | with the same client (which would mess up the active_clients | ||
12 | counter) | ||
13 | |||
14 | Upstream-Status: Submitted [Seebs CC'd by email] | ||
15 | Signed-off-by: Alexander Kanavin <alex.kanavin@gmail.com> | ||
16 | |||
17 | --- | ||
18 | enums/exit_status.in | 3 + | ||
19 | pseudo_server.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++- | ||
20 | 2 files changed, 190 insertions(+), 2 deletions(-) | ||
21 | |||
22 | diff --git a/enums/exit_status.in b/enums/exit_status.in | ||
23 | index 6be44d3..88f94cd 100644 | ||
24 | --- a/enums/exit_status.in | ||
25 | +++ b/enums/exit_status.in | ||
26 | @@ -18,3 +18,6 @@ listen_fd, "server loop had no valid listen fd" | ||
27 | pseudo_loaded, "server couldn't get out of pseudo environment" | ||
28 | pseudo_prefix, "couldn't get valid pseudo prefix" | ||
29 | pseudo_invocation, "invalid server command arguments" | ||
30 | +epoll_create, "epoll_create() failed" | ||
31 | +epoll_ctl, "epoll_ctl() failed" | ||
32 | + | ||
33 | diff --git a/pseudo_server.c b/pseudo_server.c | ||
34 | index ff16efd..14d34de 100644 | ||
35 | --- a/pseudo_server.c | ||
36 | +++ b/pseudo_server.c | ||
37 | @@ -40,6 +40,12 @@ | ||
38 | #include "pseudo_client.h" | ||
39 | #include "pseudo_db.h" | ||
40 | |||
41 | +// This has to come after pseudo includes, as that's where PSEUDO_PORT defines are | ||
42 | +#ifdef PSEUDO_PORT_LINUX | ||
43 | +#include <sys/epoll.h> | ||
44 | +#endif | ||
45 | + | ||
46 | + | ||
47 | static int listen_fd = -1; | ||
48 | |||
49 | typedef struct { | ||
50 | @@ -59,6 +65,7 @@ static int active_clients = 0, highest_client = 0, max_clients = 0; | ||
51 | |||
52 | #define LOOP_DELAY 2 | ||
53 | #define DEFAULT_PSEUDO_SERVER_TIMEOUT 30 | ||
54 | +#define EPOLL_MAX_EVENTS 10 | ||
55 | int pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT; | ||
56 | static int die_peacefully = 0; | ||
57 | static int die_forcefully = 0; | ||
58 | @@ -80,6 +87,9 @@ quit_now(int signal) { | ||
59 | static int messages = 0, responses = 0; | ||
60 | static struct timeval message_time = { .tv_sec = 0 }; | ||
61 | |||
62 | +#ifdef PSEUDO_PORT_LINUX | ||
63 | +static void pseudo_server_loop_epoll(void); | ||
64 | +#endif | ||
65 | static void pseudo_server_loop(void); | ||
66 | |||
67 | /* helper function to make a directory, just like mkdir -p. | ||
68 | @@ -369,12 +379,16 @@ pseudo_server_start(int daemonize) { | ||
69 | kill(ppid, SIGUSR1); | ||
70 | } | ||
71 | } | ||
72 | +#ifdef PSEUDO_PORT_LINUX | ||
73 | + pseudo_server_loop_epoll(); | ||
74 | +#else | ||
75 | pseudo_server_loop(); | ||
76 | +#endif | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | /* mess with internal tables as needed */ | ||
81 | -static void | ||
82 | +static unsigned int | ||
83 | open_client(int fd) { | ||
84 | pseudo_client_t *new_clients; | ||
85 | int i; | ||
86 | @@ -390,7 +404,7 @@ open_client(int fd) { | ||
87 | ++active_clients; | ||
88 | if (i > highest_client) | ||
89 | highest_client = i; | ||
90 | - return; | ||
91 | + return i; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | @@ -414,9 +428,11 @@ open_client(int fd) { | ||
96 | |||
97 | max_clients += 16; | ||
98 | ++active_clients; | ||
99 | + return max_clients - 16; | ||
100 | } else { | ||
101 | pseudo_diag("error allocating new client, fd %d\n", fd); | ||
102 | close(fd); | ||
103 | + return 0; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | @@ -433,6 +449,10 @@ close_client(int client) { | ||
108 | client, highest_client); | ||
109 | return; | ||
110 | } | ||
111 | + if (clients[client].fd == -1) { | ||
112 | + pseudo_debug(PDBGF_SERVER, "client %d already closed\n", client); | ||
113 | + return; | ||
114 | + } | ||
115 | close(clients[client].fd); | ||
116 | clients[client].fd = -1; | ||
117 | free(clients[client].tag); | ||
118 | @@ -566,6 +586,171 @@ serve_client(int i) { | ||
119 | } | ||
120 | } | ||
121 | |||
122 | +#ifdef PSEUDO_PORT_LINUX | ||
123 | +static void pseudo_server_loop_epoll(void) | ||
124 | +{ | ||
125 | + struct sockaddr_un client; | ||
126 | + socklen_t len; | ||
127 | + int i; | ||
128 | + int rc; | ||
129 | + int fd; | ||
130 | + int timeout; | ||
131 | + struct epoll_event ev, events[EPOLL_MAX_EVENTS]; | ||
132 | + int loop_timeout = pseudo_server_timeout; | ||
133 | + | ||
134 | + clients = malloc(16 * sizeof(*clients)); | ||
135 | + | ||
136 | + clients[0].fd = listen_fd; | ||
137 | + clients[0].pid = getpid(); | ||
138 | + | ||
139 | + for (i = 1; i < 16; ++i) { | ||
140 | + clients[i].fd = -1; | ||
141 | + clients[i].pid = 0; | ||
142 | + clients[i].tag = NULL; | ||
143 | + clients[i].program = NULL; | ||
144 | + } | ||
145 | + | ||
146 | + active_clients = 1; | ||
147 | + max_clients = 16; | ||
148 | + highest_client = 0; | ||
149 | + | ||
150 | + pseudo_debug(PDBGF_SERVER, "server loop started.\n"); | ||
151 | + if (listen_fd < 0) { | ||
152 | + pseudo_diag("got into loop with no valid listen fd.\n"); | ||
153 | + exit(PSEUDO_EXIT_LISTEN_FD); | ||
154 | + } | ||
155 | + | ||
156 | + timeout = LOOP_DELAY * 1000; | ||
157 | + | ||
158 | + int epollfd = epoll_create1(0); | ||
159 | + if (epollfd == -1) { | ||
160 | + pseudo_diag("epoll_create1() failed.\n"); | ||
161 | + exit(PSEUDO_EXIT_EPOLL_CREATE); | ||
162 | + } | ||
163 | + ev.events = EPOLLIN; | ||
164 | + ev.data.u64 = 0; | ||
165 | + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clients[0].fd, &ev) == -1) { | ||
166 | + pseudo_diag("epoll_ctl() failed with listening socket.\n"); | ||
167 | + exit(PSEUDO_EXIT_EPOLL_CTL); | ||
168 | + } | ||
169 | + | ||
170 | + pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server started (pid %d)", getpid()); | ||
171 | + | ||
172 | + for (;;) { | ||
173 | + rc = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, timeout); | ||
174 | + if (rc == 0 || (rc == -1 && errno == EINTR)) { | ||
175 | + /* If there's no clients, start timing out. If there | ||
176 | + * are active clients, never time out. | ||
177 | + */ | ||
178 | + if (active_clients == 1) { | ||
179 | + loop_timeout -= LOOP_DELAY; | ||
180 | + /* maybe flush database to disk */ | ||
181 | + pdb_maybe_backup(); | ||
182 | + if (loop_timeout <= 0) { | ||
183 | + pseudo_debug(PDBGF_SERVER, "no more clients, got bored.\n"); | ||
184 | + die_peacefully = 1; | ||
185 | + } else { | ||
186 | + /* display this if not exiting */ | ||
187 | + pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "%d messages handled in %.4f seconds, %d responses\n", | ||
188 | + messages, | ||
189 | + (double) message_time.tv_sec + | ||
190 | + (double) message_time.tv_usec / 1000000.0, | ||
191 | + responses); | ||
192 | + } | ||
193 | + } | ||
194 | + } else if (rc > 0) { | ||
195 | + loop_timeout = pseudo_server_timeout; | ||
196 | + for (i = 0; i < rc; ++i) { | ||
197 | + if (clients[events[i].data.u64].fd == listen_fd) { | ||
198 | + if (!die_forcefully) { | ||
199 | + len = sizeof(client); | ||
200 | + if ((fd = accept(listen_fd, (struct sockaddr *) &client, &len)) != -1) { | ||
201 | + /* Don't allow clients to end up on fd 2, because glibc's | ||
202 | + * malloc debug uses that fd unconditionally. | ||
203 | + */ | ||
204 | + if (fd == 2) { | ||
205 | + int newfd = fcntl(fd, F_DUPFD, 3); | ||
206 | + close(fd); | ||
207 | + fd = newfd; | ||
208 | + } | ||
209 | + pseudo_debug(PDBGF_SERVER, "new client fd %d\n", fd); | ||
210 | + /* A new client implicitly cancels any | ||
211 | + * previous shutdown request, or a | ||
212 | + * shutdown for lack of clients. | ||
213 | + */ | ||
214 | + pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT; | ||
215 | + die_peacefully = 0; | ||
216 | + | ||
217 | + ev.events = EPOLLIN; | ||
218 | + ev.data.u64 = open_client(fd); | ||
219 | + if (ev.data.u64 != 0 && epoll_ctl(epollfd, EPOLL_CTL_ADD, clients[ev.data.u64].fd, &ev) == -1) { | ||
220 | + pseudo_diag("epoll_ctl() failed with accepted socket.\n"); | ||
221 | + exit(PSEUDO_EXIT_EPOLL_CTL); | ||
222 | + } | ||
223 | + } else if (errno == EMFILE) { | ||
224 | + pseudo_debug(PDBGF_SERVER, "Hit max open files, dropping a client.\n"); | ||
225 | + /* In theory there is a potential race here where if we close a client, | ||
226 | + it may have sent us a fastop message which we don't act upon. | ||
227 | + If we don't close a filehandle we'll loop indefinitely thought. | ||
228 | + Only close one per loop iteration in the interests of caution */ | ||
229 | + for (int j = 1; j <= highest_client; ++j) { | ||
230 | + if (clients[j].fd != -1) { | ||
231 | + close_client(j); | ||
232 | + break; | ||
233 | + } | ||
234 | + } | ||
235 | + } | ||
236 | + } | ||
237 | + } else { | ||
238 | + struct timeval tv1, tv2; | ||
239 | + int rc; | ||
240 | + gettimeofday(&tv1, NULL); | ||
241 | + rc = serve_client(events[i].data.u64); | ||
242 | + gettimeofday(&tv2, NULL); | ||
243 | + ++messages; | ||
244 | + if (rc == 0) | ||
245 | + ++responses; | ||
246 | + message_time.tv_sec += (tv2.tv_sec - tv1.tv_sec); | ||
247 | + message_time.tv_usec += (tv2.tv_usec - tv1.tv_usec); | ||
248 | + if (message_time.tv_usec < 0) { | ||
249 | + message_time.tv_usec += 1000000; | ||
250 | + --message_time.tv_sec; | ||
251 | + } else while (message_time.tv_usec > 1000000) { | ||
252 | + message_time.tv_usec -= 1000000; | ||
253 | + ++message_time.tv_sec; | ||
254 | + } | ||
255 | + } | ||
256 | + if (die_forcefully) | ||
257 | + break; | ||
258 | + } | ||
259 | + pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients); | ||
260 | + } else { | ||
261 | + pseudo_diag("epoll_wait failed: %s\n", strerror(errno)); | ||
262 | + break; | ||
263 | + } | ||
264 | + if (die_peacefully || die_forcefully) { | ||
265 | + pseudo_debug(PDBGF_SERVER, "quitting.\n"); | ||
266 | + pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "server %d exiting: handled %d messages in %.4f seconds\n", | ||
267 | + getpid(), messages, | ||
268 | + (double) message_time.tv_sec + | ||
269 | + (double) message_time.tv_usec / 1000000.0); | ||
270 | + pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds", | ||
271 | + getpid(), messages, | ||
272 | + (double) message_time.tv_sec + | ||
273 | + (double) message_time.tv_usec / 1000000.0); | ||
274 | + /* and at this point, we'll start refusing connections */ | ||
275 | + close(clients[0].fd); | ||
276 | + /* This is a good place to insert a delay for | ||
277 | + * debugging race conditions during startup. */ | ||
278 | + /* usleep(300000); */ | ||
279 | + exit(0); | ||
280 | + } | ||
281 | + } | ||
282 | + | ||
283 | +} | ||
284 | + | ||
285 | +#endif | ||
286 | + | ||
287 | /* get clients, handle messages, shut down. | ||
288 | * This doesn't actually do any work, it just calls a ton of things which | ||
289 | * do work. | ||
290 | -- | ||
291 | 2.14.1 | ||
292 | |||
diff --git a/meta/recipes-devtools/pseudo/pseudo_1.8.2.bb b/meta/recipes-devtools/pseudo/pseudo_1.8.2.bb index 9bcd031892..81853e95c4 100644 --- a/meta/recipes-devtools/pseudo/pseudo_1.8.2.bb +++ b/meta/recipes-devtools/pseudo/pseudo_1.8.2.bb | |||
@@ -8,6 +8,7 @@ SRC_URI = "http://downloads.yoctoproject.org/releases/pseudo/${BPN}-${PV}.tar.bz | |||
8 | file://efe0be279901006f939cd357ccee47b651c786da.patch \ | 8 | file://efe0be279901006f939cd357ccee47b651c786da.patch \ |
9 | file://b6b68db896f9963558334aff7fca61adde4ec10f.patch \ | 9 | file://b6b68db896f9963558334aff7fca61adde4ec10f.patch \ |
10 | file://toomanyfiles.patch \ | 10 | file://toomanyfiles.patch \ |
11 | file://0001-Use-epoll-API-on-Linux.patch \ | ||
11 | " | 12 | " |
12 | 13 | ||
13 | SRC_URI[md5sum] = "7d41e72188fbea1f696c399c1a435675" | 14 | SRC_URI[md5sum] = "7d41e72188fbea1f696c399c1a435675" |