str_conf.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/modctl.h>
#include <sys/modhash.h>
#include <sys/atomic.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/t_lock.h>
/*
* This module provides the framework that manage STREAMS modules.
* fmodsw_alloc() is called from modconf.c as a result of a module calling
* mod_install() and fmodsw_free() is called as the result of the module
* calling mod_remove().
* fmodsw_find() will find the fmodsw_impl_t structure relating to a named
* module. There is no equivalent of driver major numbers for modules; the
* the database of fmodsw_impl_t structures is purely keyed by name and
* is hence a hash table to keep lookup cost to a minimum.
*/
/*
* fmodsw_hash is the hash table that will be used to map module names to
* their fmodsw_impl_t structures. The hash function requires that the value is
* a power of 2 so this definition specifies the log of the hash table size.
*/
#define FMODSW_LOG_HASHSZ 8
/*
* Hash table and associated reader-writer lock
*
* NOTE: Because the lock is global data, it is initialized to zero and hence
* a call to rw_init() is not required. Similarly all the pointers in
* the hash table will be implicitly initialized to NULL.
*/
#define FMODSW_HASHSZ (1 << FMODSW_LOG_HASHSZ)
static fmodsw_impl_t *fmodsw_hash[FMODSW_HASHSZ];
static krwlock_t fmodsw_lock;
/*
* Debug code:
*
* This is not conditionally compiled since it may be useful to third
* parties when developing new modules.
*/
#define BUFSZ 512
#define FMODSW_INIT 0x00000001
#define FMODSW_REGISTER 0x00000002
#define FMODSW_UNREGISTER 0x00000004
#define FMODSW_FIND 0x00000008
uint32_t fmodsw_debug_flags = 0x00000000;
static void fmodsw_dprintf(uint_t flag, const char *fmt, ...) __KPRINTFLIKE(2);
/* PRINTFLIKE2 */
static void
i_fmodsw_dprintf(uint_t flag, const char *fmt, ...)
{
va_list alist;
char buf[BUFSZ + 1];
char *ptr;
if (fmodsw_debug_flags & flag) {
va_start(alist, fmt);
ptr = buf;
(void) sprintf(ptr, "strmod debug: ");
ptr += strlen(buf);
(void) vsnprintf(ptr, buf + BUFSZ - ptr, fmt, alist);
printf(buf);
va_end(alist);
}
}
/*
* Local functions:
*/
#define FMODSW_HASH(_key) \
(uint_t)(((_key[0] << 4) | (_key[1] & 0x0f)) & (FMODSW_HASHSZ - 1))
#define FMODSW_KEYCMP(_k1, _k2, _match) \
{ \
char *p1 = (char *)(_k1); \
char *p2 = (char *)(_k2); \
\
while (*p1 == *p2++) { \
if (*p1++ == '\0') { \
goto _match; \
} \
} \
}
static int
i_fmodsw_hash_insert(fmodsw_impl_t *fp)
{
uint_t bucket;
fmodsw_impl_t **pp;
fmodsw_impl_t *p;
ASSERT(rw_write_held(&fmodsw_lock));
bucket = FMODSW_HASH(fp->f_name);
for (pp = &(fmodsw_hash[bucket]); (p = *pp) != NULL;
pp = &(p->f_next))
FMODSW_KEYCMP(p->f_name, fp->f_name, found);
fp->f_next = p;
*pp = fp;
return (0);
found:
return (EEXIST);
}
static int
i_fmodsw_hash_remove(const char *name, fmodsw_impl_t **fpp)
{
uint_t bucket;
fmodsw_impl_t **pp;
fmodsw_impl_t *p;
ASSERT(rw_write_held(&fmodsw_lock));
bucket = FMODSW_HASH(name);
for (pp = &(fmodsw_hash[bucket]); (p = *pp) != NULL;
pp = &(p->f_next))
FMODSW_KEYCMP(p->f_name, name, found);
return (ENOENT);
found:
if (p->f_ref > 0)
return (EBUSY);
*pp = p->f_next;
*fpp = p;
return (0);
}
static int
i_fmodsw_hash_find(const char *name, fmodsw_impl_t **fpp)
{
uint_t bucket;
fmodsw_impl_t *p;
int rc = 0;
ASSERT(rw_read_held(&fmodsw_lock));
bucket = FMODSW_HASH(name);
for (p = fmodsw_hash[bucket]; p != NULL; p = p->f_next)
FMODSW_KEYCMP(p->f_name, name, found);
rc = ENOENT;
found:
*fpp = p;
#ifdef DEBUG
if (p != NULL)
p->f_hits++;
#endif /* DEBUG */
return (rc);
}
/*
* Exported functions:
*/
int
fmodsw_register(const char *name, struct streamtab *str, int flag)
{
fmodsw_impl_t *fp;
int len;
int err;
uint_t qflag;
uint_t sqtype;
if ((len = strlen(name)) > FMNAMESZ)
return (EINVAL);
if ((fp = kmem_zalloc(sizeof (fmodsw_impl_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
(void) strncpy(fp->f_name, name, len);
fp->f_name[len] = '\0';
if ((err = devflg_to_qflag(str, flag, &qflag, &sqtype)) != 0)
goto failed;
fp->f_str = str;
fp->f_qflag = qflag;
fp->f_sqtype = sqtype;
if (qflag & (QPERMOD | QMTOUTPERIM))
fp->f_dmp = hold_dm(str, qflag, sqtype);
rw_enter(&fmodsw_lock, RW_WRITER);
if ((err = i_fmodsw_hash_insert(fp)) != 0) {
rw_exit(&fmodsw_lock);
goto failed;
}
rw_exit(&fmodsw_lock);
i_fmodsw_dprintf(FMODSW_REGISTER, "registered module '%s'\n", name);
return (0);
failed:
i_fmodsw_dprintf(FMODSW_REGISTER, "failed to register module '%s'\n",
name);
if (fp->f_dmp != NULL)
rele_dm(fp->f_dmp);
kmem_free(fp, sizeof (fmodsw_impl_t));
return (err);
}
int
fmodsw_unregister(const char *name)
{
fmodsw_impl_t *fp;
int err;
rw_enter(&fmodsw_lock, RW_WRITER);
if ((err = i_fmodsw_hash_remove(name, &fp)) != 0) {
rw_exit(&fmodsw_lock);
goto failed;
}
rw_exit(&fmodsw_lock);
if (fp->f_dmp != NULL)
rele_dm(fp->f_dmp);
kmem_free(fp, sizeof (fmodsw_impl_t));
i_fmodsw_dprintf(FMODSW_UNREGISTER, "unregistered module '%s'\n",
name);
return (0);
failed:
i_fmodsw_dprintf(FMODSW_UNREGISTER, "failed to unregister module "
"'%s'\n", name);
return (err);
}
fmodsw_impl_t *
fmodsw_find(const char *name, fmodsw_flags_t flags)
{
fmodsw_impl_t *fp;
int id;
try_again:
rw_enter(&fmodsw_lock, RW_READER);
if (i_fmodsw_hash_find(name, &fp) == 0) {
if (flags & FMODSW_HOLD) {
atomic_add_32(&(fp->f_ref), 1); /* lock must be held */
ASSERT(fp->f_ref > 0);
}
rw_exit(&fmodsw_lock);
return (fp);
}
rw_exit(&fmodsw_lock);
if (flags & FMODSW_LOAD) {
if ((id = modload("strmod", (char *)name)) != -1) {
i_fmodsw_dprintf(FMODSW_FIND,
"module '%s' loaded: id = %d\n", name, id);
goto try_again;
}
}
return (NULL);
}
void
fmodsw_rele(fmodsw_impl_t *fp)
{
ASSERT(fp->f_ref > 0);
atomic_add_32(&(fp->f_ref), -1);
}