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 }