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 }