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