1 /* $NetBSD: svc_fdset.c,v 1.12 2015/11/08 19:30: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.12 2015/11/08 19:30: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] 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 *)__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 return fds; 219 } 220 221 static struct svc_fdset * 222 svc_pollfd_del(int fd, struct svc_fdset *fds) 223 { 224 struct pollfd *pfd; 225 226 if ((pfd = svc_pollfd_alloc(fds)) == NULL) 227 return NULL; 228 229 for (int i = 0; i < fds->fdnum; i++) { 230 if (pfd[i].fd != fd) 231 continue; 232 233 pfd[i].fd = -1; 234 DPRINTF("del fd=%d slot=%d", fd, fds->fdused); 235 if (i != fds->fdused - 1) 236 return fds; 237 238 do 239 if (pfd[i].fd != -1) 240 break; 241 while (--i >= 0); 242 243 fds->fdused = i + 1; 244 DPRINTF("del fd=%d fdused=%d", fd, fds->fdused); 245 return fds; 246 } 247 DPRINTF("del fd=%d not found", fd); 248 return NULL; 249 } 250 251 static struct svc_fdset * 252 svc_fdset_resize(int fd, struct svc_fdset *fds) 253 { 254 if (fds->fdset && fd < fds->fdsize) { 255 DPRINTF_FDSET(fds, "keeping %d < %d", fd, fds->fdsize); 256 return fds; 257 } 258 259 fd += FD_SETSIZE; 260 261 char *newfdset = realloc(fds->fdset, __NFD_BYTES(fd)); 262 if (newfdset == NULL) 263 return NULL; 264 265 memset(newfdset + __NFD_BYTES(fds->fdsize), 0, 266 __NFD_BYTES(fd) - __NFD_BYTES(fds->fdsize)); 267 268 269 fds->fdset = (void *)newfdset; 270 DPRINTF_FDSET(fds, "resize %d > %d", fd, fds->fdsize); 271 fds->fdsize = fd; 272 273 return fds; 274 } 275 276 static struct svc_fdset * 277 svc_fdset_alloc(int fd) 278 { 279 struct svc_fdset *fds; 280 281 if (!__isthreaded || fdsetkey == -2) 282 return svc_fdset_resize(fd, &__svc_fdset); 283 284 if (fdsetkey == -1) 285 thr_keycreate(&fdsetkey, svc_fdset_free); 286 287 if ((fds = thr_getspecific(fdsetkey)) == NULL) { 288 289 fds = calloc(1, sizeof(*fds)); 290 if (fds == NULL) 291 return NULL; 292 293 (void)thr_setspecific(fdsetkey, fds); 294 295 if (__svc_fdset.fdsize != 0) { 296 *fds = __svc_fdset; 297 DPRINTF("switching to %p", fds->fdset); 298 } else { 299 DPRINTF("first thread time %p", fds->fdset); 300 } 301 } else { 302 DPRINTF("again for %p", fds->fdset); 303 if (fd < fds->fdsize) 304 return fds; 305 } 306 307 return svc_fdset_resize(fd, fds); 308 } 309 310 /* allow each thread to have their own copy */ 311 void 312 svc_fdset_init(int flags) 313 { 314 DPRINTF("%x", flags); 315 __svc_flags = flags; 316 if ((flags & SVC_FDSET_MT) && fdsetkey == -2) 317 fdsetkey = -1; 318 } 319 320 void 321 svc_fdset_zero(void) 322 { 323 DPRINTF("zero"); 324 325 struct svc_fdset *fds = svc_fdset_alloc(0); 326 memset(fds->fdset, 0, fds->fdsize); 327 fds->fdmax = -1; 328 329 free(fds->fdp); 330 fds->fdp = NULL; 331 fds->fdnum = fds->fdused = 0; 332 } 333 334 int 335 svc_fdset_set(int fd) 336 { 337 struct svc_fdset *fds = svc_fdset_alloc(fd); 338 339 if (fds == NULL) 340 return -1; 341 342 FD_SET(fd, fds->fdset); 343 if (fd > fds->fdmax) 344 fds->fdmax = fd; 345 346 int rv = svc_pollfd_add(fd, fds) ? 0 : -1; 347 DPRINTF_FDSET(fds, "%d", fd); 348 349 svc_fdset_sanitize(fds); 350 return rv; 351 } 352 353 int 354 svc_fdset_isset(int fd) 355 { 356 struct svc_fdset *fds = svc_fdset_alloc(fd); 357 358 if (fds == NULL) 359 return -1; 360 361 DPRINTF_FDSET(fds, "%d", fd); 362 363 return FD_ISSET(fd, fds->fdset) != 0; 364 } 365 366 int 367 svc_fdset_clr(int fd) 368 { 369 struct svc_fdset *fds = svc_fdset_alloc(fd); 370 371 if (fds == NULL) 372 return -1; 373 374 FD_CLR(fd, fds->fdset); 375 376 int rv = svc_pollfd_del(fd, fds) ? 0 : -1; 377 DPRINTF_FDSET(fds, "%d", fd); 378 379 svc_fdset_sanitize(fds); 380 return rv; 381 } 382 383 fd_set * 384 svc_fdset_copy(const fd_set *orig) 385 { 386 int size = svc_fdset_getsize(0); 387 fd_set *copy = calloc(1, __NFD_BYTES(size)); 388 if (copy == NULL) 389 return NULL; 390 if (orig) 391 memcpy(copy, orig, __NFD_BYTES(size)); 392 return copy; 393 } 394 395 fd_set * 396 svc_fdset_get(void) 397 { 398 struct svc_fdset *fds = svc_fdset_alloc(0); 399 400 if (fds == NULL) 401 return NULL; 402 403 DPRINTF_FDSET(fds, "get"); 404 svc_fdset_sanitize(fds); 405 return fds->fdset; 406 } 407 408 int * 409 svc_fdset_getmax(void) 410 { 411 struct svc_fdset *fds = svc_fdset_alloc(0); 412 413 if (fds == NULL) 414 return NULL; 415 416 DPRINTF_FDSET(fds, "getmax"); 417 svc_fdset_sanitize(fds); 418 return &fds->fdmax; 419 } 420 421 int 422 svc_fdset_getsize(int fd) 423 { 424 struct svc_fdset *fds = svc_fdset_alloc(fd); 425 426 if (fds == NULL) 427 return -1; 428 429 DPRINTF_FDSET(fds, "getsize"); 430 return fds->fdsize; 431 } 432 433 struct pollfd * 434 svc_pollfd_copy(const struct pollfd *orig) 435 { 436 int size = svc_fdset_getsize(0); 437 struct pollfd *copy = calloc(size, sizeof(*orig)); 438 if (copy == NULL) 439 return NULL; 440 if (orig) 441 memcpy(copy, orig, size * sizeof(*orig)); 442 return copy; 443 } 444 445 struct pollfd * 446 svc_pollfd_get(void) 447 { 448 struct svc_fdset *fds = svc_fdset_alloc(0); 449 450 if (fds == NULL) 451 return NULL; 452 453 DPRINTF_FDSET(fds, "getpoll"); 454 return fds->fdp; 455 } 456 457 int * 458 svc_pollfd_getmax(void) 459 { 460 struct svc_fdset *fds = svc_fdset_alloc(0); 461 462 if (fds == NULL) 463 return NULL; 464 return &fds->fdused; 465 } 466 467 int 468 svc_pollfd_getsize(int fd) 469 { 470 struct svc_fdset *fds = svc_fdset_alloc(fd); 471 472 if (fds == NULL) 473 return -1; 474 475 DPRINTF_FDSET(fds, "getsize"); 476 return fds->fdnum; 477 } 478