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