1 /* $NetBSD: svc_fdset.c,v 1.11 2015/11/08 02:46:53 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.11 2015/11/08 02:46:53 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 #ifdef _LIBC 57 extern __fd_set_256 svc_fdset; 58 #endif 59 extern int svc_maxfd; 60 int __svc_flags; 61 62 struct svc_fdset { 63 /* select */ 64 fd_set *fdset; 65 int fdmax; 66 int fdsize; 67 /* poll */ 68 struct pollfd *fdp; 69 int fdnum; 70 int fdused; 71 }; 72 73 /* The single threaded, one global fd_set version */ 74 static struct svc_fdset __svc_fdset; 75 76 static thread_key_t fdsetkey = -2; 77 78 #ifdef FDSET_DEBUG 79 80 static void __printflike(3, 0) 81 svc_header(const char *func, size_t line, const char *fmt, va_list ap) 82 { 83 fprintf(stderr, "%s[%d.%d]: %s, %zu: ", getprogname(), (int)getpid(), 84 (int)_lwp_self(), func, line); 85 vfprintf(stderr, fmt, ap); 86 va_end(ap); 87 } 88 89 static void __printflike(4, 5) 90 svc_fdset_print(const char *func, size_t line, struct svc_fdset *fds, 91 const char *fmt, ...) 92 { 93 va_list ap; 94 const char *did = ""; 95 96 va_start(ap, fmt); 97 svc_header(func, line, fmt, ap); 98 va_end(ap); 99 100 fprintf(stderr, "%p[%d] <", fds->fdset, fds->fdmax); 101 for (int i = 0; i <= fds->fdmax; i++) { 102 if (!FD_ISSET(i, fds->fdset)) 103 continue; 104 fprintf(stderr, "%s%d", did, i); 105 did = ", "; 106 } 107 fprintf(stderr, ">\n"); 108 } 109 110 static void __printflike(3, 4) 111 svc_print(const char *func, size_t line, const char *fmt, ...) 112 { 113 va_list ap; 114 115 va_start(ap, fmt); 116 svc_header(func, line, fmt, ap); 117 va_end(ap); 118 fprintf(stderr, "\n"); 119 } 120 121 #define DPRINTF(...) svc_print(__func__, __LINE__, __VA_ARGS__) 122 #define DPRINTF_FDSET(...) svc_fdset_print(__func__, __LINE__, __VA_ARGS__) 123 124 #else 125 126 #define DPRINTF(...) 127 #define DPRINTF_FDSET(...) 128 129 #endif 130 131 132 static inline void 133 svc_fdset_sanitize(struct svc_fdset *fds) 134 { 135 while (fds->fdmax >= 0 && !FD_ISSET(fds->fdmax, fds->fdset)) 136 fds->fdmax--; 137 #ifdef _LIBC 138 /* Compat update */ 139 if (fds == &__svc_fdset) { 140 svc_fdset = *(__fd_set_256 *)__svc_fdset.fdset; 141 svc_maxfd = __svc_fdset.fdmax; 142 } 143 #endif 144 } 145 146 static void 147 svc_fdset_free(void *v) 148 { 149 struct svc_fdset *fds = v; 150 DPRINTF_FDSET(fds, "free"); 151 152 free(fds->fdp); 153 free(fds->fdset); 154 free(fds); 155 } 156 157 static void 158 svc_pollfd_init(struct pollfd *pfd, int nfd) 159 { 160 for (int i = 0; i < nfd; i++) { 161 pfd[i].fd = -1; 162 pfd[i].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; 163 pfd[i].revents = 0; 164 } 165 } 166 167 static struct pollfd * 168 svc_pollfd_alloc(struct svc_fdset *fds) 169 { 170 fds->fdnum = FD_SETSIZE; 171 fds->fdp = calloc(fds->fdnum, sizeof(*fds->fdp)); 172 if (fds->fdp == NULL) 173 return NULL; 174 svc_pollfd_init(fds->fdp, fds->fdnum); 175 return fds->fdp; 176 } 177 178 179 static struct svc_fdset * 180 svc_pollfd_add(int fd, struct svc_fdset *fds) 181 { 182 struct pollfd *pfd; 183 184 if ((pfd = svc_pollfd_alloc(fds)) == NULL) 185 return NULL; 186 187 for (int i = 0; i < fds->fdnum; i++) 188 if (pfd[i].fd == -1) { 189 if (i > fds->fdused) 190 fds->fdused = i + 1; 191 pfd[i].fd = fd; 192 return fds; 193 } 194 195 pfd = realloc(fds->fdp, (fds->fdnum + FD_SETSIZE) * sizeof(*fds->fdp)); 196 if (pfd == NULL) 197 return NULL; 198 199 svc_pollfd_init(pfd + fds->fdnum, FD_SETSIZE); 200 pfd[fds->fdnum].fd = fd; 201 fds->fdused = fds->fdnum + 1; 202 fds->fdnum += FD_SETSIZE; 203 return fds; 204 } 205 206 static struct svc_fdset * 207 svc_pollfd_del(int fd, struct svc_fdset *fds) 208 { 209 struct pollfd *pfd; 210 211 if ((pfd = svc_pollfd_alloc(fds)) == NULL) 212 return NULL; 213 214 for (int i = 0; i < fds->fdnum; i++) { 215 if (pfd[i].fd != fd) 216 continue; 217 218 pfd[i].fd = -1; 219 if (i != fds->fdused - 1) 220 return fds; 221 222 do 223 if (pfd[i].fd != -1) 224 break; 225 while (--i >= 0); 226 fds->fdused = i + 1; 227 return fds; 228 } 229 return NULL; 230 } 231 232 static struct svc_fdset * 233 svc_fdset_resize(int fd, struct svc_fdset *fds) 234 { 235 if (fds->fdset && fd < fds->fdsize) { 236 DPRINTF_FDSET(fds, "keeping %d < %d", fd, fds->fdsize); 237 return fds; 238 } 239 240 fd += FD_SETSIZE; 241 242 char *newfdset = realloc(fds->fdset, __NFD_BYTES(fd)); 243 if (newfdset == NULL) 244 return NULL; 245 246 memset(newfdset + __NFD_BYTES(fds->fdsize), 0, 247 __NFD_BYTES(fd) - __NFD_BYTES(fds->fdsize)); 248 249 250 fds->fdset = (void *)newfdset; 251 DPRINTF_FDSET(fds, "resize %d > %d", fd, fds->fdsize); 252 fds->fdsize = fd; 253 254 return fds; 255 } 256 257 static struct svc_fdset * 258 svc_fdset_alloc(int fd) 259 { 260 struct svc_fdset *fds; 261 262 if (!__isthreaded || fdsetkey == -2) 263 return svc_fdset_resize(fd, &__svc_fdset); 264 265 if (fdsetkey == -1) 266 thr_keycreate(&fdsetkey, svc_fdset_free); 267 268 if ((fds = thr_getspecific(fdsetkey)) == NULL) { 269 270 fds = calloc(1, sizeof(*fds)); 271 if (fds == NULL) 272 return NULL; 273 274 (void)thr_setspecific(fdsetkey, fds); 275 276 if (__svc_fdset.fdsize != 0) { 277 *fds = __svc_fdset; 278 DPRINTF("switching to %p", fds->fdset); 279 } else { 280 DPRINTF("first thread time %p", fds->fdset); 281 } 282 } else { 283 DPRINTF("again for %p", fds->fdset); 284 if (fd < fds->fdsize) 285 return fds; 286 } 287 288 return svc_fdset_resize(fd, fds); 289 } 290 291 /* allow each thread to have their own copy */ 292 void 293 svc_fdset_init(int flags) 294 { 295 DPRINTF("%x", flags); 296 __svc_flags = flags; 297 if ((flags & SVC_FDSET_MT) && fdsetkey == -2) 298 fdsetkey = -1; 299 } 300 301 void 302 svc_fdset_zero(void) 303 { 304 DPRINTF("zero"); 305 306 struct svc_fdset *fds = svc_fdset_alloc(0); 307 memset(fds->fdset, 0, fds->fdsize); 308 fds->fdmax = -1; 309 310 free(fds->fdp); 311 fds->fdp = NULL; 312 fds->fdnum = fds->fdused = 0; 313 } 314 315 int 316 svc_fdset_set(int fd) 317 { 318 struct svc_fdset *fds = svc_fdset_alloc(fd); 319 320 if (fds == NULL) 321 return -1; 322 323 FD_SET(fd, fds->fdset); 324 if (fd > fds->fdmax) 325 fds->fdmax = fd; 326 327 DPRINTF_FDSET(fds, "%d", fd); 328 329 svc_fdset_sanitize(fds); 330 return svc_pollfd_add(fd, fds) ? 0 : -1; 331 } 332 333 int 334 svc_fdset_isset(int fd) 335 { 336 struct svc_fdset *fds = svc_fdset_alloc(fd); 337 338 if (fds == NULL) 339 return -1; 340 341 DPRINTF_FDSET(fds, "%d", fd); 342 343 return FD_ISSET(fd, fds->fdset) != 0; 344 } 345 346 int 347 svc_fdset_clr(int fd) 348 { 349 struct svc_fdset *fds = svc_fdset_alloc(fd); 350 351 if (fds == NULL) 352 return -1; 353 354 FD_CLR(fd, fds->fdset); 355 DPRINTF_FDSET(fds, "%d", fd); 356 357 svc_fdset_sanitize(fds); 358 return svc_pollfd_del(fd, fds) ? 0 : -1; 359 } 360 361 fd_set * 362 svc_fdset_copy(const fd_set *orig) 363 { 364 int size = svc_fdset_getsize(0); 365 fd_set *copy = calloc(1, __NFD_BYTES(size)); 366 if (copy == NULL) 367 return NULL; 368 if (orig) 369 memcpy(copy, orig, __NFD_BYTES(size)); 370 return copy; 371 } 372 373 fd_set * 374 svc_fdset_get(void) 375 { 376 struct svc_fdset *fds = svc_fdset_alloc(0); 377 378 if (fds == NULL) 379 return NULL; 380 381 DPRINTF_FDSET(fds, "get"); 382 svc_fdset_sanitize(fds); 383 return fds->fdset; 384 } 385 386 int * 387 svc_fdset_getmax(void) 388 { 389 struct svc_fdset *fds = svc_fdset_alloc(0); 390 391 if (fds == NULL) 392 return NULL; 393 394 DPRINTF_FDSET(fds, "getmax"); 395 svc_fdset_sanitize(fds); 396 return &fds->fdmax; 397 } 398 399 int 400 svc_fdset_getsize(int fd) 401 { 402 struct svc_fdset *fds = svc_fdset_alloc(fd); 403 404 if (fds == NULL) 405 return -1; 406 407 DPRINTF_FDSET(fds, "getsize"); 408 return fds->fdsize; 409 } 410 411 struct pollfd * 412 svc_pollfd_copy(const struct pollfd *orig) 413 { 414 int size = svc_fdset_getsize(0); 415 struct pollfd *copy = calloc(size, sizeof(*orig)); 416 if (copy == NULL) 417 return NULL; 418 if (orig) 419 memcpy(copy, orig, size * sizeof(*orig)); 420 return copy; 421 } 422 423 struct pollfd * 424 svc_pollfd_get(void) 425 { 426 struct svc_fdset *fds = svc_fdset_alloc(0); 427 428 if (fds == NULL) 429 return NULL; 430 431 DPRINTF_FDSET(fds, "getpoll"); 432 return fds->fdp; 433 } 434 435 int * 436 svc_pollfd_getmax(void) 437 { 438 struct svc_fdset *fds = svc_fdset_alloc(0); 439 440 if (fds == NULL) 441 return NULL; 442 return &fds->fdused; 443 } 444 445 int 446 svc_pollfd_getsize(int fd) 447 { 448 struct svc_fdset *fds = svc_fdset_alloc(fd); 449 450 if (fds == NULL) 451 return -1; 452 453 DPRINTF_FDSET(fds, "getsize"); 454 return fds->fdnum; 455 } 456