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 /* 22*3e95bd4aSAnders Persson * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 230f1702c5SYu Xiangning */ 240f1702c5SYu Xiangning 250f1702c5SYu Xiangning #include <sys/sysmacros.h> 260f1702c5SYu Xiangning #include <sys/atomic.h> 270f1702c5SYu Xiangning #include <sys/strsubr.h> 280f1702c5SYu Xiangning #include <sys/socket.h> 290f1702c5SYu Xiangning #include <sys/socketvar.h> 300f1702c5SYu Xiangning #include <sys/cmn_err.h> 310f1702c5SYu Xiangning #include <sys/modctl.h> 320f1702c5SYu Xiangning #include <sys/sdt.h> 330f1702c5SYu Xiangning 340f1702c5SYu Xiangning list_t smod_list; 350f1702c5SYu Xiangning kmutex_t smod_list_lock; 360f1702c5SYu Xiangning 370f1702c5SYu Xiangning so_create_func_t sock_comm_create_function; 380f1702c5SYu Xiangning so_destroy_func_t sock_comm_destroy_function; 390f1702c5SYu Xiangning 400f1702c5SYu Xiangning static smod_info_t *smod_create(const char *); 410f1702c5SYu Xiangning static void smod_destroy(smod_info_t *); 420f1702c5SYu Xiangning 430f1702c5SYu Xiangning extern void smod_add(smod_info_t *); 440f1702c5SYu Xiangning 450f1702c5SYu Xiangning void 460f1702c5SYu Xiangning smod_init(void) 470f1702c5SYu Xiangning { 480f1702c5SYu Xiangning list_create(&smod_list, sizeof (smod_info_t), 490f1702c5SYu Xiangning offsetof(smod_info_t, smod_node)); 500f1702c5SYu Xiangning mutex_init(&smod_list_lock, NULL, MUTEX_DEFAULT, NULL); 510f1702c5SYu Xiangning } 520f1702c5SYu Xiangning 530f1702c5SYu Xiangning static smod_info_t * 540f1702c5SYu Xiangning smod_find(const char *modname) 550f1702c5SYu Xiangning { 560f1702c5SYu Xiangning smod_info_t *smodp; 570f1702c5SYu Xiangning 580f1702c5SYu Xiangning ASSERT(MUTEX_HELD(&smod_list_lock)); 590f1702c5SYu Xiangning 600f1702c5SYu Xiangning for (smodp = list_head(&smod_list); smodp != NULL; 610f1702c5SYu Xiangning smodp = list_next(&smod_list, smodp)) 620f1702c5SYu Xiangning if (strcmp(smodp->smod_name, modname) == 0) 630f1702c5SYu Xiangning return (smodp); 640f1702c5SYu Xiangning return (NULL); 650f1702c5SYu Xiangning } 660f1702c5SYu Xiangning 670f1702c5SYu Xiangning /* 680f1702c5SYu Xiangning * Register the socket module. 690f1702c5SYu Xiangning */ 700f1702c5SYu Xiangning int 710f1702c5SYu Xiangning smod_register(const smod_reg_t *reg) 720f1702c5SYu Xiangning { 730f1702c5SYu Xiangning smod_info_t *smodp; 740f1702c5SYu Xiangning 750f1702c5SYu Xiangning /* 760f1702c5SYu Xiangning * Make sure the socket module does not depend on capabilities 770f1702c5SYu Xiangning * not available on the system. 780f1702c5SYu Xiangning */ 790f1702c5SYu Xiangning if (reg->smod_version != SOCKMOD_VERSION || 800f1702c5SYu Xiangning reg->smod_dc_version != SOCK_DC_VERSION || 810f1702c5SYu Xiangning reg->smod_uc_version != SOCK_UC_VERSION) { 820f1702c5SYu Xiangning cmn_err(CE_WARN, 830f1702c5SYu Xiangning "Failed to register socket module %s: version mismatch", 840f1702c5SYu Xiangning reg->smod_name); 850f1702c5SYu Xiangning return (EINVAL); 860f1702c5SYu Xiangning } 870f1702c5SYu Xiangning 880f1702c5SYu Xiangning #ifdef DEBUG 890f1702c5SYu Xiangning mutex_enter(&smod_list_lock); 900f1702c5SYu Xiangning if ((smodp = smod_find(reg->smod_name)) != NULL) { 910f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 920f1702c5SYu Xiangning return (EEXIST); 930f1702c5SYu Xiangning } 940f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 950f1702c5SYu Xiangning #endif 960f1702c5SYu Xiangning 970f1702c5SYu Xiangning smodp = smod_create(reg->smod_name); 980f1702c5SYu Xiangning smodp->smod_version = reg->smod_version; 990f1702c5SYu Xiangning if (strcmp(smodp->smod_name, SOTPI_SMOD_NAME) == 0 || 1000f1702c5SYu Xiangning strcmp(smodp->smod_name, "socksctp") == 0 || 1010f1702c5SYu Xiangning strcmp(smodp->smod_name, "socksdp") == 0) { 1020f1702c5SYu Xiangning ASSERT(smodp->smod_proto_create_func == NULL); 1030f1702c5SYu Xiangning ASSERT(reg->__smod_priv != NULL); 1040f1702c5SYu Xiangning smodp->smod_sock_create_func = 1050f1702c5SYu Xiangning reg->__smod_priv->smodp_sock_create_func; 1060f1702c5SYu Xiangning smodp->smod_sock_destroy_func = 1070f1702c5SYu Xiangning reg->__smod_priv->smodp_sock_destroy_func; 1080f1702c5SYu Xiangning smodp->smod_proto_create_func = NULL; 1090f1702c5SYu Xiangning } else { 1100f1702c5SYu Xiangning if (reg->smod_proto_create_func == NULL || 1110f1702c5SYu Xiangning (reg->__smod_priv != NULL && 1120f1702c5SYu Xiangning (reg->__smod_priv->smodp_sock_create_func != NULL || 1130f1702c5SYu Xiangning reg->__smod_priv->smodp_sock_destroy_func != NULL))) { 1140f1702c5SYu Xiangning #ifdef DEBUG 1150f1702c5SYu Xiangning cmn_err(CE_CONT, "smod_register of %s failed", 1160f1702c5SYu Xiangning smodp->smod_name); 1170f1702c5SYu Xiangning #endif 1180f1702c5SYu Xiangning smod_destroy(smodp); 1190f1702c5SYu Xiangning return (EINVAL); 1200f1702c5SYu Xiangning } 1210f1702c5SYu Xiangning smodp->smod_proto_create_func = reg->smod_proto_create_func; 1220f1702c5SYu Xiangning smodp->smod_sock_create_func = sock_comm_create_function; 1230f1702c5SYu Xiangning smodp->smod_sock_destroy_func = sock_comm_destroy_function; 1240f1702c5SYu Xiangning smodp->smod_uc_version = reg->smod_uc_version; 1250f1702c5SYu Xiangning smodp->smod_dc_version = reg->smod_dc_version; 1260f1702c5SYu Xiangning if (reg->__smod_priv != NULL) { 1270f1702c5SYu Xiangning smodp->smod_proto_fallback_func = 1280f1702c5SYu Xiangning reg->__smod_priv->smodp_proto_fallback_func; 129*3e95bd4aSAnders Persson smodp->smod_fallback_devpath_v4 = 130*3e95bd4aSAnders Persson reg->__smod_priv->smodp_fallback_devpath_v4; 131*3e95bd4aSAnders Persson smodp->smod_fallback_devpath_v6 = 132*3e95bd4aSAnders Persson reg->__smod_priv->smodp_fallback_devpath_v6; 1330f1702c5SYu Xiangning } 1340f1702c5SYu Xiangning } 1350f1702c5SYu Xiangning smod_add(smodp); 1360f1702c5SYu Xiangning return (0); 1370f1702c5SYu Xiangning } 1380f1702c5SYu Xiangning 1390f1702c5SYu Xiangning /* 1400f1702c5SYu Xiangning * Unregister the socket module 1410f1702c5SYu Xiangning */ 1420f1702c5SYu Xiangning int 1430f1702c5SYu Xiangning smod_unregister(const char *mod_name) 1440f1702c5SYu Xiangning { 1450f1702c5SYu Xiangning smod_info_t *smodp; 1460f1702c5SYu Xiangning 1470f1702c5SYu Xiangning mutex_enter(&smod_list_lock); 1480f1702c5SYu Xiangning if ((smodp = smod_find(mod_name)) != NULL) { 1490f1702c5SYu Xiangning if (smodp->smod_refcnt != 0) { 1500f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 1510f1702c5SYu Xiangning return (EBUSY); 1520f1702c5SYu Xiangning } else { 1530f1702c5SYu Xiangning /* 1540f1702c5SYu Xiangning * Delete the entry from the socket module list. 1550f1702c5SYu Xiangning */ 1560f1702c5SYu Xiangning list_remove(&smod_list, smodp); 1570f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 1580f1702c5SYu Xiangning 1590f1702c5SYu Xiangning smod_destroy(smodp); 1600f1702c5SYu Xiangning return (0); 1610f1702c5SYu Xiangning } 1620f1702c5SYu Xiangning } 1630f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 1640f1702c5SYu Xiangning 1650f1702c5SYu Xiangning return (ENXIO); 1660f1702c5SYu Xiangning } 1670f1702c5SYu Xiangning 1680f1702c5SYu Xiangning /* 1690f1702c5SYu Xiangning * Initialize the socket module entry. 1700f1702c5SYu Xiangning */ 1710f1702c5SYu Xiangning static smod_info_t * 1720f1702c5SYu Xiangning smod_create(const char *modname) 1730f1702c5SYu Xiangning { 1740f1702c5SYu Xiangning smod_info_t *smodp; 1750f1702c5SYu Xiangning int len; 1760f1702c5SYu Xiangning 1770f1702c5SYu Xiangning smodp = kmem_zalloc(sizeof (*smodp), KM_SLEEP); 1780f1702c5SYu Xiangning len = strlen(modname) + 1; 1790f1702c5SYu Xiangning smodp->smod_name = kmem_alloc(len, KM_SLEEP); 1800f1702c5SYu Xiangning bcopy(modname, smodp->smod_name, len); 1810f1702c5SYu Xiangning smodp->smod_name[len - 1] = '\0'; 1820f1702c5SYu Xiangning return (smodp); 1830f1702c5SYu Xiangning } 1840f1702c5SYu Xiangning 1850f1702c5SYu Xiangning /* 1860f1702c5SYu Xiangning * Clean up the socket module part of the sockparams entry. 1870f1702c5SYu Xiangning */ 1880f1702c5SYu Xiangning static void 1890f1702c5SYu Xiangning smod_destroy(smod_info_t *smodp) 1900f1702c5SYu Xiangning { 1910f1702c5SYu Xiangning ASSERT(smodp->smod_name != NULL); 1920f1702c5SYu Xiangning ASSERT(smodp->smod_refcnt == 0); 1930f1702c5SYu Xiangning ASSERT(!list_link_active(&smodp->smod_node)); 1940f1702c5SYu Xiangning ASSERT(strcmp(smodp->smod_name, "socktpi") != 0); 1950f1702c5SYu Xiangning 1960f1702c5SYu Xiangning kmem_free(smodp->smod_name, strlen(smodp->smod_name) + 1); 1970f1702c5SYu Xiangning smodp->smod_name = NULL; 1980f1702c5SYu Xiangning smodp->smod_proto_create_func = NULL; 1990f1702c5SYu Xiangning smodp->smod_sock_create_func = NULL; 2000f1702c5SYu Xiangning smodp->smod_sock_destroy_func = NULL; 2010f1702c5SYu Xiangning kmem_free(smodp, sizeof (*smodp)); 2020f1702c5SYu Xiangning } 2030f1702c5SYu Xiangning 2040f1702c5SYu Xiangning /* 2050f1702c5SYu Xiangning * Add an entry at the front of the socket module list. 2060f1702c5SYu Xiangning */ 2070f1702c5SYu Xiangning void 2080f1702c5SYu Xiangning smod_add(smod_info_t *smodp) 2090f1702c5SYu Xiangning { 2100f1702c5SYu Xiangning ASSERT(smodp != NULL); 2110f1702c5SYu Xiangning mutex_enter(&smod_list_lock); 2120f1702c5SYu Xiangning list_insert_head(&smod_list, smodp); 2130f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 2140f1702c5SYu Xiangning } 2150f1702c5SYu Xiangning 2160f1702c5SYu Xiangning /* 2170f1702c5SYu Xiangning * Lookup the socket module table by the socket module name. 2180f1702c5SYu Xiangning * If there is an existing entry, then increase the reference count. 2190f1702c5SYu Xiangning * Otherwise we load the module and in the module register function create 2200f1702c5SYu Xiangning * a new entry and add it to the end of the socket module table. 2210f1702c5SYu Xiangning */ 2220f1702c5SYu Xiangning smod_info_t * 2230f1702c5SYu Xiangning smod_lookup_byname(const char *modname) 2240f1702c5SYu Xiangning { 2250f1702c5SYu Xiangning smod_info_t *smodp; 2260f1702c5SYu Xiangning int error; 2270f1702c5SYu Xiangning 2280f1702c5SYu Xiangning again: 2290f1702c5SYu Xiangning /* 2300f1702c5SYu Xiangning * If find an entry, increase the reference count and 2310f1702c5SYu Xiangning * return the entry pointer. 2320f1702c5SYu Xiangning */ 2330f1702c5SYu Xiangning mutex_enter(&smod_list_lock); 2340f1702c5SYu Xiangning if ((smodp = smod_find(modname)) != NULL) { 2350f1702c5SYu Xiangning SMOD_INC_REF(smodp); 2360f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 2370f1702c5SYu Xiangning return (smodp); 2380f1702c5SYu Xiangning } 2390f1702c5SYu Xiangning mutex_exit(&smod_list_lock); 2400f1702c5SYu Xiangning 2410f1702c5SYu Xiangning /* 2420f1702c5SYu Xiangning * We have a sockmod, and it is not loaded. 2430f1702c5SYu Xiangning * Load the module into the kernel, modload() will 2440f1702c5SYu Xiangning * take care of the multiple threads. 2450f1702c5SYu Xiangning */ 2460f1702c5SYu Xiangning DTRACE_PROBE1(load__socket__module, char *, modname); 2470f1702c5SYu Xiangning error = modload(SOCKMOD_PATH, modname); 2480f1702c5SYu Xiangning if (error == -1) { 2490f1702c5SYu Xiangning cmn_err(CE_CONT, "modload of %s/%s failed", 2500f1702c5SYu Xiangning SOCKMOD_PATH, modname); 2510f1702c5SYu Xiangning return (NULL); 2520f1702c5SYu Xiangning } 2530f1702c5SYu Xiangning goto again; 2540f1702c5SYu Xiangning } 255