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