svc_fdset.c revision 1.9 1 /* $NetBSD: svc_fdset.c,v 1.9 2015/11/07 23:09:20 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: svc_fdset.c,v 1.9 2015/11/07 23:09:20 christos Exp $");
34
35
36 #include "reentrant.h"
37
38 #include <sys/fd_set.h>
39
40 #include <rpc/rpc.h>
41
42 #ifdef FDSET_DEBUG
43 #include <stdio.h>
44 #include <stdarg.h>
45 #include <unistd.h>
46 #include <lwp.h>
47 #endif
48 #include <stdlib.h>
49 #include <string.h>
50 #include <poll.h>
51
52 #include "svc_fdset.h"
53
54 #undef svc_fdset
55 #undef svc_maxfd
56 extern __fd_set_256 svc_fdset;
57 extern int svc_maxfd;
58 int __svc_flags;
59
60 struct svc_fdset {
61 /* select */
62 fd_set *fdset;
63 int fdmax;
64 int fdsize;
65 /* poll */
66 struct pollfd *fdp;
67 int fdnum;
68 int fdused;
69 };
70
71 /* The single threaded, one global fd_set version */
72 static struct svc_fdset __svc_fdset;
73
74 static thread_key_t fdsetkey = -2;
75
76 #ifdef FDSET_DEBUG
77
78 static void __printflike(3, 0)
79 svc_header(const char *func, size_t line, const char *fmt, va_list ap)
80 {
81 fprintf(stderr, "%s[%d.%d]: %s, %zu: ", getprogname(), (int)getpid(),
82 (int)_lwp_self(), func, line);
83 vfprintf(stderr, fmt, ap);
84 va_end(ap);
85 }
86
87 static void __printflike(4, 5)
88 svc_fdset_print(const char *func, size_t line, struct svc_fdset *fds,
89 const char *fmt, ...)
90 {
91 va_list ap;
92 const char *did = "";
93
94 va_start(ap, fmt);
95 svc_header(func, line, fmt, ap);
96 va_end(ap);
97
98 fprintf(stderr, "%p[%d] <", fds->fdset, fds->fdmax);
99 for (int i = 0; i <= fds->fdmax; i++) {
100 if (!FD_ISSET(i, fds->fdset))
101 continue;
102 fprintf(stderr, "%s%d", did, i);
103 did = ", ";
104 }
105 fprintf(stderr, ">\n");
106 }
107
108 static void __printflike(3, 4)
109 svc_print(const char *func, size_t line, const char *fmt, ...)
110 {
111 va_list ap;
112
113 va_start(ap, fmt);
114 svc_header(func, line, fmt, ap);
115 va_end(ap);
116 fprintf(stderr, "\n");
117 }
118
119 #define DPRINTF(...) svc_print(__func__, __LINE__, __VA_ARGS__)
120 #define DPRINTF_FDSET(...) svc_fdset_print(__func__, __LINE__, __VA_ARGS__)
121
122 #else
123
124 #define DPRINTF(...)
125 #define DPRINTF_FDSET(...)
126
127 #endif
128
129
130 static inline void
131 svc_fdset_sanitize(struct svc_fdset *fds)
132 {
133 while (fds->fdmax >= 0 && !FD_ISSET(fds->fdmax, fds->fdset))
134 fds->fdmax--;
135 /* Compat update */
136 if (fds == &__svc_fdset) {
137 svc_fdset = *(__fd_set_256 *)__svc_fdset.fdset;
138 svc_maxfd = __svc_fdset.fdmax;
139 }
140 }
141
142 static void
143 svc_fdset_free(void *v)
144 {
145 struct svc_fdset *fds = v;
146 DPRINTF_FDSET(fds, "free");
147
148 free(fds->fdp);
149 free(fds->fdset);
150 free(fds);
151 }
152
153 static void
154 svc_pollfd_init(struct pollfd *pfd, int nfd)
155 {
156 for (int i = 0; i < nfd; i++) {
157 pfd[i].fd = -1;
158 pfd[i].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
159 }
160 }
161
162 static struct pollfd *
163 svc_pollfd_alloc(struct svc_fdset *fds)
164 {
165 fds->fdnum = FD_SETSIZE;
166 fds->fdp = calloc(fds->fdnum, sizeof(*fds->fdp));
167 if (fds->fdp == NULL)
168 return NULL;
169 svc_pollfd_init(fds->fdp, fds->fdnum);
170 return fds->fdp;
171 }
172
173
174 static struct svc_fdset *
175 svc_pollfd_add(int fd, struct svc_fdset *fds)
176 {
177 struct pollfd *pfd;
178
179 if ((pfd = svc_pollfd_alloc(fds)) == NULL)
180 return NULL;
181
182 for (int i = 0; i < fds->fdnum; i++)
183 if (pfd[i].fd == -1) {
184 if (i > fds->fdused)
185 fds->fdused = i + 1;
186 pfd[i].fd = fd;
187 return fds;
188 }
189
190 pfd = realloc(fds->fdp, (fds->fdnum + FD_SETSIZE) * sizeof(*fds->fdp));
191 if (pfd == NULL)
192 return NULL;
193
194 svc_pollfd_init(pfd + fds->fdnum, FD_SETSIZE);
195 pfd[fds->fdnum].fd = fd;
196 fds->fdused = fds->fdnum + 1;
197 fds->fdnum += FD_SETSIZE;
198 return fds;
199 }
200
201 static struct svc_fdset *
202 svc_pollfd_del(int fd, struct svc_fdset *fds)
203 {
204 struct pollfd *pfd;
205
206 if ((pfd = svc_pollfd_alloc(fds)) == NULL)
207 return NULL;
208
209 for (int i = 0; i < fds->fdnum; i++) {
210 if (pfd[i].fd != fd)
211 continue;
212
213 pfd[i].fd = -1;
214 if (i != fds->fdused - 1)
215 return fds;
216
217 do
218 if (pfd[i].fd != -1)
219 break;
220 while (--i >= 0);
221 fds->fdused = i + 1;
222 return fds;
223 }
224 return NULL;
225 }
226
227 static struct svc_fdset *
228 svc_fdset_resize(int fd, struct svc_fdset *fds)
229 {
230 if (fds->fdset && fd < fds->fdsize) {
231 DPRINTF_FDSET(fds, "keeping %d < %d", fd, fds->fdsize);
232 return fds;
233 }
234
235 fd += FD_SETSIZE;
236
237 char *newfdset = realloc(fds->fdset, __NFD_BYTES(fd));
238 if (newfdset == NULL)
239 return NULL;
240
241 memset(newfdset + __NFD_BYTES(fds->fdsize), 0,
242 __NFD_BYTES(fd) - __NFD_BYTES(fds->fdsize));
243
244
245 fds->fdset = (void *)newfdset;
246 DPRINTF_FDSET(fds, "resize %d > %d", fd, fds->fdsize);
247 fds->fdsize = fd;
248
249 return fds;
250 }
251
252 static struct svc_fdset *
253 svc_fdset_alloc(int fd)
254 {
255 struct svc_fdset *fds;
256
257 if (!__isthreaded || fdsetkey == -2)
258 return svc_fdset_resize(fd, &__svc_fdset);
259
260 if (fdsetkey == -1)
261 thr_keycreate(&fdsetkey, svc_fdset_free);
262
263 if ((fds = thr_getspecific(fdsetkey)) == NULL) {
264
265 fds = calloc(1, sizeof(*fds));
266 if (fds == NULL)
267 return NULL;
268
269 (void)thr_setspecific(fdsetkey, fds);
270
271 if (__svc_fdset.fdsize != 0) {
272 *fds = __svc_fdset;
273 DPRINTF("switching to %p", fds->fdset);
274 } else {
275 DPRINTF("first thread time %p", fds->fdset);
276 }
277 } else {
278 DPRINTF("again for %p", fds->fdset);
279 if (fd < fds->fdsize)
280 return fds;
281 }
282
283 return svc_fdset_resize(fd, fds);
284 }
285
286 /* allow each thread to have their own copy */
287 void
288 svc_fdset_init(int flags)
289 {
290 DPRINTF("%x", flags);
291 __svc_flags = flags;
292 if ((flags & SVC_FDSET_MT) && fdsetkey == -2)
293 fdsetkey = -1;
294 }
295
296 void
297 svc_fdset_zero(void)
298 {
299 DPRINTF("zero");
300
301 struct svc_fdset *fds = svc_fdset_alloc(0);
302 memset(fds->fdset, 0, fds->fdsize);
303 fds->fdmax = -1;
304
305 free(fds->fdp);
306 fds->fdp = NULL;
307 fds->fdnum = fds->fdused = 0;
308 }
309
310 int
311 svc_fdset_set(int fd)
312 {
313 struct svc_fdset *fds = svc_fdset_alloc(fd);
314
315 if (fds == NULL)
316 return -1;
317
318 FD_SET(fd, fds->fdset);
319 if (fd > fds->fdmax)
320 fds->fdmax = fd;
321
322 DPRINTF_FDSET(fds, "%d", fd);
323
324 svc_fdset_sanitize(fds);
325 return svc_pollfd_add(fd, fds) ? 0 : -1;
326 }
327
328 int
329 svc_fdset_isset(int fd)
330 {
331 struct svc_fdset *fds = svc_fdset_alloc(fd);
332
333 if (fds == NULL)
334 return -1;
335
336 DPRINTF_FDSET(fds, "%d", fd);
337
338 return FD_ISSET(fd, fds->fdset) != 0;
339 }
340
341 int
342 svc_fdset_clr(int fd)
343 {
344 struct svc_fdset *fds = svc_fdset_alloc(fd);
345
346 if (fds == NULL)
347 return -1;
348
349 FD_CLR(fd, fds->fdset);
350 DPRINTF_FDSET(fds, "%d", fd);
351
352 svc_fdset_sanitize(fds);
353 return svc_pollfd_del(fd, fds) ? 0 : -1;
354 }
355
356 fd_set *
357 svc_fdset_copy(const fd_set *orig)
358 {
359 int size = svc_fdset_getsize(0);
360 fd_set *copy = calloc(1, __NFD_BYTES(size));
361 if (copy == NULL)
362 return NULL;
363 if (orig)
364 memcpy(copy, orig, __NFD_BYTES(size));
365 return copy;
366 }
367
368 fd_set *
369 svc_fdset_get(void)
370 {
371 struct svc_fdset *fds = svc_fdset_alloc(0);
372
373 if (fds == NULL)
374 return NULL;
375
376 DPRINTF_FDSET(fds, "get");
377 svc_fdset_sanitize(fds);
378 return fds->fdset;
379 }
380
381 int *
382 svc_fdset_getmax(void)
383 {
384 struct svc_fdset *fds = svc_fdset_alloc(0);
385
386 if (fds == NULL)
387 return NULL;
388
389 DPRINTF_FDSET(fds, "getmax");
390 svc_fdset_sanitize(fds);
391 return &fds->fdmax;
392 }
393
394 int
395 svc_fdset_getsize(int fd)
396 {
397 struct svc_fdset *fds = svc_fdset_alloc(fd);
398
399 if (fds == NULL)
400 return -1;
401
402 DPRINTF_FDSET(fds, "getsize");
403 return fds->fdsize;
404 }
405
406 struct pollfd *
407 svc_pollfd_copy(const struct pollfd *orig)
408 {
409 int size = svc_fdset_getsize(0);
410 struct pollfd *copy = calloc(size, sizeof(*orig));
411 if (copy == NULL)
412 return NULL;
413 if (orig)
414 memcpy(copy, orig, size * sizeof(*orig));
415 return copy;
416 }
417
418 struct pollfd *
419 svc_pollfd_get(void)
420 {
421 struct svc_fdset *fds = svc_fdset_alloc(0);
422
423 if (fds == NULL)
424 return NULL;
425
426 DPRINTF_FDSET(fds, "getpoll");
427 return fds->fdp;
428 }
429
430 int *
431 svc_pollfd_getmax(void)
432 {
433 struct svc_fdset *fds = svc_fdset_alloc(0);
434
435 if (fds == NULL)
436 return NULL;
437 return &fds->fdused;
438 }
439
440 int
441 svc_pollfd_getsize(int fd)
442 {
443 struct svc_fdset *fds = svc_fdset_alloc(fd);
444
445 if (fds == NULL)
446 return -1;
447
448 DPRINTF_FDSET(fds, "getsize");
449 return fds->fdnum;
450 }
451