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 70static fmodsw_impl_t *fmodsw_hash[FMODSW_HASHSZ]; 71static 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 87uint32_t fmodsw_debug_flags = 0x00000000; 88 89static void fmodsw_dprintf(uint_t flag, const char *fmt, ...) __KPRINTFLIKE(2); 90 91/* PRINTFLIKE2 */ 92static void 93i_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 130static int 131i_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 148found: 149 return (EEXIST); 150} 151 152static int 153i_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 168found: 169 if (p->f_ref > 0) 170 return (EBUSY); 171 172 *pp = p->f_next; 173 *fpp = p; 174 return (0); 175} 176 177static int 178i_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; 191found: 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 206int 207fmodsw_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); 242failed: 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 251int 252fmodsw_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); 271failed: 272 i_fmodsw_dprintf(FMODSW_UNREGISTER, "failed to unregister module " 273 "'%s'\n", name); 274 return (err); 275} 276 277fmodsw_impl_t * 278fmodsw_find(const char *name, fmodsw_flags_t flags) 279{ 280 fmodsw_impl_t *fp; 281 int id; 282 283try_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 307void 308fmodsw_rele(fmodsw_impl_t *fp) 309{ 310 ASSERT(fp->f_ref > 0); 311 atomic_add_32(&(fp->f_ref), -1); 312} 313