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