1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/t_lock.h> 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/sysmacros.h> 32 #include <sys/cmn_err.h> 33 #include <sys/list.h> 34 35 #include <sys/stropts.h> 36 #include <sys/socket.h> 37 #include <sys/socketvar.h> 38 39 #include <fs/sockfs/sockcommon.h> 40 #include <fs/sockfs/socktpi.h> 41 42 /* 43 * Socket Parameters 44 * 45 * Socket parameter (struct sockparams) entries represent the socket types 46 * available on the system. 47 * 48 * Flags (sp_flags): 49 * 50 * SOCKPARAMS_EPHEMERAL: A temporary sockparams entry that will be deleted 51 * as soon as its' ref count drops to zero. In addition, ephemeral entries will 52 * never be hooked onto the global sockparams list. Ephemeral entries are 53 * created when application requests to create a socket using an application 54 * supplied device path, or when a socket is falling back to TPI. 55 * 56 * Lock order: 57 * The lock order is splist_lock -> sp_lock. 58 * The lock order is sp_ephem_lock -> sp_lock. 59 */ 60 extern int kobj_path_exists(char *, int); 61 extern void nl7c_init(void); 62 extern int sockfs_defer_nl7c_init; 63 64 static int sockparams_sdev_init(struct sockparams *, char *, int); 65 static void sockparams_sdev_fini(struct sockparams *); 66 67 /* 68 * Global sockparams list (populated via soconfig(1M)). 69 */ 70 static list_t sphead; 71 static krwlock_t splist_lock; 72 73 /* 74 * List of ephemeral sockparams. 75 */ 76 static list_t sp_ephem_list; 77 static krwlock_t sp_ephem_lock; 78 79 /* Global kstats for sockparams */ 80 typedef struct sockparams_g_stats { 81 kstat_named_t spgs_ephem_nalloc; 82 kstat_named_t spgs_ephem_nreuse; 83 } sockparams_g_stats_t; 84 85 static sockparams_g_stats_t sp_g_stats; 86 static kstat_t *sp_g_kstat; 87 88 89 void 90 sockparams_init(void) 91 { 92 list_create(&sphead, sizeof (struct sockparams), 93 offsetof(struct sockparams, sp_node)); 94 list_create(&sp_ephem_list, sizeof (struct sockparams), 95 offsetof(struct sockparams, sp_node)); 96 97 rw_init(&splist_lock, NULL, RW_DEFAULT, NULL); 98 rw_init(&sp_ephem_lock, NULL, RW_DEFAULT, NULL); 99 100 kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc", 101 KSTAT_DATA_UINT64); 102 kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse", 103 KSTAT_DATA_UINT64); 104 105 sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc", 106 KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t), 107 KSTAT_FLAG_VIRTUAL); 108 if (sp_g_kstat == NULL) 109 return; 110 111 sp_g_kstat->ks_data = &sp_g_stats; 112 113 kstat_install(sp_g_kstat); 114 } 115 116 static int 117 sockparams_kstat_update(kstat_t *ksp, int rw) 118 { 119 struct sockparams *sp = ksp->ks_private; 120 sockparams_stats_t *sps = ksp->ks_data; 121 122 if (rw == KSTAT_WRITE) 123 return (EACCES); 124 125 sps->sps_nactive.value.ui64 = sp->sp_refcnt; 126 127 return (0); 128 } 129 130 /* 131 * Setup kstats for the given sockparams entry. 132 */ 133 static void 134 sockparams_kstat_init(struct sockparams *sp) 135 { 136 char name[KSTAT_STRLEN]; 137 138 kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback", 139 KSTAT_DATA_UINT64); 140 kstat_named_init(&sp->sp_stats.sps_nactive, "nactive", 141 KSTAT_DATA_UINT64); 142 kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate", 143 KSTAT_DATA_UINT64); 144 145 (void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family, 146 sp->sp_type, sp->sp_protocol); 147 148 sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED, 149 sizeof (sockparams_stats_t) / sizeof (kstat_named_t), 150 KSTAT_FLAG_VIRTUAL); 151 152 if (sp->sp_kstat == NULL) 153 return; 154 155 sp->sp_kstat->ks_data = &sp->sp_stats; 156 sp->sp_kstat->ks_update = sockparams_kstat_update; 157 sp->sp_kstat->ks_private = sp; 158 kstat_install(sp->sp_kstat); 159 } 160 161 static void 162 sockparams_kstat_fini(struct sockparams *sp) 163 { 164 if (sp->sp_kstat != NULL) { 165 kstat_delete(sp->sp_kstat); 166 sp->sp_kstat = NULL; 167 } 168 } 169 170 /* 171 * sockparams_create(int family, int type, int protocol, char *modname, 172 * char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 173 * 174 * Create a new sockparams entry. 175 * 176 * Arguments: 177 * family, type, protocol: specifies the socket type 178 * modname: Name of the module associated with the socket type. The 179 * module can be NULL if a device path is given, in which 180 * case the TPI module is used. 181 * devpath: Path to the STREAMS device. May be NULL for non-STREAMS 182 * based transports, or those transports that do not provide 183 * the capability to fallback to STREAMS. 184 * devpathlen: Length of the devpath string. The argument can be 0, 185 * indicating that devpath was allocated statically, and should 186 * not be freed when the sockparams entry is destroyed. 187 * 188 * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed. 189 * kmflags: KM_{NO,}SLEEP 190 * errorp : Value-return argument, set when an error occurs. 191 * 192 * Returns: 193 * On success a new sockparams entry is returned, and *errorp is set 194 * to 0. On failure NULL is returned and *errorp is set to indicate the 195 * type of error that occured. 196 * 197 * Notes: 198 * devpath and modname are freed upon failure. 199 */ 200 struct sockparams * 201 sockparams_create(int family, int type, int protocol, char *modname, 202 char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 203 { 204 struct sockparams *sp = NULL; 205 size_t size; 206 207 ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0); 208 if (flags & ~SOCKPARAMS_EPHEMERAL) { 209 *errorp = EINVAL; 210 goto error; 211 } 212 213 /* either a module or device must be given */ 214 if (modname == NULL && devpath == NULL) { 215 *errorp = EINVAL; 216 goto error; 217 } 218 219 sp = kmem_zalloc(sizeof (*sp), kmflags); 220 if (sp == NULL) { 221 *errorp = ENOMEM; 222 goto error; 223 } 224 sp->sp_family = family; 225 sp->sp_type = type; 226 sp->sp_protocol = protocol; 227 sp->sp_refcnt = 0; 228 sp->sp_flags = flags; 229 230 /* 231 * We do not create kstats for ephemeral entries, but we do keep 232 * track how many we have created. 233 */ 234 if (sp->sp_flags & SOCKPARAMS_EPHEMERAL) 235 sp_g_stats.spgs_ephem_nalloc.value.ui64++; 236 else 237 sockparams_kstat_init(sp); 238 239 if (modname != NULL) { 240 sp->sp_smod_name = modname; 241 } else { 242 size = strlen(SOTPI_SMOD_NAME) + 1; 243 modname = kmem_zalloc(size, kmflags); 244 if (modname == NULL) { 245 *errorp = ENOMEM; 246 goto error; 247 } 248 sp->sp_smod_name = modname; 249 (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME); 250 } 251 252 if (devpath != NULL) { 253 /* Set up the device entry. */ 254 *errorp = sockparams_sdev_init(sp, devpath, devpathlen); 255 if (*errorp != 0) 256 goto error; 257 } 258 259 mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL); 260 *errorp = 0; 261 return (sp); 262 error: 263 ASSERT(*errorp != 0); 264 if (modname != NULL) 265 kmem_free(modname, strlen(modname) + 1); 266 if (devpathlen != 0) 267 kmem_free(devpath, devpathlen); 268 if (sp != NULL) { 269 sockparams_kstat_fini(sp); 270 kmem_free(sp, sizeof (*sp)); 271 } 272 return (NULL); 273 } 274 275 /* 276 * Initialize the STREAMS device aspect of the sockparams entry. 277 */ 278 static int 279 sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen) 280 { 281 vnode_t *vp = NULL; 282 int error; 283 284 ASSERT(devpath != NULL); 285 286 if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) { 287 dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n", 288 devpath, error)); 289 return (error); 290 } 291 292 ASSERT(vp != NULL); 293 sp->sp_sdev_info.sd_vnode = vp; 294 sp->sp_sdev_info.sd_devpath = devpath; 295 sp->sp_sdev_info.sd_devpathlen = devpathlen; 296 297 return (0); 298 } 299 300 /* 301 * sockparams_destroy(struct sockparams *sp) 302 * 303 * Releases all the resources associated with the sockparams entry, 304 * and frees the sockparams entry. 305 * 306 * Arguments: 307 * sp: the sockparams entry to destroy. 308 * 309 * Returns: 310 * Nothing. 311 * 312 * Locking: 313 * The sp_lock of the entry can not be held. 314 */ 315 void 316 sockparams_destroy(struct sockparams *sp) 317 { 318 ASSERT(sp->sp_refcnt == 0); 319 ASSERT(!list_link_active(&sp->sp_node)); 320 321 sockparams_sdev_fini(sp); 322 323 if (sp->sp_smod_info != NULL) 324 SMOD_DEC_REF(sp, sp->sp_smod_info); 325 kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1); 326 sp->sp_smod_name = NULL; 327 sp->sp_smod_info = NULL; 328 mutex_destroy(&sp->sp_lock); 329 sockparams_kstat_fini(sp); 330 331 kmem_free(sp, sizeof (*sp)); 332 } 333 334 /* 335 * Clean up the STREAMS device part of the sockparams entry. 336 */ 337 static void 338 sockparams_sdev_fini(struct sockparams *sp) 339 { 340 sdev_info_t sd; 341 342 /* 343 * if the entry does not have a STREAMS device, then there 344 * is nothing to do. 345 */ 346 if (!SOCKPARAMS_HAS_DEVICE(sp)) 347 return; 348 349 sd = sp->sp_sdev_info; 350 if (sd.sd_vnode != NULL) 351 VN_RELE(sd.sd_vnode); 352 if (sd.sd_devpathlen != 0) 353 kmem_free(sd.sd_devpath, sd.sd_devpathlen); 354 355 sp->sp_sdev_info.sd_vnode = NULL; 356 sp->sp_sdev_info.sd_devpath = NULL; 357 } 358 359 /* 360 * Look for a matching sockparams entry on the given list. 361 * The caller must hold the associated list lock. 362 */ 363 static struct sockparams * 364 sockparams_find(list_t *list, int family, int type, int protocol, 365 boolean_t by_devpath, const char *name) 366 { 367 struct sockparams *sp; 368 369 for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) { 370 if (sp->sp_family == family && sp->sp_type == type) { 371 if (sp->sp_protocol == protocol) { 372 if (name == NULL) 373 break; 374 else if (by_devpath && 375 sp->sp_sdev_info.sd_devpath != NULL && 376 strcmp(sp->sp_sdev_info.sd_devpath, 377 name) == 0) 378 break; 379 else if (strcmp(sp->sp_smod_name, name) == 0) 380 break; 381 } 382 } 383 } 384 return (sp); 385 } 386 387 /* 388 * sockparams_hold_ephemeral() 389 * 390 * Returns an ephemeral sockparams entry of the requested family, type and 391 * protocol. The entry is returned held, and the caller is responsible for 392 * dropping the reference using SOCKPARAMS_DEC_REF() once done. 393 * 394 * All ephemeral entries are on list (sp_ephem_list). If there is an 395 * entry on the list that match the search criteria, then a reference is 396 * placed on that entry. Otherwise, a new entry is created and inserted 397 * in the list. The entry is removed from the list when the last reference 398 * is dropped. 399 * 400 * The tpi flag is used to determine whether name refers to a device or 401 * module name. 402 */ 403 static struct sockparams * 404 sockparams_hold_ephemeral(int family, int type, int protocol, 405 const char *name, boolean_t by_devpath, int kmflag, int *errorp) 406 { 407 struct sockparams *sp = NULL; 408 *errorp = 0; 409 410 /* 411 * First look for an existing entry 412 */ 413 rw_enter(&sp_ephem_lock, RW_READER); 414 sp = sockparams_find(&sp_ephem_list, family, type, protocol, 415 by_devpath, name); 416 if (sp != NULL) { 417 SOCKPARAMS_INC_REF(sp); 418 rw_exit(&sp_ephem_lock); 419 sp_g_stats.spgs_ephem_nreuse.value.ui64++; 420 421 return (sp); 422 } else { 423 struct sockparams *newsp = NULL; 424 char *namebuf = NULL; 425 int namelen = 0; 426 427 rw_exit(&sp_ephem_lock); 428 429 namelen = strlen(name) + 1; 430 namebuf = kmem_alloc(namelen, kmflag); 431 if (namebuf == NULL) { 432 *errorp = ENOMEM; 433 return (NULL); 434 } 435 436 (void *)strncpy(namebuf, name, namelen); 437 if (by_devpath) { 438 newsp = sockparams_create(family, type, 439 protocol, NULL, namebuf, namelen, 440 SOCKPARAMS_EPHEMERAL, kmflag, errorp); 441 } else { 442 newsp = sockparams_create(family, type, 443 protocol, namebuf, NULL, 0, 444 SOCKPARAMS_EPHEMERAL, kmflag, errorp); 445 } 446 447 if (newsp == NULL) { 448 ASSERT(*errorp != 0); 449 return (NULL); 450 } 451 452 /* 453 * Time to load the socket module. 454 */ 455 ASSERT(newsp->sp_smod_info == NULL); 456 newsp->sp_smod_info = 457 smod_lookup_byname(newsp->sp_smod_name); 458 if (newsp->sp_smod_info == NULL) { 459 /* Failed to load */ 460 sockparams_destroy(newsp); 461 *errorp = ENXIO; 462 return (NULL); 463 } 464 465 /* 466 * The sockparams entry was created, now try to add it 467 * to the list. We need to hold the lock as a WRITER. 468 */ 469 rw_enter(&sp_ephem_lock, RW_WRITER); 470 sp = sockparams_find(&sp_ephem_list, family, type, protocol, 471 by_devpath, name); 472 if (sp != NULL) { 473 /* 474 * Someone has requested a matching entry, so just 475 * place a hold on it and release the entry we alloc'ed. 476 */ 477 SOCKPARAMS_INC_REF(sp); 478 rw_exit(&sp_ephem_lock); 479 480 sockparams_destroy(newsp); 481 } else { 482 SOCKPARAMS_INC_REF(newsp); 483 list_insert_tail(&sp_ephem_list, newsp); 484 rw_exit(&sp_ephem_lock); 485 486 sp = newsp; 487 } 488 ASSERT(*errorp == 0); 489 490 return (sp); 491 } 492 } 493 494 struct sockparams * 495 sockparams_hold_ephemeral_bydev(int family, int type, int protocol, 496 const char *dev, int kmflag, int *errorp) 497 { 498 return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE, 499 kmflag, errorp)); 500 } 501 502 struct sockparams * 503 sockparams_hold_ephemeral_bymod(int family, int type, int protocol, 504 const char *mod, int kmflag, int *errorp) 505 { 506 return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE, 507 kmflag, errorp)); 508 } 509 510 /* 511 * Called when the last socket using the ephemeral entry is dropping 512 * its' reference. To maintain lock order we must drop the sockparams 513 * lock before calling this function. As a result, a new reference 514 * might be placed on the entry, in which case there is nothing to 515 * do. However, if ref count goes to zero, we delete the entry. 516 */ 517 void 518 sockparams_ephemeral_drop_last_ref(struct sockparams *sp) 519 { 520 ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL); 521 ASSERT(MUTEX_NOT_HELD(&sp->sp_lock)); 522 523 rw_enter(&sp_ephem_lock, RW_WRITER); 524 mutex_enter(&sp->sp_lock); 525 526 if (--sp->sp_refcnt == 0) { 527 list_remove(&sp_ephem_list, sp); 528 mutex_exit(&sp->sp_lock); 529 rw_exit(&sp_ephem_lock); 530 531 sockparams_destroy(sp); 532 } else { 533 mutex_exit(&sp->sp_lock); 534 rw_exit(&sp_ephem_lock); 535 } 536 } 537 538 /* 539 * sockparams_add(struct sockparams *sp) 540 * 541 * Tries to add the given sockparams entry to the global list. 542 * 543 * Arguments: 544 * sp: the sockparms entry to add 545 * 546 * Returns: 547 * On success 0, but if an entry already exists, then EEXIST 548 * is returned. 549 * 550 * Locking: 551 * The caller can not be holding splist_lock. 552 */ 553 static int 554 sockparams_add(struct sockparams *sp) 555 { 556 ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL)); 557 558 rw_enter(&splist_lock, RW_WRITER); 559 if (sockparams_find(&sphead, sp->sp_family, sp->sp_type, 560 sp->sp_protocol, B_TRUE, NULL) != 0) { 561 rw_exit(&splist_lock); 562 return (EEXIST); 563 } else { 564 list_insert_tail(&sphead, sp); 565 rw_exit(&splist_lock); 566 return (0); 567 } 568 } 569 570 /* 571 * sockparams_delete(int family, int type, int protocol) 572 * 573 * Marks the sockparams entry for a specific family, type and protocol 574 * for deletion. The entry is removed from the list and destroyed 575 * if no one is holding a reference to it. 576 * 577 * Arguments: 578 * family, type, protocol: the socket type that should be removed. 579 * 580 * Returns: 581 * On success 0, otherwise ENXIO. 582 * 583 * Locking: 584 * Caller can not be holding splist_lock or the sp_lock of 585 * any sockparams entry. 586 */ 587 static int 588 sockparams_delete(int family, int type, int protocol) 589 { 590 struct sockparams *sp; 591 592 rw_enter(&splist_lock, RW_WRITER); 593 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 594 595 if (sp != NULL) { 596 /* 597 * If no one is holding a reference to the entry, then 598 * we go ahead and remove it from the list and then 599 * destroy it. 600 */ 601 mutex_enter(&sp->sp_lock); 602 if (sp->sp_refcnt != 0) { 603 mutex_exit(&sp->sp_lock); 604 rw_exit(&splist_lock); 605 return (EBUSY); 606 } 607 mutex_exit(&sp->sp_lock); 608 /* Delete the sockparams entry. */ 609 list_remove(&sphead, sp); 610 rw_exit(&splist_lock); 611 612 sockparams_destroy(sp); 613 return (0); 614 } else { 615 rw_exit(&splist_lock); 616 return (ENXIO); 617 } 618 } 619 620 /* 621 * soconfig(int family, int type, int protocol, 622 * char *devpath, int devpathlen, char *module) 623 * 624 * Add or delete an entry to the sockparams table. 625 * When devpath and module both are NULL, it will delete an entry. 626 * 627 * Arguments: 628 * family, type, protocol: the tuple in question 629 * devpath: STREAMS device path. Can be NULL for module based sockets. 630 * module : Name of the socket module. Can be NULL for STREAMS 631 * based sockets. 632 * devpathlen: length of the devpath string, or 0 if devpath 633 * was statically allocated. 634 * 635 * Note: 636 * This routine assumes that the caller has kmem_alloced 637 * devpath (if devpathlen > 0) and module for this routine to 638 * consume. 639 */ 640 int 641 soconfig(int family, int type, int protocol, 642 char *devpath, int devpathlen, char *module) 643 { 644 struct sockparams *sp; 645 int error = 0; 646 647 dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n", 648 family, type, protocol, devpath, devpathlen, 649 module == NULL ? "NULL" : module)); 650 651 if (sockfs_defer_nl7c_init) { 652 nl7c_init(); 653 sockfs_defer_nl7c_init = 0; 654 } 655 656 if (devpath == NULL && module == NULL) { 657 /* 658 * Delete existing entry, 659 * both socket module and STEAMS device. 660 */ 661 ASSERT(module == NULL); 662 error = sockparams_delete(family, type, protocol); 663 } else { 664 /* 665 * Adding an entry 666 * sockparams_create frees mod name and devpath upon failure. 667 */ 668 sp = sockparams_create(family, type, protocol, module, 669 devpath, devpathlen, 0, KM_SLEEP, &error); 670 671 if (sp != NULL) { 672 error = sockparams_add(sp); 673 if (error != 0) 674 sockparams_destroy(sp); 675 } 676 } 677 678 return (error); 679 } 680 681 /* 682 * solookup(int family, int type, int protocol, struct sockparams **spp) 683 * 684 * Lookup an entry in the sockparams list based on the triple. The returned 685 * entry either exactly match the given tuple, or it is the 'default' entry 686 * for the given <family, type>. A default entry is on with a protocol 687 * value of zero. 688 * 689 * Arguments: 690 * family, type, protocol: tuple to search for 691 * spp: Value-return argument 692 * 693 * Returns: 694 * If an entry is found, 0 is returned and *spp is set to point to the 695 * entry. In case an entry is not found, *spp is set to NULL, and an 696 * error code is returned. The errors are (in decreasing precedence): 697 * EAFNOSUPPORT - address family not in list 698 * EPROTONOSUPPORT - address family supported but not protocol. 699 * EPROTOTYPE - address family and protocol supported but not socket type. 700 * 701 * TODO: should use ddi_modopen()/ddi_modclose() 702 */ 703 int 704 solookup(int family, int type, int protocol, struct sockparams **spp) 705 { 706 struct sockparams *sp = NULL; 707 int error = 0; 708 709 *spp = NULL; 710 rw_enter(&splist_lock, RW_READER); 711 712 /* 713 * Search the sockparams list for an appropiate entry. 714 * Hopefully we find an entry that match the exact family, 715 * type and protocol specified by the user, in which case 716 * we return that entry. However, we also keep track of 717 * the default entry for a specific family and type, the 718 * entry of which would have a protocol value of 0. 719 */ 720 sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 721 722 if (sp == NULL) { 723 int found = 0; 724 725 /* Determine correct error code */ 726 for (sp = list_head(&sphead); sp != NULL; 727 sp = list_next(&sphead, sp)) { 728 if (sp->sp_family == family && found < 1) 729 found = 1; 730 if (sp->sp_family == family && 731 sp->sp_protocol == protocol && found < 2) 732 found = 2; 733 } 734 rw_exit(&splist_lock); 735 switch (found) { 736 case 0: 737 error = EAFNOSUPPORT; 738 break; 739 case 1: 740 error = EPROTONOSUPPORT; 741 break; 742 case 2: 743 error = EPROTOTYPE; 744 break; 745 } 746 return (error); 747 } 748 749 /* 750 * An entry was found. 751 * 752 * We put a hold on the entry early on, so if the 753 * sockmod is not loaded, and we have to exit 754 * splist_lock to call modload(), we know that the 755 * sockparams entry wont go away. That way we don't 756 * have to look up the entry once we come back from 757 * modload(). 758 */ 759 SOCKPARAMS_INC_REF(sp); 760 rw_exit(&splist_lock); 761 762 if (sp->sp_smod_info == NULL) { 763 sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name); 764 if (sp->sp_smod_info == NULL) { 765 /* 766 * We put a hold on the sockparams entry 767 * earlier, hoping everything would work out. 768 * That obviously did not happen, so release 769 * the hold here. 770 */ 771 SOCKPARAMS_DEC_REF(sp); 772 /* 773 * We should probably mark the sockparams as 774 * "bad", and redo the lookup skipping the 775 * "bad" entries. I.e., sp->sp_mod_state |= BAD, 776 * return (solookup(...)) 777 */ 778 return (ENXIO); 779 } 780 } 781 782 /* 783 * Alright, we have a valid sockparams entry. 784 */ 785 *spp = sp; 786 return (0); 787 } 788