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