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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/sysmacros.h> 27 #include <sys/atomic.h> 28 #include <sys/strsubr.h> 29 #include <sys/socket.h> 30 #include <sys/socketvar.h> 31 #include <sys/cmn_err.h> 32 #include <sys/modctl.h> 33 #include <sys/sdt.h> 34 35 list_t smod_list; 36 kmutex_t smod_list_lock; 37 38 so_create_func_t sock_comm_create_function; 39 so_destroy_func_t sock_comm_destroy_function; 40 41 static smod_info_t *smod_create(const char *); 42 static void smod_destroy(smod_info_t *); 43 44 extern void smod_add(smod_info_t *); 45 46 void 47 smod_init(void) 48 { 49 list_create(&smod_list, sizeof (smod_info_t), 50 offsetof(smod_info_t, smod_node)); 51 mutex_init(&smod_list_lock, NULL, MUTEX_DEFAULT, NULL); 52 } 53 54 static smod_info_t * 55 smod_find(const char *modname) 56 { 57 smod_info_t *smodp; 58 59 ASSERT(MUTEX_HELD(&smod_list_lock)); 60 61 for (smodp = list_head(&smod_list); smodp != NULL; 62 smodp = list_next(&smod_list, smodp)) 63 if (strcmp(smodp->smod_name, modname) == 0) 64 return (smodp); 65 return (NULL); 66 } 67 68 /* 69 * Register the socket module. 70 */ 71 int 72 smod_register(const smod_reg_t *reg) 73 { 74 smod_info_t *smodp; 75 76 /* 77 * Make sure the socket module does not depend on capabilities 78 * not available on the system. 79 */ 80 if (reg->smod_version != SOCKMOD_VERSION || 81 reg->smod_dc_version != SOCK_DC_VERSION || 82 reg->smod_uc_version != SOCK_UC_VERSION) { 83 cmn_err(CE_WARN, 84 "Failed to register socket module %s: version mismatch", 85 reg->smod_name); 86 return (EINVAL); 87 } 88 89 #ifdef DEBUG 90 mutex_enter(&smod_list_lock); 91 if ((smodp = smod_find(reg->smod_name)) != NULL) { 92 mutex_exit(&smod_list_lock); 93 return (EEXIST); 94 } 95 mutex_exit(&smod_list_lock); 96 #endif 97 98 smodp = smod_create(reg->smod_name); 99 smodp->smod_version = reg->smod_version; 100 if (strcmp(smodp->smod_name, SOTPI_SMOD_NAME) == 0 || 101 strcmp(smodp->smod_name, "socksctp") == 0 || 102 strcmp(smodp->smod_name, "socksdp") == 0) { 103 ASSERT(smodp->smod_proto_create_func == NULL); 104 ASSERT(reg->__smod_priv != NULL); 105 smodp->smod_sock_create_func = 106 reg->__smod_priv->smodp_sock_create_func; 107 smodp->smod_sock_destroy_func = 108 reg->__smod_priv->smodp_sock_destroy_func; 109 smodp->smod_proto_create_func = NULL; 110 } else { 111 if (reg->smod_proto_create_func == NULL || 112 (reg->__smod_priv != NULL && 113 (reg->__smod_priv->smodp_sock_create_func != NULL || 114 reg->__smod_priv->smodp_sock_destroy_func != NULL))) { 115 #ifdef DEBUG 116 cmn_err(CE_CONT, "smod_register of %s failed", 117 smodp->smod_name); 118 #endif 119 smod_destroy(smodp); 120 return (EINVAL); 121 } 122 smodp->smod_proto_create_func = reg->smod_proto_create_func; 123 smodp->smod_sock_create_func = sock_comm_create_function; 124 smodp->smod_sock_destroy_func = sock_comm_destroy_function; 125 smodp->smod_uc_version = reg->smod_uc_version; 126 smodp->smod_dc_version = reg->smod_dc_version; 127 if (reg->__smod_priv != NULL) { 128 smodp->smod_proto_fallback_func = 129 reg->__smod_priv->smodp_proto_fallback_func; 130 } 131 } 132 smod_add(smodp); 133 return (0); 134 } 135 136 /* 137 * Unregister the socket module 138 */ 139 int 140 smod_unregister(const char *mod_name) 141 { 142 smod_info_t *smodp; 143 144 mutex_enter(&smod_list_lock); 145 if ((smodp = smod_find(mod_name)) != NULL) { 146 if (smodp->smod_refcnt != 0) { 147 mutex_exit(&smod_list_lock); 148 return (EBUSY); 149 } else { 150 /* 151 * Delete the entry from the socket module list. 152 */ 153 list_remove(&smod_list, smodp); 154 mutex_exit(&smod_list_lock); 155 156 smod_destroy(smodp); 157 return (0); 158 } 159 } 160 mutex_exit(&smod_list_lock); 161 162 return (ENXIO); 163 } 164 165 /* 166 * Initialize the socket module entry. 167 */ 168 static smod_info_t * 169 smod_create(const char *modname) 170 { 171 smod_info_t *smodp; 172 int len; 173 174 smodp = kmem_zalloc(sizeof (*smodp), KM_SLEEP); 175 len = strlen(modname) + 1; 176 smodp->smod_name = kmem_alloc(len, KM_SLEEP); 177 bcopy(modname, smodp->smod_name, len); 178 smodp->smod_name[len - 1] = '\0'; 179 return (smodp); 180 } 181 182 /* 183 * Clean up the socket module part of the sockparams entry. 184 */ 185 static void 186 smod_destroy(smod_info_t *smodp) 187 { 188 ASSERT(smodp->smod_name != NULL); 189 ASSERT(smodp->smod_refcnt == 0); 190 ASSERT(!list_link_active(&smodp->smod_node)); 191 ASSERT(strcmp(smodp->smod_name, "socktpi") != 0); 192 193 kmem_free(smodp->smod_name, strlen(smodp->smod_name) + 1); 194 smodp->smod_name = NULL; 195 smodp->smod_proto_create_func = NULL; 196 smodp->smod_sock_create_func = NULL; 197 smodp->smod_sock_destroy_func = NULL; 198 kmem_free(smodp, sizeof (*smodp)); 199 } 200 201 /* 202 * Add an entry at the front of the socket module list. 203 */ 204 void 205 smod_add(smod_info_t *smodp) 206 { 207 ASSERT(smodp != NULL); 208 mutex_enter(&smod_list_lock); 209 list_insert_head(&smod_list, smodp); 210 mutex_exit(&smod_list_lock); 211 } 212 213 /* 214 * Lookup the socket module table by the socket module name. 215 * If there is an existing entry, then increase the reference count. 216 * Otherwise we load the module and in the module register function create 217 * a new entry and add it to the end of the socket module table. 218 */ 219 smod_info_t * 220 smod_lookup_byname(const char *modname) 221 { 222 smod_info_t *smodp; 223 int error; 224 225 again: 226 /* 227 * If find an entry, increase the reference count and 228 * return the entry pointer. 229 */ 230 mutex_enter(&smod_list_lock); 231 if ((smodp = smod_find(modname)) != NULL) { 232 SMOD_INC_REF(smodp); 233 mutex_exit(&smod_list_lock); 234 return (smodp); 235 } 236 mutex_exit(&smod_list_lock); 237 238 /* 239 * We have a sockmod, and it is not loaded. 240 * Load the module into the kernel, modload() will 241 * take care of the multiple threads. 242 */ 243 DTRACE_PROBE1(load__socket__module, char *, modname); 244 error = modload(SOCKMOD_PATH, modname); 245 if (error == -1) { 246 cmn_err(CE_CONT, "modload of %s/%s failed", 247 SOCKMOD_PATH, modname); 248 return (NULL); 249 } 250 goto again; 251 } 252