xref: /illumos-gate/usr/src/uts/common/io/sock_conf.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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