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