xref: /illumos-gate/usr/src/uts/common/io/str_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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/conf.h>
33 #include <sys/stream.h>
34 #include <sys/strsubr.h>
35 #include <sys/modctl.h>
36 #include <sys/modhash.h>
37 #include <sys/atomic.h>
38 
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/t_lock.h>
42 
43 /*
44  * This module provides the framework that manage STREAMS modules.
45  * fmodsw_alloc() is called from modconf.c as a result of a module calling
46  * mod_install() and fmodsw_free() is called as the result of the module
47  * calling mod_remove().
48  * fmodsw_find() will find the fmodsw_impl_t structure relating to a named
49  * module. There is no equivalent of driver major numbers for modules; the
50  * the database of fmodsw_impl_t structures is purely keyed by name and
51  * is hence a hash table to keep lookup cost to a minimum.
52  */
53 
54 /*
55  * fmodsw_hash is the hash table that will be used to map module names to
56  * their fmodsw_impl_t structures. The hash function requires that the value is
57  * a power of 2 so this definition specifies the log of the hash table size.
58  */
59 #define	FMODSW_LOG_HASHSZ	8
60 
61 /*
62  * Hash table and associated reader-writer lock
63  *
64  * NOTE: Because the lock is global data, it is initialized to zero and hence
65  *       a call to rw_init() is not required. Similarly all the pointers in
66  *       the hash table will be implicitly initialized to NULL.
67  */
68 #define	FMODSW_HASHSZ		(1 << FMODSW_LOG_HASHSZ)
69 
70 static fmodsw_impl_t	*fmodsw_hash[FMODSW_HASHSZ];
71 static krwlock_t	fmodsw_lock;
72 
73 /*
74  * Debug code:
75  *
76  * This is not conditionally compiled since it may be useful to third
77  * parties when developing new modules.
78  */
79 
80 #define	BUFSZ	512
81 
82 #define	FMODSW_INIT		0x00000001
83 #define	FMODSW_REGISTER		0x00000002
84 #define	FMODSW_UNREGISTER	0x00000004
85 #define	FMODSW_FIND		0x00000008
86 
87 uint32_t	fmodsw_debug_flags = 0x00000000;
88 
89 static void fmodsw_dprintf(uint_t flag, const char *fmt, ...) __KPRINTFLIKE(2);
90 
91 /* PRINTFLIKE2 */
92 static void
93 i_fmodsw_dprintf(uint_t flag, const char *fmt, ...)
94 {
95 	va_list	alist;
96 	char	buf[BUFSZ + 1];
97 	char	*ptr;
98 
99 	if (fmodsw_debug_flags & flag) {
100 		va_start(alist, fmt);
101 		ptr = buf;
102 		(void) sprintf(ptr, "strmod debug: ");
103 		ptr += strlen(buf);
104 		(void) vsnprintf(ptr, buf + BUFSZ - ptr, fmt, alist);
105 		printf(buf);
106 		va_end(alist);
107 	}
108 }
109 
110 
111 /*
112  * Local functions:
113  */
114 
115 #define	FMODSW_HASH(_key) \
116 	(uint_t)(((_key[0] << 4) | (_key[1] & 0x0f)) & (FMODSW_HASHSZ - 1))
117 
118 #define	FMODSW_KEYCMP(_k1, _k2, _match)					\
119 	{								\
120 		char	*p1 = (char *)(_k1);				\
121 		char	*p2 = (char *)(_k2);				\
122 									\
123 		while (*p1 == *p2++) {					\
124 			if (*p1++ == '\0') {				\
125 				goto _match;				\
126 			}						\
127 		}							\
128 	}
129 
130 static int
131 i_fmodsw_hash_insert(fmodsw_impl_t *fp)
132 {
133 	uint_t		bucket;
134 	fmodsw_impl_t	**pp;
135 	fmodsw_impl_t	*p;
136 
137 	ASSERT(rw_write_held(&fmodsw_lock));
138 
139 	bucket = FMODSW_HASH(fp->f_name);
140 	for (pp = &(fmodsw_hash[bucket]); (p = *pp) != NULL;
141 	    pp = &(p->f_next))
142 		FMODSW_KEYCMP(p->f_name, fp->f_name, found);
143 
144 	fp->f_next = p;
145 	*pp = fp;
146 	return (0);
147 
148 found:
149 	return (EEXIST);
150 }
151 
152 static int
153 i_fmodsw_hash_remove(const char *name, fmodsw_impl_t **fpp)
154 {
155 	uint_t		bucket;
156 	fmodsw_impl_t	**pp;
157 	fmodsw_impl_t	*p;
158 
159 	ASSERT(rw_write_held(&fmodsw_lock));
160 
161 	bucket = FMODSW_HASH(name);
162 	for (pp = &(fmodsw_hash[bucket]); (p = *pp) != NULL;
163 	    pp = &(p->f_next))
164 		FMODSW_KEYCMP(p->f_name, name, found);
165 
166 	return (ENOENT);
167 
168 found:
169 	if (p->f_ref > 0)
170 		return (EBUSY);
171 
172 	*pp = p->f_next;
173 	*fpp = p;
174 	return (0);
175 }
176 
177 static int
178 i_fmodsw_hash_find(const char *name, fmodsw_impl_t **fpp)
179 {
180 	uint_t		bucket;
181 	fmodsw_impl_t	*p;
182 	int		rc = 0;
183 
184 	ASSERT(rw_read_held(&fmodsw_lock));
185 
186 	bucket = FMODSW_HASH(name);
187 	for (p = fmodsw_hash[bucket]; p != NULL; p = p->f_next)
188 		FMODSW_KEYCMP(p->f_name, name, found);
189 
190 	rc = ENOENT;
191 found:
192 	*fpp = p;
193 #ifdef	DEBUG
194 	if (p != NULL)
195 		p->f_hits++;
196 #endif	/* DEBUG */
197 
198 	return (rc);
199 }
200 
201 
202 /*
203  * Exported functions:
204  */
205 
206 int
207 fmodsw_register(const char *name, struct streamtab *str, int flag)
208 {
209 	fmodsw_impl_t	*fp;
210 	int		len;
211 	int		err;
212 	uint_t	qflag;
213 	uint_t	sqtype;
214 
215 	if ((len = strlen(name)) > FMNAMESZ)
216 		return (EINVAL);
217 
218 	if ((fp = kmem_zalloc(sizeof (fmodsw_impl_t), KM_NOSLEEP)) == NULL)
219 		return (ENOMEM);
220 
221 	(void) strncpy(fp->f_name, name, len);
222 	fp->f_name[len] = '\0';
223 
224 	if ((err = devflg_to_qflag(str, flag, &qflag, &sqtype)) != 0)
225 		goto failed;
226 
227 	fp->f_str = str;
228 	fp->f_qflag = qflag;
229 	fp->f_sqtype = sqtype;
230 	if (qflag & (QPERMOD | QMTOUTPERIM))
231 		fp->f_dmp = hold_dm(str, qflag, sqtype);
232 
233 	rw_enter(&fmodsw_lock, RW_WRITER);
234 	if ((err = i_fmodsw_hash_insert(fp)) != 0) {
235 		rw_exit(&fmodsw_lock);
236 		goto failed;
237 	}
238 	rw_exit(&fmodsw_lock);
239 
240 	i_fmodsw_dprintf(FMODSW_REGISTER, "registered module '%s'\n", name);
241 	return (0);
242 failed:
243 	i_fmodsw_dprintf(FMODSW_REGISTER, "failed to register module '%s'\n",
244 	    name);
245 	if (fp->f_dmp != NULL)
246 		rele_dm(fp->f_dmp);
247 	kmem_free(fp, sizeof (fmodsw_impl_t));
248 	return (err);
249 }
250 
251 int
252 fmodsw_unregister(const char *name)
253 {
254 	fmodsw_impl_t	*fp;
255 	int		err;
256 
257 	rw_enter(&fmodsw_lock, RW_WRITER);
258 	if ((err = i_fmodsw_hash_remove(name, &fp)) != 0) {
259 		rw_exit(&fmodsw_lock);
260 		goto failed;
261 	}
262 	rw_exit(&fmodsw_lock);
263 
264 	if (fp->f_dmp != NULL)
265 		rele_dm(fp->f_dmp);
266 	kmem_free(fp, sizeof (fmodsw_impl_t));
267 
268 	i_fmodsw_dprintf(FMODSW_UNREGISTER, "unregistered module '%s'\n",
269 	    name);
270 	return (0);
271 failed:
272 	i_fmodsw_dprintf(FMODSW_UNREGISTER, "failed to unregister module "
273 	    "'%s'\n", name);
274 	return (err);
275 }
276 
277 fmodsw_impl_t *
278 fmodsw_find(const char *name, fmodsw_flags_t flags)
279 {
280 	fmodsw_impl_t	*fp;
281 	int		id;
282 
283 try_again:
284 	rw_enter(&fmodsw_lock, RW_READER);
285 	if (i_fmodsw_hash_find(name, &fp) == 0) {
286 		if (flags & FMODSW_HOLD) {
287 			atomic_add_32(&(fp->f_ref), 1);	/* lock must be held */
288 			ASSERT(fp->f_ref > 0);
289 		}
290 
291 		rw_exit(&fmodsw_lock);
292 		return (fp);
293 	}
294 	rw_exit(&fmodsw_lock);
295 
296 	if (flags & FMODSW_LOAD) {
297 		if ((id = modload("strmod", (char *)name)) != -1) {
298 			i_fmodsw_dprintf(FMODSW_FIND,
299 			    "module '%s' loaded: id = %d\n", name, id);
300 			goto try_again;
301 		}
302 	}
303 
304 	return (NULL);
305 }
306 
307 void
308 fmodsw_rele(fmodsw_impl_t *fp)
309 {
310 	ASSERT(fp->f_ref > 0);
311 	atomic_add_32(&(fp->f_ref), -1);
312 }
313