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