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