10f1702c5SYu Xiangning /* 20f1702c5SYu Xiangning * CDDL HEADER START 30f1702c5SYu Xiangning * 40f1702c5SYu Xiangning * The contents of this file are subject to the terms of the 50f1702c5SYu Xiangning * Common Development and Distribution License (the "License"). 60f1702c5SYu Xiangning * You may not use this file except in compliance with the License. 70f1702c5SYu Xiangning * 80f1702c5SYu Xiangning * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90f1702c5SYu Xiangning * or http://www.opensolaris.org/os/licensing. 100f1702c5SYu Xiangning * See the License for the specific language governing permissions 110f1702c5SYu Xiangning * and limitations under the License. 120f1702c5SYu Xiangning * 130f1702c5SYu Xiangning * When distributing Covered Code, include this CDDL HEADER in each 140f1702c5SYu Xiangning * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150f1702c5SYu Xiangning * If applicable, add the following below this CDDL HEADER, with the 160f1702c5SYu Xiangning * fields enclosed by brackets "[]" replaced with your own identifying 170f1702c5SYu Xiangning * information: Portions Copyright [yyyy] [name of copyright owner] 180f1702c5SYu Xiangning * 190f1702c5SYu Xiangning * CDDL HEADER END 200f1702c5SYu Xiangning */ 210f1702c5SYu Xiangning 220f1702c5SYu Xiangning /* 23*22238f73Sshenjian * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240f1702c5SYu Xiangning * Use is subject to license terms. 250f1702c5SYu Xiangning */ 260f1702c5SYu Xiangning 270f1702c5SYu Xiangning #include <sys/types.h> 280f1702c5SYu Xiangning #include <sys/t_lock.h> 290f1702c5SYu Xiangning #include <sys/param.h> 300f1702c5SYu Xiangning #include <sys/systm.h> 310f1702c5SYu Xiangning #include <sys/sysmacros.h> 320f1702c5SYu Xiangning #include <sys/cmn_err.h> 330f1702c5SYu Xiangning #include <sys/list.h> 340f1702c5SYu Xiangning 350f1702c5SYu Xiangning #include <sys/stropts.h> 360f1702c5SYu Xiangning #include <sys/socket.h> 370f1702c5SYu Xiangning #include <sys/socketvar.h> 380f1702c5SYu Xiangning 390f1702c5SYu Xiangning #include <fs/sockfs/sockcommon.h> 400f1702c5SYu Xiangning #include <fs/sockfs/socktpi.h> 410f1702c5SYu Xiangning 420f1702c5SYu Xiangning /* 430f1702c5SYu Xiangning * Socket Parameters 440f1702c5SYu Xiangning * 450f1702c5SYu Xiangning * Socket parameter (struct sockparams) entries represent the socket types 460f1702c5SYu Xiangning * available on the system. 470f1702c5SYu Xiangning * 480f1702c5SYu Xiangning * Flags (sp_flags): 490f1702c5SYu Xiangning * 500f1702c5SYu Xiangning * SOCKPARAMS_EPHEMERAL: A temporary sockparams entry that will be deleted 510f1702c5SYu Xiangning * as soon as its' ref count drops to zero. In addition, ephemeral entries will 520f1702c5SYu Xiangning * never be hooked onto the global sockparams list. Ephemeral entries are 530f1702c5SYu Xiangning * created when application requests to create a socket using an application 540f1702c5SYu Xiangning * supplied device path, or when a socket is falling back to TPI. 550f1702c5SYu Xiangning * 560f1702c5SYu Xiangning * Lock order: 570f1702c5SYu Xiangning * The lock order is splist_lock -> sp_lock. 580f1702c5SYu Xiangning * The lock order is sp_ephem_lock -> sp_lock. 590f1702c5SYu Xiangning */ 600f1702c5SYu Xiangning extern int kobj_path_exists(char *, int); 610f1702c5SYu Xiangning extern void nl7c_init(void); 620f1702c5SYu Xiangning extern int sockfs_defer_nl7c_init; 630f1702c5SYu Xiangning 640f1702c5SYu Xiangning static int sockparams_sdev_init(struct sockparams *, char *, int); 650f1702c5SYu Xiangning static void sockparams_sdev_fini(struct sockparams *); 660f1702c5SYu Xiangning 670f1702c5SYu Xiangning /* 680f1702c5SYu Xiangning * Global sockparams list (populated via soconfig(1M)). 690f1702c5SYu Xiangning */ 700f1702c5SYu Xiangning static list_t sphead; 710f1702c5SYu Xiangning static krwlock_t splist_lock; 720f1702c5SYu Xiangning 730f1702c5SYu Xiangning /* 740f1702c5SYu Xiangning * List of ephemeral sockparams. 750f1702c5SYu Xiangning */ 760f1702c5SYu Xiangning static list_t sp_ephem_list; 770f1702c5SYu Xiangning static krwlock_t sp_ephem_lock; 780f1702c5SYu Xiangning 790f1702c5SYu Xiangning void 800f1702c5SYu Xiangning sockparams_init(void) 810f1702c5SYu Xiangning { 820f1702c5SYu Xiangning list_create(&sphead, sizeof (struct sockparams), 830f1702c5SYu Xiangning offsetof(struct sockparams, sp_node)); 840f1702c5SYu Xiangning list_create(&sp_ephem_list, sizeof (struct sockparams), 850f1702c5SYu Xiangning offsetof(struct sockparams, sp_node)); 860f1702c5SYu Xiangning 870f1702c5SYu Xiangning rw_init(&splist_lock, NULL, RW_DEFAULT, NULL); 880f1702c5SYu Xiangning rw_init(&sp_ephem_lock, NULL, RW_DEFAULT, NULL); 890f1702c5SYu Xiangning } 900f1702c5SYu Xiangning 910f1702c5SYu Xiangning /* 920f1702c5SYu Xiangning * sockparams_create(int family, int type, int protocol, char *modname, 930f1702c5SYu Xiangning * char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 940f1702c5SYu Xiangning * 950f1702c5SYu Xiangning * Create a new sockparams entry. 960f1702c5SYu Xiangning * 970f1702c5SYu Xiangning * Arguments: 980f1702c5SYu Xiangning * family, type, protocol: specifies the socket type 990f1702c5SYu Xiangning * modname: Name of the module associated with the socket type. The 1000f1702c5SYu Xiangning * module can be NULL if a device path is given, in which 1010f1702c5SYu Xiangning * case the TPI module is used. 1020f1702c5SYu Xiangning * devpath: Path to the STREAMS device. May be NULL for non-STREAMS 1030f1702c5SYu Xiangning * based transports, or those transports that do not provide 1040f1702c5SYu Xiangning * the capability to fallback to STREAMS. 1050f1702c5SYu Xiangning * devpathlen: Length of the devpath string. The argument can be 0, 1060f1702c5SYu Xiangning * indicating that devpath was allocated statically, and should 1070f1702c5SYu Xiangning * not be freed when the sockparams entry is destroyed. 1080f1702c5SYu Xiangning * 1090f1702c5SYu Xiangning * flags : SOCKPARAMS_EPHEMERAL is the only flag that is allowed. 1100f1702c5SYu Xiangning * kmflags: KM_{NO,}SLEEP 1110f1702c5SYu Xiangning * errorp : Value-return argument, set when an error occurs. 1120f1702c5SYu Xiangning * 1130f1702c5SYu Xiangning * Returns: 1140f1702c5SYu Xiangning * On success a new sockparams entry is returned, and *errorp is set 1150f1702c5SYu Xiangning * to 0. On failure NULL is returned and *errorp is set to indicate the 1160f1702c5SYu Xiangning * type of error that occured. 1170f1702c5SYu Xiangning * 1180f1702c5SYu Xiangning * Notes: 1190f1702c5SYu Xiangning * devpath and modname are freed upon failure. 1200f1702c5SYu Xiangning */ 1210f1702c5SYu Xiangning struct sockparams * 1220f1702c5SYu Xiangning sockparams_create(int family, int type, int protocol, char *modname, 1230f1702c5SYu Xiangning char *devpath, int devpathlen, int flags, int kmflags, int *errorp) 1240f1702c5SYu Xiangning { 1250f1702c5SYu Xiangning struct sockparams *sp = NULL; 1260f1702c5SYu Xiangning size_t size; 1270f1702c5SYu Xiangning 1280f1702c5SYu Xiangning ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0); 1290f1702c5SYu Xiangning if (flags & ~SOCKPARAMS_EPHEMERAL) { 1300f1702c5SYu Xiangning *errorp = EINVAL; 1310f1702c5SYu Xiangning goto error; 1320f1702c5SYu Xiangning } 1330f1702c5SYu Xiangning 1340f1702c5SYu Xiangning /* either a module or device must be given */ 1350f1702c5SYu Xiangning if (modname == NULL && devpath == NULL) { 1360f1702c5SYu Xiangning *errorp = EINVAL; 1370f1702c5SYu Xiangning goto error; 1380f1702c5SYu Xiangning } 1390f1702c5SYu Xiangning 1400f1702c5SYu Xiangning sp = kmem_zalloc(sizeof (*sp), kmflags); 1410f1702c5SYu Xiangning if (sp == NULL) { 1420f1702c5SYu Xiangning *errorp = ENOMEM; 1430f1702c5SYu Xiangning goto error; 1440f1702c5SYu Xiangning } 1450f1702c5SYu Xiangning sp->sp_family = family; 1460f1702c5SYu Xiangning sp->sp_type = type; 1470f1702c5SYu Xiangning sp->sp_protocol = protocol; 1480f1702c5SYu Xiangning sp->sp_refcnt = 0; 1490f1702c5SYu Xiangning sp->sp_flags = flags; 1500f1702c5SYu Xiangning 1510f1702c5SYu Xiangning if (modname != NULL) { 1520f1702c5SYu Xiangning sp->sp_smod_name = modname; 1530f1702c5SYu Xiangning } else { 1540f1702c5SYu Xiangning size = strlen(SOTPI_SMOD_NAME) + 1; 1550f1702c5SYu Xiangning modname = kmem_zalloc(size, kmflags); 1560f1702c5SYu Xiangning if (modname == NULL) { 1570f1702c5SYu Xiangning *errorp = ENOMEM; 1580f1702c5SYu Xiangning goto error; 1590f1702c5SYu Xiangning } 1600f1702c5SYu Xiangning sp->sp_smod_name = modname; 1610f1702c5SYu Xiangning (void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME); 1620f1702c5SYu Xiangning } 1630f1702c5SYu Xiangning 1640f1702c5SYu Xiangning if (devpath != NULL) { 1650f1702c5SYu Xiangning /* Set up the device entry. */ 1660f1702c5SYu Xiangning *errorp = sockparams_sdev_init(sp, devpath, devpathlen); 1670f1702c5SYu Xiangning if (*errorp != 0) 1680f1702c5SYu Xiangning goto error; 1690f1702c5SYu Xiangning } 1700f1702c5SYu Xiangning 1710f1702c5SYu Xiangning mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL); 1720f1702c5SYu Xiangning *errorp = 0; 1730f1702c5SYu Xiangning return (sp); 1740f1702c5SYu Xiangning error: 1750f1702c5SYu Xiangning ASSERT(*errorp != 0); 1760f1702c5SYu Xiangning if (modname != NULL) 1770f1702c5SYu Xiangning kmem_free(modname, strlen(modname) + 1); 1780f1702c5SYu Xiangning if (devpathlen != 0) 1790f1702c5SYu Xiangning kmem_free(devpath, devpathlen); 1800f1702c5SYu Xiangning if (sp != NULL) 1810f1702c5SYu Xiangning kmem_free(sp, sizeof (*sp)); 1820f1702c5SYu Xiangning return (NULL); 1830f1702c5SYu Xiangning } 1840f1702c5SYu Xiangning 1850f1702c5SYu Xiangning /* 1860f1702c5SYu Xiangning * Initialize the STREAMS device aspect of the sockparams entry. 1870f1702c5SYu Xiangning */ 1880f1702c5SYu Xiangning static int 1890f1702c5SYu Xiangning sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen) 1900f1702c5SYu Xiangning { 1910f1702c5SYu Xiangning vnode_t *vp = NULL; 1920f1702c5SYu Xiangning int error; 1930f1702c5SYu Xiangning 1940f1702c5SYu Xiangning ASSERT(devpath != NULL); 1950f1702c5SYu Xiangning 1960f1702c5SYu Xiangning if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) { 1970f1702c5SYu Xiangning dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n", 1980f1702c5SYu Xiangning devpath, error)); 1990f1702c5SYu Xiangning return (error); 2000f1702c5SYu Xiangning } 2010f1702c5SYu Xiangning 2020f1702c5SYu Xiangning ASSERT(vp != NULL); 2030f1702c5SYu Xiangning sp->sp_sdev_info.sd_vnode = vp; 2040f1702c5SYu Xiangning sp->sp_sdev_info.sd_devpath = devpath; 2050f1702c5SYu Xiangning sp->sp_sdev_info.sd_devpathlen = devpathlen; 2060f1702c5SYu Xiangning 2070f1702c5SYu Xiangning return (0); 2080f1702c5SYu Xiangning } 2090f1702c5SYu Xiangning 2100f1702c5SYu Xiangning /* 2110f1702c5SYu Xiangning * sockparams_destroy(struct sockparams *sp) 2120f1702c5SYu Xiangning * 2130f1702c5SYu Xiangning * Releases all the resources associated with the sockparams entry, 2140f1702c5SYu Xiangning * and frees the sockparams entry. 2150f1702c5SYu Xiangning * 2160f1702c5SYu Xiangning * Arguments: 2170f1702c5SYu Xiangning * sp: the sockparams entry to destroy. 2180f1702c5SYu Xiangning * 2190f1702c5SYu Xiangning * Returns: 2200f1702c5SYu Xiangning * Nothing. 2210f1702c5SYu Xiangning * 2220f1702c5SYu Xiangning * Locking: 2230f1702c5SYu Xiangning * The sp_lock of the entry can not be held. 2240f1702c5SYu Xiangning */ 2250f1702c5SYu Xiangning void 2260f1702c5SYu Xiangning sockparams_destroy(struct sockparams *sp) 2270f1702c5SYu Xiangning { 2280f1702c5SYu Xiangning ASSERT(sp->sp_refcnt == 0); 2290f1702c5SYu Xiangning ASSERT(!list_link_active(&sp->sp_node)); 2300f1702c5SYu Xiangning 2310f1702c5SYu Xiangning sockparams_sdev_fini(sp); 2320f1702c5SYu Xiangning 2330f1702c5SYu Xiangning if (sp->sp_smod_info != NULL) 2340f1702c5SYu Xiangning SMOD_DEC_REF(sp, sp->sp_smod_info); 2350f1702c5SYu Xiangning kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1); 2360f1702c5SYu Xiangning sp->sp_smod_name = NULL; 2370f1702c5SYu Xiangning sp->sp_smod_info = NULL; 2380f1702c5SYu Xiangning mutex_destroy(&sp->sp_lock); 2390f1702c5SYu Xiangning 2400f1702c5SYu Xiangning kmem_free(sp, sizeof (*sp)); 2410f1702c5SYu Xiangning } 2420f1702c5SYu Xiangning 2430f1702c5SYu Xiangning /* 2440f1702c5SYu Xiangning * Clean up the STREAMS device part of the sockparams entry. 2450f1702c5SYu Xiangning */ 2460f1702c5SYu Xiangning static void 2470f1702c5SYu Xiangning sockparams_sdev_fini(struct sockparams *sp) 2480f1702c5SYu Xiangning { 2490f1702c5SYu Xiangning sdev_info_t sd; 2500f1702c5SYu Xiangning 2510f1702c5SYu Xiangning /* 2520f1702c5SYu Xiangning * if the entry does not have a STREAMS device, then there 2530f1702c5SYu Xiangning * is nothing to do. 2540f1702c5SYu Xiangning */ 2550f1702c5SYu Xiangning if (!SOCKPARAMS_HAS_DEVICE(sp)) 2560f1702c5SYu Xiangning return; 2570f1702c5SYu Xiangning 2580f1702c5SYu Xiangning sd = sp->sp_sdev_info; 2590f1702c5SYu Xiangning if (sd.sd_vnode != NULL) 2600f1702c5SYu Xiangning VN_RELE(sd.sd_vnode); 2610f1702c5SYu Xiangning if (sd.sd_devpathlen != 0) 2620f1702c5SYu Xiangning kmem_free(sd.sd_devpath, sd.sd_devpathlen); 2630f1702c5SYu Xiangning 2640f1702c5SYu Xiangning sp->sp_sdev_info.sd_vnode = NULL; 2650f1702c5SYu Xiangning sp->sp_sdev_info.sd_devpath = NULL; 2660f1702c5SYu Xiangning } 2670f1702c5SYu Xiangning 2680f1702c5SYu Xiangning /* 2690f1702c5SYu Xiangning * Look for a matching sockparams entry on the given list. 2700f1702c5SYu Xiangning * The caller must hold the associated list lock. 2710f1702c5SYu Xiangning */ 2720f1702c5SYu Xiangning static struct sockparams * 2730f1702c5SYu Xiangning sockparams_find(list_t *list, int family, int type, int protocol, 274*22238f73Sshenjian boolean_t by_devpath, const char *name) 2750f1702c5SYu Xiangning { 2760f1702c5SYu Xiangning struct sockparams *sp; 2770f1702c5SYu Xiangning 2780f1702c5SYu Xiangning for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) { 279*22238f73Sshenjian if (sp->sp_family == family && sp->sp_type == type) { 2800f1702c5SYu Xiangning if (sp->sp_protocol == protocol) { 281*22238f73Sshenjian if (name == NULL) 2820f1702c5SYu Xiangning break; 283*22238f73Sshenjian else if (by_devpath && 2840f1702c5SYu Xiangning sp->sp_sdev_info.sd_devpath != NULL && 2850f1702c5SYu Xiangning strcmp(sp->sp_sdev_info.sd_devpath, 2860f1702c5SYu Xiangning name) == 0) 2870f1702c5SYu Xiangning break; 288*22238f73Sshenjian else if (strcmp(sp->sp_smod_name, name) == 0) 2890f1702c5SYu Xiangning break; 2900f1702c5SYu Xiangning } 2910f1702c5SYu Xiangning } 2920f1702c5SYu Xiangning } 293*22238f73Sshenjian return (sp); 2940f1702c5SYu Xiangning } 2950f1702c5SYu Xiangning 2960f1702c5SYu Xiangning /* 2970f1702c5SYu Xiangning * sockparams_hold_ephemeral() 2980f1702c5SYu Xiangning * 2990f1702c5SYu Xiangning * Returns an ephemeral sockparams entry of the requested family, type and 3000f1702c5SYu Xiangning * protocol. The entry is returned held, and the caller is responsible for 3010f1702c5SYu Xiangning * dropping the reference using SOCKPARAMS_DEC_REF() once done. 3020f1702c5SYu Xiangning * 3030f1702c5SYu Xiangning * All ephemeral entries are on list (sp_ephem_list). If there is an 3040f1702c5SYu Xiangning * entry on the list that match the search criteria, then a reference is 3050f1702c5SYu Xiangning * placed on that entry. Otherwise, a new entry is created and inserted 3060f1702c5SYu Xiangning * in the list. The entry is removed from the list when the last reference 3070f1702c5SYu Xiangning * is dropped. 3080f1702c5SYu Xiangning * 3090f1702c5SYu Xiangning * The tpi flag is used to determine whether name refers to a device or 3100f1702c5SYu Xiangning * module name. 3110f1702c5SYu Xiangning */ 3120f1702c5SYu Xiangning static struct sockparams * 3130f1702c5SYu Xiangning sockparams_hold_ephemeral(int family, int type, int protocol, 314*22238f73Sshenjian const char *name, boolean_t by_devpath, int kmflag, int *errorp) 3150f1702c5SYu Xiangning { 3160f1702c5SYu Xiangning struct sockparams *sp = NULL; 3170f1702c5SYu Xiangning *errorp = 0; 3180f1702c5SYu Xiangning 3190f1702c5SYu Xiangning /* 3200f1702c5SYu Xiangning * First look for an existing entry 3210f1702c5SYu Xiangning */ 3220f1702c5SYu Xiangning rw_enter(&sp_ephem_lock, RW_READER); 3230f1702c5SYu Xiangning sp = sockparams_find(&sp_ephem_list, family, type, protocol, 324*22238f73Sshenjian by_devpath, name); 3250f1702c5SYu Xiangning if (sp != NULL) { 3260f1702c5SYu Xiangning SOCKPARAMS_INC_REF(sp); 3270f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 3280f1702c5SYu Xiangning 3290f1702c5SYu Xiangning return (sp); 3300f1702c5SYu Xiangning } else { 3310f1702c5SYu Xiangning struct sockparams *newsp = NULL; 3320f1702c5SYu Xiangning char *namebuf = NULL; 3330f1702c5SYu Xiangning int namelen = 0; 3340f1702c5SYu Xiangning 3350f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 3360f1702c5SYu Xiangning 3370f1702c5SYu Xiangning namelen = strlen(name) + 1; 3380f1702c5SYu Xiangning namebuf = kmem_alloc(namelen, kmflag); 3390f1702c5SYu Xiangning if (namebuf == NULL) { 3400f1702c5SYu Xiangning *errorp = ENOMEM; 3410f1702c5SYu Xiangning return (NULL); 3420f1702c5SYu Xiangning } 3430f1702c5SYu Xiangning 3440f1702c5SYu Xiangning (void *)strncpy(namebuf, name, namelen); 345*22238f73Sshenjian if (by_devpath) { 3460f1702c5SYu Xiangning newsp = sockparams_create(family, type, 3470f1702c5SYu Xiangning protocol, NULL, namebuf, namelen, 3480f1702c5SYu Xiangning SOCKPARAMS_EPHEMERAL, kmflag, errorp); 3490f1702c5SYu Xiangning } else { 3500f1702c5SYu Xiangning newsp = sockparams_create(family, type, 3510f1702c5SYu Xiangning protocol, namebuf, NULL, 0, 3520f1702c5SYu Xiangning SOCKPARAMS_EPHEMERAL, kmflag, errorp); 3530f1702c5SYu Xiangning } 3540f1702c5SYu Xiangning 3550f1702c5SYu Xiangning if (newsp == NULL) { 3560f1702c5SYu Xiangning ASSERT(*errorp != 0); 3570f1702c5SYu Xiangning return (NULL); 3580f1702c5SYu Xiangning } 3590f1702c5SYu Xiangning 3600f1702c5SYu Xiangning /* 3610f1702c5SYu Xiangning * Time to load the socket module. 3620f1702c5SYu Xiangning */ 3630f1702c5SYu Xiangning ASSERT(newsp->sp_smod_info == NULL); 3640f1702c5SYu Xiangning newsp->sp_smod_info = 3650f1702c5SYu Xiangning smod_lookup_byname(newsp->sp_smod_name); 3660f1702c5SYu Xiangning if (newsp->sp_smod_info == NULL) { 3670f1702c5SYu Xiangning /* Failed to load */ 3680f1702c5SYu Xiangning sockparams_destroy(newsp); 3690f1702c5SYu Xiangning *errorp = ENXIO; 3700f1702c5SYu Xiangning return (NULL); 3710f1702c5SYu Xiangning } 3720f1702c5SYu Xiangning 3730f1702c5SYu Xiangning /* 3740f1702c5SYu Xiangning * The sockparams entry was created, now try to add it 3750f1702c5SYu Xiangning * to the list. We need to hold the lock as a WRITER. 3760f1702c5SYu Xiangning */ 3770f1702c5SYu Xiangning rw_enter(&sp_ephem_lock, RW_WRITER); 3780f1702c5SYu Xiangning sp = sockparams_find(&sp_ephem_list, family, type, protocol, 379*22238f73Sshenjian by_devpath, name); 3800f1702c5SYu Xiangning if (sp != NULL) { 3810f1702c5SYu Xiangning /* 3820f1702c5SYu Xiangning * Someone has requested a matching entry, so just 3830f1702c5SYu Xiangning * place a hold on it and release the entry we alloc'ed. 3840f1702c5SYu Xiangning */ 3850f1702c5SYu Xiangning SOCKPARAMS_INC_REF(sp); 3860f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 3870f1702c5SYu Xiangning 3880f1702c5SYu Xiangning sockparams_destroy(newsp); 3890f1702c5SYu Xiangning } else { 3900f1702c5SYu Xiangning SOCKPARAMS_INC_REF(newsp); 3910f1702c5SYu Xiangning list_insert_tail(&sp_ephem_list, newsp); 3920f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 3930f1702c5SYu Xiangning 3940f1702c5SYu Xiangning sp = newsp; 3950f1702c5SYu Xiangning } 3960f1702c5SYu Xiangning ASSERT(*errorp == 0); 3970f1702c5SYu Xiangning 3980f1702c5SYu Xiangning return (sp); 3990f1702c5SYu Xiangning } 4000f1702c5SYu Xiangning } 4010f1702c5SYu Xiangning 4020f1702c5SYu Xiangning struct sockparams * 4030f1702c5SYu Xiangning sockparams_hold_ephemeral_bydev(int family, int type, int protocol, 4040f1702c5SYu Xiangning const char *dev, int kmflag, int *errorp) 4050f1702c5SYu Xiangning { 4060f1702c5SYu Xiangning return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE, 4070f1702c5SYu Xiangning kmflag, errorp)); 4080f1702c5SYu Xiangning } 4090f1702c5SYu Xiangning 4100f1702c5SYu Xiangning struct sockparams * 4110f1702c5SYu Xiangning sockparams_hold_ephemeral_bymod(int family, int type, int protocol, 4120f1702c5SYu Xiangning const char *mod, int kmflag, int *errorp) 4130f1702c5SYu Xiangning { 4140f1702c5SYu Xiangning return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE, 4150f1702c5SYu Xiangning kmflag, errorp)); 4160f1702c5SYu Xiangning } 4170f1702c5SYu Xiangning 4180f1702c5SYu Xiangning /* 4190f1702c5SYu Xiangning * Called when the last socket using the ephemeral entry is dropping 4200f1702c5SYu Xiangning * its' reference. To maintain lock order we must drop the sockparams 4210f1702c5SYu Xiangning * lock before calling this function. As a result, a new reference 4220f1702c5SYu Xiangning * might be placed on the entry, in which case there is nothing to 4230f1702c5SYu Xiangning * do. However, if ref count goes to zero, we delete the entry. 4240f1702c5SYu Xiangning */ 4250f1702c5SYu Xiangning void 4260f1702c5SYu Xiangning sockparams_ephemeral_drop_last_ref(struct sockparams *sp) 4270f1702c5SYu Xiangning { 4280f1702c5SYu Xiangning ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL); 4290f1702c5SYu Xiangning ASSERT(MUTEX_NOT_HELD(&sp->sp_lock)); 4300f1702c5SYu Xiangning 4310f1702c5SYu Xiangning rw_enter(&sp_ephem_lock, RW_WRITER); 4320f1702c5SYu Xiangning mutex_enter(&sp->sp_lock); 4330f1702c5SYu Xiangning 4340f1702c5SYu Xiangning if (--sp->sp_refcnt == 0) { 4350f1702c5SYu Xiangning list_remove(&sp_ephem_list, sp); 4360f1702c5SYu Xiangning mutex_exit(&sp->sp_lock); 4370f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 4380f1702c5SYu Xiangning 4390f1702c5SYu Xiangning sockparams_destroy(sp); 4400f1702c5SYu Xiangning } else { 4410f1702c5SYu Xiangning mutex_exit(&sp->sp_lock); 4420f1702c5SYu Xiangning rw_exit(&sp_ephem_lock); 4430f1702c5SYu Xiangning } 4440f1702c5SYu Xiangning } 4450f1702c5SYu Xiangning 4460f1702c5SYu Xiangning /* 4470f1702c5SYu Xiangning * sockparams_add(struct sockparams *sp) 4480f1702c5SYu Xiangning * 4490f1702c5SYu Xiangning * Tries to add the given sockparams entry to the global list. 4500f1702c5SYu Xiangning * 4510f1702c5SYu Xiangning * Arguments: 4520f1702c5SYu Xiangning * sp: the sockparms entry to add 4530f1702c5SYu Xiangning * 4540f1702c5SYu Xiangning * Returns: 4550f1702c5SYu Xiangning * On success 0, but if an entry already exists, then EEXIST 4560f1702c5SYu Xiangning * is returned. 4570f1702c5SYu Xiangning * 4580f1702c5SYu Xiangning * Locking: 4590f1702c5SYu Xiangning * The caller can not be holding splist_lock. 4600f1702c5SYu Xiangning */ 4610f1702c5SYu Xiangning static int 4620f1702c5SYu Xiangning sockparams_add(struct sockparams *sp) 4630f1702c5SYu Xiangning { 4640f1702c5SYu Xiangning ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL)); 4650f1702c5SYu Xiangning 4660f1702c5SYu Xiangning rw_enter(&splist_lock, RW_WRITER); 4670f1702c5SYu Xiangning if (sockparams_find(&sphead, sp->sp_family, sp->sp_type, 468*22238f73Sshenjian sp->sp_protocol, B_TRUE, NULL) != 0) { 4690f1702c5SYu Xiangning rw_exit(&splist_lock); 4700f1702c5SYu Xiangning return (EEXIST); 4710f1702c5SYu Xiangning } else { 4720f1702c5SYu Xiangning list_insert_tail(&sphead, sp); 4730f1702c5SYu Xiangning rw_exit(&splist_lock); 4740f1702c5SYu Xiangning return (0); 4750f1702c5SYu Xiangning } 4760f1702c5SYu Xiangning } 4770f1702c5SYu Xiangning 4780f1702c5SYu Xiangning /* 4790f1702c5SYu Xiangning * sockparams_delete(int family, int type, int protocol) 4800f1702c5SYu Xiangning * 4810f1702c5SYu Xiangning * Marks the sockparams entry for a specific family, type and protocol 4820f1702c5SYu Xiangning * for deletion. The entry is removed from the list and destroyed 4830f1702c5SYu Xiangning * if no one is holding a reference to it. 4840f1702c5SYu Xiangning * 4850f1702c5SYu Xiangning * Arguments: 4860f1702c5SYu Xiangning * family, type, protocol: the socket type that should be removed. 4870f1702c5SYu Xiangning * 4880f1702c5SYu Xiangning * Returns: 4890f1702c5SYu Xiangning * On success 0, otherwise ENXIO. 4900f1702c5SYu Xiangning * 4910f1702c5SYu Xiangning * Locking: 4920f1702c5SYu Xiangning * Caller can not be holding splist_lock or the sp_lock of 4930f1702c5SYu Xiangning * any sockparams entry. 4940f1702c5SYu Xiangning */ 4950f1702c5SYu Xiangning static int 4960f1702c5SYu Xiangning sockparams_delete(int family, int type, int protocol) 4970f1702c5SYu Xiangning { 4980f1702c5SYu Xiangning struct sockparams *sp; 4990f1702c5SYu Xiangning 5000f1702c5SYu Xiangning rw_enter(&splist_lock, RW_WRITER); 501*22238f73Sshenjian sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 5020f1702c5SYu Xiangning 5030f1702c5SYu Xiangning if (sp != NULL) { 5040f1702c5SYu Xiangning /* 5050f1702c5SYu Xiangning * If no one is holding a reference to the entry, then 5060f1702c5SYu Xiangning * we go ahead and remove it from the list and then 5070f1702c5SYu Xiangning * destroy it. 5080f1702c5SYu Xiangning */ 5090f1702c5SYu Xiangning mutex_enter(&sp->sp_lock); 5100f1702c5SYu Xiangning if (sp->sp_refcnt != 0) { 5110f1702c5SYu Xiangning mutex_exit(&sp->sp_lock); 5120f1702c5SYu Xiangning rw_exit(&splist_lock); 5130f1702c5SYu Xiangning return (EBUSY); 5140f1702c5SYu Xiangning } 5150f1702c5SYu Xiangning mutex_exit(&sp->sp_lock); 5160f1702c5SYu Xiangning /* Delete the sockparams entry. */ 5170f1702c5SYu Xiangning list_remove(&sphead, sp); 5180f1702c5SYu Xiangning rw_exit(&splist_lock); 5190f1702c5SYu Xiangning 5200f1702c5SYu Xiangning sockparams_destroy(sp); 5210f1702c5SYu Xiangning return (0); 5220f1702c5SYu Xiangning } else { 5230f1702c5SYu Xiangning rw_exit(&splist_lock); 5240f1702c5SYu Xiangning return (ENXIO); 5250f1702c5SYu Xiangning } 5260f1702c5SYu Xiangning } 5270f1702c5SYu Xiangning 5280f1702c5SYu Xiangning /* 5290f1702c5SYu Xiangning * soconfig(int family, int type, int protocol, 5300f1702c5SYu Xiangning * char *devpath, int devpathlen, char *module) 5310f1702c5SYu Xiangning * 5320f1702c5SYu Xiangning * Add or delete an entry to the sockparams table. 5330f1702c5SYu Xiangning * When devpath and module both are NULL, it will delete an entry. 5340f1702c5SYu Xiangning * 5350f1702c5SYu Xiangning * Arguments: 5360f1702c5SYu Xiangning * family, type, protocol: the tuple in question 5370f1702c5SYu Xiangning * devpath: STREAMS device path. Can be NULL for module based sockets. 5380f1702c5SYu Xiangning * module : Name of the socket module. Can be NULL for STREAMS 5390f1702c5SYu Xiangning * based sockets. 5400f1702c5SYu Xiangning * devpathlen: length of the devpath string, or 0 if devpath 5410f1702c5SYu Xiangning * was statically allocated. 5420f1702c5SYu Xiangning * 5430f1702c5SYu Xiangning * Note: 5440f1702c5SYu Xiangning * This routine assumes that the caller has kmem_alloced 5450f1702c5SYu Xiangning * devpath (if devpathlen > 0) and module for this routine to 5460f1702c5SYu Xiangning * consume. 5470f1702c5SYu Xiangning */ 5480f1702c5SYu Xiangning int 5490f1702c5SYu Xiangning soconfig(int family, int type, int protocol, 5500f1702c5SYu Xiangning char *devpath, int devpathlen, char *module) 5510f1702c5SYu Xiangning { 5520f1702c5SYu Xiangning struct sockparams *sp; 5530f1702c5SYu Xiangning int error = 0; 5540f1702c5SYu Xiangning 5550f1702c5SYu Xiangning dprint(0, ("soconfig(%d,%d,%d,%s,%d,%s)\n", 5560f1702c5SYu Xiangning family, type, protocol, devpath, devpathlen, 5570f1702c5SYu Xiangning module == NULL ? "NULL" : module)); 5580f1702c5SYu Xiangning 5590f1702c5SYu Xiangning if (sockfs_defer_nl7c_init) { 5600f1702c5SYu Xiangning nl7c_init(); 5610f1702c5SYu Xiangning sockfs_defer_nl7c_init = 0; 5620f1702c5SYu Xiangning } 5630f1702c5SYu Xiangning 5640f1702c5SYu Xiangning if (devpath == NULL && module == NULL) { 5650f1702c5SYu Xiangning /* 5660f1702c5SYu Xiangning * Delete existing entry, 5670f1702c5SYu Xiangning * both socket module and STEAMS device. 5680f1702c5SYu Xiangning */ 5690f1702c5SYu Xiangning ASSERT(module == NULL); 5700f1702c5SYu Xiangning error = sockparams_delete(family, type, protocol); 5710f1702c5SYu Xiangning } else { 5720f1702c5SYu Xiangning /* 5730f1702c5SYu Xiangning * Adding an entry 5740f1702c5SYu Xiangning * sockparams_create frees mod name and devpath upon failure. 5750f1702c5SYu Xiangning */ 5760f1702c5SYu Xiangning sp = sockparams_create(family, type, protocol, module, 5770f1702c5SYu Xiangning devpath, devpathlen, 0, KM_SLEEP, &error); 5780f1702c5SYu Xiangning 5790f1702c5SYu Xiangning if (sp != NULL) { 5800f1702c5SYu Xiangning error = sockparams_add(sp); 5810f1702c5SYu Xiangning if (error != 0) 5820f1702c5SYu Xiangning sockparams_destroy(sp); 5830f1702c5SYu Xiangning } 5840f1702c5SYu Xiangning } 5850f1702c5SYu Xiangning 5860f1702c5SYu Xiangning return (error); 5870f1702c5SYu Xiangning } 5880f1702c5SYu Xiangning 5890f1702c5SYu Xiangning /* 5900f1702c5SYu Xiangning * solookup(int family, int type, int protocol, struct sockparams **spp) 5910f1702c5SYu Xiangning * 5920f1702c5SYu Xiangning * Lookup an entry in the sockparams list based on the triple. The returned 5930f1702c5SYu Xiangning * entry either exactly match the given tuple, or it is the 'default' entry 5940f1702c5SYu Xiangning * for the given <family, type>. A default entry is on with a protocol 5950f1702c5SYu Xiangning * value of zero. 5960f1702c5SYu Xiangning * 5970f1702c5SYu Xiangning * Arguments: 5980f1702c5SYu Xiangning * family, type, protocol: tuple to search for 5990f1702c5SYu Xiangning * spp: Value-return argument 6000f1702c5SYu Xiangning * 6010f1702c5SYu Xiangning * Returns: 6020f1702c5SYu Xiangning * If an entry is found, 0 is returned and *spp is set to point to the 6030f1702c5SYu Xiangning * entry. In case an entry is not found, *spp is set to NULL, and an 6040f1702c5SYu Xiangning * error code is returned. The errors are (in decreasing precedence): 6050f1702c5SYu Xiangning * EAFNOSUPPORT - address family not in list 6060f1702c5SYu Xiangning * EPROTONOSUPPORT - address family supported but not protocol. 6070f1702c5SYu Xiangning * EPROTOTYPE - address family and protocol supported but not socket type. 6080f1702c5SYu Xiangning * 6090f1702c5SYu Xiangning * TODO: should use ddi_modopen()/ddi_modclose() 6100f1702c5SYu Xiangning */ 6110f1702c5SYu Xiangning int 6120f1702c5SYu Xiangning solookup(int family, int type, int protocol, struct sockparams **spp) 6130f1702c5SYu Xiangning { 6140f1702c5SYu Xiangning struct sockparams *sp = NULL; 6150f1702c5SYu Xiangning int error = 0; 6160f1702c5SYu Xiangning 6170f1702c5SYu Xiangning *spp = NULL; 6180f1702c5SYu Xiangning rw_enter(&splist_lock, RW_READER); 6190f1702c5SYu Xiangning 6200f1702c5SYu Xiangning /* 6210f1702c5SYu Xiangning * Search the sockparams list for an appropiate entry. 6220f1702c5SYu Xiangning * Hopefully we find an entry that match the exact family, 6230f1702c5SYu Xiangning * type and protocol specified by the user, in which case 6240f1702c5SYu Xiangning * we return that entry. However, we also keep track of 6250f1702c5SYu Xiangning * the default entry for a specific family and type, the 6260f1702c5SYu Xiangning * entry of which would have a protocol value of 0. 6270f1702c5SYu Xiangning */ 628*22238f73Sshenjian sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL); 6290f1702c5SYu Xiangning 6300f1702c5SYu Xiangning if (sp == NULL) { 6310f1702c5SYu Xiangning int found = 0; 6320f1702c5SYu Xiangning 6330f1702c5SYu Xiangning /* Determine correct error code */ 6340f1702c5SYu Xiangning for (sp = list_head(&sphead); sp != NULL; 6350f1702c5SYu Xiangning sp = list_next(&sphead, sp)) { 6360f1702c5SYu Xiangning if (sp->sp_family == family && found < 1) 6370f1702c5SYu Xiangning found = 1; 6380f1702c5SYu Xiangning if (sp->sp_family == family && 6390f1702c5SYu Xiangning sp->sp_protocol == protocol && found < 2) 6400f1702c5SYu Xiangning found = 2; 6410f1702c5SYu Xiangning } 6420f1702c5SYu Xiangning rw_exit(&splist_lock); 6430f1702c5SYu Xiangning switch (found) { 6440f1702c5SYu Xiangning case 0: 6450f1702c5SYu Xiangning error = EAFNOSUPPORT; 6460f1702c5SYu Xiangning break; 6470f1702c5SYu Xiangning case 1: 6480f1702c5SYu Xiangning error = EPROTONOSUPPORT; 6490f1702c5SYu Xiangning break; 6500f1702c5SYu Xiangning case 2: 6510f1702c5SYu Xiangning error = EPROTOTYPE; 6520f1702c5SYu Xiangning break; 6530f1702c5SYu Xiangning } 6540f1702c5SYu Xiangning return (error); 6550f1702c5SYu Xiangning } 6560f1702c5SYu Xiangning 6570f1702c5SYu Xiangning /* 6580f1702c5SYu Xiangning * An entry was found. 6590f1702c5SYu Xiangning * 6600f1702c5SYu Xiangning * We put a hold on the entry early on, so if the 6610f1702c5SYu Xiangning * sockmod is not loaded, and we have to exit 6620f1702c5SYu Xiangning * splist_lock to call modload(), we know that the 6630f1702c5SYu Xiangning * sockparams entry wont go away. That way we don't 6640f1702c5SYu Xiangning * have to look up the entry once we come back from 6650f1702c5SYu Xiangning * modload(). 6660f1702c5SYu Xiangning */ 6670f1702c5SYu Xiangning SOCKPARAMS_INC_REF(sp); 6680f1702c5SYu Xiangning rw_exit(&splist_lock); 6690f1702c5SYu Xiangning 6700f1702c5SYu Xiangning if (sp->sp_smod_info == NULL) { 6710f1702c5SYu Xiangning sp->sp_smod_info = smod_lookup_byname(sp->sp_smod_name); 6720f1702c5SYu Xiangning if (sp->sp_smod_info == NULL) { 6730f1702c5SYu Xiangning /* 6740f1702c5SYu Xiangning * We put a hold on the sockparams entry 6750f1702c5SYu Xiangning * earlier, hoping everything would work out. 6760f1702c5SYu Xiangning * That obviously did not happen, so release 6770f1702c5SYu Xiangning * the hold here. 6780f1702c5SYu Xiangning */ 6790f1702c5SYu Xiangning SOCKPARAMS_DEC_REF(sp); 6800f1702c5SYu Xiangning /* 6810f1702c5SYu Xiangning * We should probably mark the sockparams as 6820f1702c5SYu Xiangning * "bad", and redo the lookup skipping the 6830f1702c5SYu Xiangning * "bad" entries. I.e., sp->sp_mod_state |= BAD, 6840f1702c5SYu Xiangning * return (solookup(...)) 6850f1702c5SYu Xiangning */ 6860f1702c5SYu Xiangning return (ENXIO); 6870f1702c5SYu Xiangning } 6880f1702c5SYu Xiangning } 6890f1702c5SYu Xiangning 6900f1702c5SYu Xiangning /* 6910f1702c5SYu Xiangning * Alright, we have a valid sockparams entry. 6920f1702c5SYu Xiangning */ 6930f1702c5SYu Xiangning *spp = sp; 6940f1702c5SYu Xiangning return (0); 6950f1702c5SYu Xiangning } 696