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
i_fmodsw_dprintf(uint_t flag,const char * fmt,...)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
i_fmodsw_hash_insert(fmodsw_impl_t * fp)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
i_fmodsw_hash_remove(const char * name,fmodsw_impl_t ** fpp)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
i_fmodsw_hash_find(const char * name,fmodsw_impl_t ** fpp)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
fmodsw_register(const char * name,struct streamtab * str,int flag)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
fmodsw_unregister(const char * name)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 *
fmodsw_find(const char * name,fmodsw_flags_t flags)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
fmodsw_rele(fmodsw_impl_t * fp)308 fmodsw_rele(fmodsw_impl_t *fp)
309 {
310 ASSERT(fp->f_ref > 0);
311 atomic_add_32(&(fp->f_ref), -1);
312 }
313