Home | History | Annotate | Line # | Download | only in rpc
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