#include <sys/types.h>
#include <sys/param.h>
#include <sys/modctl.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/spl.h>
#include <sys/time.h>
#include <sys/varargs.h>
#include <ipp/ipp.h>
#include <ipp/ipp_impl.h>
#include <ipp/ipgpc/ipgpc.h>
#if defined(DEBUG)
#define IPP_DBG
#endif
uint32_t ipp_action_count = 0;
static kmem_cache_t *ipp_mod_cache = NULL;
static uint32_t ipp_mod_count = 0;
static uint32_t ipp_max_mod = IPP_NMOD;
static ipp_mod_t **ipp_mod_byid;
static krwlock_t ipp_mod_byid_lock[1];
static ipp_mod_id_t ipp_next_mid = IPP_MOD_RESERVED + 1;
static ipp_mod_id_t ipp_mid_limit;
static ipp_ref_t *ipp_mod_byname[IPP_NBUCKET];
static krwlock_t ipp_mod_byname_lock[1];
static kmem_cache_t *ipp_action_cache = NULL;
static uint32_t ipp_max_action = IPP_NACTION;
static ipp_action_t **ipp_action_byid;
static krwlock_t ipp_action_byid_lock[1];
static ipp_action_id_t ipp_next_aid = IPP_ACTION_RESERVED + 1;
static ipp_action_id_t ipp_aid_limit;
static ipp_ref_t *ipp_action_byname[IPP_NBUCKET];
static krwlock_t ipp_action_byname_lock[1];
static ipp_ref_t *ipp_action_noname;
static kmem_cache_t *ipp_packet_cache = NULL;
static uint_t ipp_packet_classes = IPP_NCLASS;
static uint_t ipp_packet_logging = 0;
static uint_t ipp_packet_log_entries = IPP_NLOG;
void ipp_init(void);
int ipp_list_mods(ipp_mod_id_t **, int *);
ipp_mod_id_t ipp_mod_lookup(const char *);
int ipp_mod_name(ipp_mod_id_t, char **);
int ipp_mod_register(const char *, ipp_ops_t *);
int ipp_mod_unregister(ipp_mod_id_t);
int ipp_mod_list_actions(ipp_mod_id_t, ipp_action_id_t **,
int *);
ipp_action_id_t ipp_action_lookup(const char *);
int ipp_action_name(ipp_action_id_t, char **);
int ipp_action_mod(ipp_action_id_t, ipp_mod_id_t *);
int ipp_action_create(ipp_mod_id_t, const char *,
nvlist_t **, ipp_flags_t, ipp_action_id_t *);
int ipp_action_modify(ipp_action_id_t, nvlist_t **,
ipp_flags_t);
int ipp_action_destroy(ipp_action_id_t, ipp_flags_t);
int ipp_action_info(ipp_action_id_t, int (*)(nvlist_t *,
void *), void *, ipp_flags_t);
void ipp_action_set_ptr(ipp_action_id_t, void *);
void *ipp_action_get_ptr(ipp_action_id_t);
int ipp_action_ref(ipp_action_id_t, ipp_action_id_t,
ipp_flags_t);
int ipp_action_unref(ipp_action_id_t, ipp_action_id_t,
ipp_flags_t);
int ipp_packet_alloc(ipp_packet_t **, const char *,
ipp_action_id_t);
void ipp_packet_free(ipp_packet_t *);
int ipp_packet_add_class(ipp_packet_t *, const char *,
ipp_action_id_t);
int ipp_packet_process(ipp_packet_t **);
int ipp_packet_next(ipp_packet_t *, ipp_action_id_t);
void ipp_packet_set_data(ipp_packet_t *, mblk_t *);
mblk_t *ipp_packet_get_data(ipp_packet_t *);
void ipp_packet_set_private(ipp_packet_t *, void *,
void (*)(void *));
void *ipp_packet_get_private(ipp_packet_t *);
int ipp_stat_create(ipp_action_id_t, const char *, int,
int (*)(ipp_stat_t *, void *, int), void *, ipp_stat_t **);
void ipp_stat_install(ipp_stat_t *);
void ipp_stat_destroy(ipp_stat_t *);
int ipp_stat_named_init(ipp_stat_t *, const char *, uchar_t,
ipp_named_t *);
int ipp_stat_named_op(ipp_named_t *, void *, int);
static int ref_mod(ipp_action_t *, ipp_mod_t *);
static void unref_mod(ipp_action_t *, ipp_mod_t *);
static int is_mod_busy(ipp_mod_t *);
static int get_mod_ref(ipp_mod_t *, ipp_action_id_t **, int *);
static int get_mods(ipp_mod_id_t **bufp, int *);
static ipp_mod_id_t find_mod(const char *);
static int alloc_mod(const char *, ipp_mod_id_t *);
static void free_mod(ipp_mod_t *);
static ipp_mod_t *hold_mod(ipp_mod_id_t);
static void rele_mod(ipp_mod_t *);
static ipp_mod_id_t get_mid(void);
static int condemn_action(ipp_ref_t **, ipp_action_t *);
static int destroy_action(ipp_action_t *, ipp_flags_t);
static int ref_action(ipp_action_t *, ipp_action_t *);
static int unref_action(ipp_action_t *, ipp_action_t *);
static int is_action_refd(ipp_action_t *);
static ipp_action_id_t find_action(const char *);
static int alloc_action(const char *, ipp_action_id_t *);
static void free_action(ipp_action_t *);
static ipp_action_t *hold_action(ipp_action_id_t);
static void rele_action(ipp_action_t *);
static ipp_action_id_t get_aid(void);
static int alloc_packet(const char *, ipp_action_id_t,
ipp_packet_t **);
static int realloc_packet(ipp_packet_t *);
static void free_packet(ipp_packet_t *);
static int hash(const char *);
static int update_stats(kstat_t *, int);
static void init_mods(void);
static void init_actions(void);
static void init_packets(void);
static int mod_constructor(void *, void *, int);
static void mod_destructor(void *, void *);
static int action_constructor(void *, void *, int);
static void action_destructor(void *, void *);
static int packet_constructor(void *, void *, int);
static void packet_destructor(void *, void *);
#ifdef IPP_DBG
#define DBG_MOD 0x00000001ull
#define DBG_ACTION 0x00000002ull
#define DBG_PACKET 0x00000004ull
#define DBG_STATS 0x00000008ull
#define DBG_LIST 0x00000010ull
static uint64_t ipp_debug_flags =
0;
static kmutex_t debug_mutex[1];
static void ipp_debug(uint64_t, const char *, char *, ...)
__KPRINTFLIKE(3);
#define DBG0(_type, _fmt) \
ipp_debug((_type), __FN__, (_fmt));
#define DBG1(_type, _fmt, _a1) \
ipp_debug((_type), __FN__, (_fmt), (_a1));
#define DBG2(_type, _fmt, _a1, _a2) \
ipp_debug((_type), __FN__, (_fmt), (_a1), (_a2));
#define DBG3(_type, _fmt, _a1, _a2, _a3) \
ipp_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3));
#define DBG4(_type, _fmt, _a1, _a2, _a3, _a4) \
ipp_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3), (_a4));
#define DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5) \
ipp_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3), (_a4), (_a5));
#else
#define DBG0(_type, _fmt)
#define DBG1(_type, _fmt, _a1)
#define DBG2(_type, _fmt, _a1, _a2)
#define DBG3(_type, _fmt, _a1, _a2, _a3)
#define DBG4(_type, _fmt, _a1, _a2, _a3, _a4)
#define DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5)
#endif
#define LOCK_MOD(_imp, _rw) \
rw_enter((_imp)->ippm_lock, (_rw))
#define UNLOCK_MOD(_imp) \
rw_exit((_imp)->ippm_lock)
#define LOCK_ACTION(_ap, _rw) \
rw_enter((_ap)->ippa_lock, (_rw))
#define UNLOCK_ACTION(_imp) \
rw_exit((_imp)->ippa_lock)
#define CONFIG_WRITE_START(_ap) \
CONFIG_LOCK_ENTER((_ap)->ippa_config_lock, CL_WRITE)
#define CONFIG_WRITE_END(_ap) \
CONFIG_LOCK_EXIT((_ap)->ippa_config_lock)
#define CONFIG_READ_START(_ap) \
CONFIG_LOCK_ENTER((_ap)->ippa_config_lock, CL_READ)
#define CONFIG_READ_END(_ap) \
CONFIG_LOCK_EXIT((_ap)->ippa_config_lock)
#define __FN__ "ipp_init"
void
ipp_init(
void)
{
#ifdef IPP_DBG
mutex_init(debug_mutex, NULL, MUTEX_ADAPTIVE,
(void *)ipltospl(LOCK_LEVEL));
#endif
init_mods();
init_actions();
init_packets();
}
#undef __FN__
#define __FN__ "ipp_list_mods"
int
ipp_list_mods(
ipp_mod_id_t **bufp,
int *neltp)
{
ASSERT(bufp != NULL);
ASSERT(neltp != NULL);
return (get_mods(bufp, neltp));
}
#undef __FN__
#define __FN__ "ipp_mod_lookup"
ipp_mod_id_t
ipp_mod_lookup(
const char *modname)
{
ipp_mod_id_t mid;
#define FIRST_TIME 0
int try = FIRST_TIME;
if (modname == NULL || strlen(modname) > MAXNAMELEN - 1)
return (IPP_MOD_INVAL);
try_again:
if ((mid = find_mod(modname)) == IPP_MOD_INVAL) {
if (try++ == FIRST_TIME) {
DBG1(DBG_MOD, "loading module '%s'\n", modname);
(void) modload("ipp", (char *)modname);
goto try_again;
}
}
return (mid);
#undef FIRST_TIME
}
#undef __FN__
#define __FN__ "ipp_mod_name"
int
ipp_mod_name(
ipp_mod_id_t mid,
char **modnamep)
{
ipp_mod_t *imp;
char *modname;
char *buf;
ASSERT(modnamep != NULL);
if ((imp = hold_mod(mid)) == NULL)
return (ENOENT);
LOCK_MOD(imp, RW_READER);
modname = imp->ippm_name;
if ((buf = kmem_zalloc(strlen(modname) + 1, KM_NOSLEEP)) == NULL) {
UNLOCK_MOD(imp);
rele_mod(imp);
return (ENOMEM);
}
(void) strcpy(buf, modname);
UNLOCK_MOD(imp);
*modnamep = buf;
rele_mod(imp);
return (0);
}
#undef __FN__
#define __FN__ "ipp_mod_register"
int
ipp_mod_register(
const char *modname,
ipp_ops_t *ipp_ops)
{
ipp_mod_id_t mid;
ipp_mod_t *imp;
int rc;
ASSERT(ipp_ops != NULL);
if (modname == NULL || strlen(modname) > MAXNAMELEN - 1)
return (EINVAL);
if ((rc = alloc_mod(modname, &mid)) != 0)
return (rc);
imp = hold_mod(mid);
ASSERT(imp != NULL);
LOCK_MOD(imp, RW_WRITER);
DBG1(DBG_MOD, "registering module '%s'\n", imp->ippm_name);
imp->ippm_ops = ipp_ops;
imp->ippm_state = IPP_MODSTATE_AVAILABLE;
UNLOCK_MOD(imp);
rele_mod(imp);
return (0);
}
#undef __FN__
#define __FN__ "ipp_mod_unregister"
int
ipp_mod_unregister(
ipp_mod_id_t mid)
{
ipp_mod_t *imp;
if ((imp = hold_mod(mid)) == NULL)
return (ENOENT);
LOCK_MOD(imp, RW_WRITER);
ASSERT(imp->ippm_state == IPP_MODSTATE_AVAILABLE);
if (is_mod_busy(imp)) {
UNLOCK_MOD(imp);
rele_mod(imp);
return (EBUSY);
}
DBG1(DBG_MOD, "unregistering module '%s'\n", imp->ippm_name);
imp->ippm_state = IPP_MODSTATE_PROTO;
imp->ippm_ops = NULL;
UNLOCK_MOD(imp);
free_mod(imp);
rele_mod(imp);
return (0);
}
#undef __FN__
#define __FN__ "ipp_mod_list_actions"
int
ipp_mod_list_actions(
ipp_mod_id_t mid,
ipp_action_id_t **bufp,
int *neltp)
{
ipp_mod_t *imp;
int rc;
ASSERT(bufp != NULL);
ASSERT(neltp != NULL);
if ((imp = hold_mod(mid)) == NULL)
return (ENOENT);
LOCK_MOD(imp, RW_READER);
rc = get_mod_ref(imp, bufp, neltp);
UNLOCK_MOD(imp);
rele_mod(imp);
return (rc);
}
#undef __FN__
#define __FN__ "ipp_action_lookup"
ipp_action_id_t
ipp_action_lookup(
const char *aname)
{
if (aname == NULL)
return (IPP_ACTION_INVAL);
if (strcmp(aname, IPP_ANAME_CONT) == 0)
return (IPP_ACTION_CONT);
else if (strcmp(aname, IPP_ANAME_DEFER) == 0)
return (IPP_ACTION_DEFER);
else if (strcmp(aname, IPP_ANAME_DROP) == 0)
return (IPP_ACTION_DROP);
return (find_action(aname));
}
#undef __FN__
#define __FN__ "ipp_action_name"
int
ipp_action_name(
ipp_action_id_t aid,
char **anamep)
{
ipp_action_t *ap;
char *aname;
char *buf;
int rc;
ASSERT(anamep != NULL);
switch (aid) {
case IPP_ACTION_CONT:
ap = NULL;
aname = IPP_ANAME_CONT;
break;
case IPP_ACTION_DEFER:
ap = NULL;
aname = IPP_ANAME_DEFER;
break;
case IPP_ACTION_DROP:
ap = NULL;
aname = IPP_ANAME_DROP;
break;
default:
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
LOCK_ACTION(ap, RW_READER);
aname = ap->ippa_name;
break;
}
if ((buf = kmem_zalloc(strlen(aname) + 1, KM_NOSLEEP)) == NULL) {
rc = ENOMEM;
goto done;
}
(void) strcpy(buf, aname);
*anamep = buf;
rc = 0;
done:
if (ap != NULL) {
UNLOCK_ACTION(ap);
rele_action(ap);
}
return (rc);
}
#undef __FN__
#define __FN__ "ipp_action_mod"
int
ipp_action_mod(
ipp_action_id_t aid,
ipp_mod_id_t *midp)
{
ipp_action_t *ap;
ipp_mod_t *imp;
ASSERT(midp != NULL);
switch (aid) {
case IPP_ACTION_CONT:
case IPP_ACTION_DEFER:
case IPP_ACTION_DROP:
return (EINVAL);
default:
break;
}
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_state == IPP_ASTATE_PROTO) {
UNLOCK_ACTION(ap);
rele_action(ap);
return (ENOENT);
}
imp = ap->ippa_mod;
ASSERT(imp != NULL);
UNLOCK_ACTION(ap);
*midp = imp->ippm_id;
rele_action(ap);
return (0);
}
#undef __FN__
#define __FN__ "ipp_action_create"
int
ipp_action_create(
ipp_mod_id_t mid,
const char *aname,
nvlist_t **nvlpp,
ipp_flags_t flags,
ipp_action_id_t *aidp)
{
ipp_ops_t *ippo;
ipp_mod_t *imp;
ipp_action_id_t aid;
ipp_action_t *ap;
int rc;
ASSERT(nvlpp != NULL);
ASSERT(*nvlpp != NULL);
if (aname != NULL && strlen(aname) > MAXNAMELEN - 1)
return (EINVAL);
if ((imp = hold_mod(mid)) == NULL)
return (ENOENT);
if ((rc = alloc_action(aname, &aid)) != 0) {
rele_mod(imp);
return (rc);
}
ap = hold_action(aid);
ASSERT(ap != NULL);
LOCK_ACTION(ap, RW_WRITER);
ap->ippa_state = IPP_ASTATE_CONFIG_PENDING;
LOCK_MOD(imp, RW_WRITER);
if ((rc = ref_mod(ap, imp)) != 0) {
UNLOCK_MOD(imp);
ap->ippa_state = IPP_ASTATE_PROTO;
UNLOCK_ACTION(ap);
free_action(ap);
rele_action(ap);
rele_mod(imp);
return (rc);
}
UNLOCK_ACTION(ap);
ippo = imp->ippm_ops;
ASSERT(ippo != NULL);
UNLOCK_MOD(imp);
CONFIG_WRITE_START(ap);
DBG2(DBG_ACTION, "creating action '%s' in module '%s'\n",
ap->ippa_name, imp->ippm_name);
if ((rc = ippo->ippo_action_create(ap->ippa_id, nvlpp, flags)) != 0) {
LOCK_ACTION(ap, RW_WRITER);
LOCK_MOD(imp, RW_WRITER);
unref_mod(ap, imp);
UNLOCK_MOD(imp);
ap->ippa_state = IPP_ASTATE_PROTO;
UNLOCK_ACTION(ap);
CONFIG_WRITE_END(ap);
free_action(ap);
rele_action(ap);
rele_mod(imp);
return (rc);
}
CONFIG_WRITE_END(ap);
LOCK_ACTION(ap, RW_WRITER);
ap->ippa_state = IPP_ASTATE_AVAILABLE;
if (aidp != NULL)
*aidp = ap->ippa_id;
UNLOCK_ACTION(ap);
rele_action(ap);
rele_mod(imp);
return (0);
}
#undef __FN__
#define __FN__ "ipp_action_destroy"
int
ipp_action_destroy(
ipp_action_id_t aid,
ipp_flags_t flags)
{
ipp_ref_t *rp = NULL;
ipp_ref_t *tmp;
ipp_action_t *ap;
int rc;
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
ap->ippa_condemned = &rp;
if ((rc = destroy_action(ap, flags)) == 0) {
for (tmp = rp; tmp != NULL; tmp = tmp->ippr_nextp) {
ap = tmp->ippr_action;
ap->ippa_condemned = &rp;
(void) destroy_action(ap, flags);
}
} else {
for (tmp = rp; tmp != NULL; tmp = tmp->ippr_nextp) {
ap = tmp->ippr_action;
rele_action(ap);
}
}
while (rp != NULL) {
tmp = rp;
rp = rp->ippr_nextp;
kmem_free(tmp, sizeof (ipp_ref_t));
}
return (rc);
}
#undef __FN__
#define __FN__ "ipp_action_modify"
int
ipp_action_modify(
ipp_action_id_t aid,
nvlist_t **nvlpp,
ipp_flags_t flags)
{
ipp_action_t *ap;
ipp_ops_t *ippo;
ipp_mod_t *imp;
int rc;
ASSERT(nvlpp != NULL);
ASSERT(*nvlpp != NULL);
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_state != IPP_ASTATE_AVAILABLE &&
ap->ippa_state != IPP_ASTATE_CONFIG_PENDING) {
UNLOCK_ACTION(ap);
rele_action(ap);
return (EPROTO);
}
imp = ap->ippa_mod;
ASSERT(imp != NULL);
UNLOCK_ACTION(ap);
ippo = imp->ippm_ops;
ASSERT(ippo != NULL);
DBG1(DBG_ACTION, "modifying action '%s'\n", ap->ippa_name);
CONFIG_WRITE_START(ap);
rc = ippo->ippo_action_modify(aid, nvlpp, flags);
CONFIG_WRITE_END(ap);
rele_action(ap);
return (rc);
}
#undef __FN__
#define __FN__ "ipp_action_info"
int
ipp_action_info(
ipp_action_id_t aid,
int (*fn)(nvlist_t *, void *),
void *arg,
ipp_flags_t flags)
{
ipp_action_t *ap;
ipp_mod_t *imp;
ipp_ops_t *ippo;
int rc;
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_state != IPP_ASTATE_AVAILABLE) {
UNLOCK_ACTION(ap);
rele_action(ap);
return (EPROTO);
}
imp = ap->ippa_mod;
ASSERT(imp != NULL);
UNLOCK_ACTION(ap);
ippo = imp->ippm_ops;
ASSERT(ippo != NULL);
DBG1(DBG_ACTION,
"getting configuration information from action '%s'\n",
ap->ippa_name);
CONFIG_READ_START(ap);
if ((rc = ippo->ippo_action_info(aid, fn, arg, flags)) != 0) {
CONFIG_READ_END(ap);
rele_action(ap);
return (rc);
}
CONFIG_READ_END(ap);
rele_action(ap);
return (0);
}
#undef __FN__
#define __FN__ "ipp_action_set_ptr"
void
ipp_action_set_ptr(
ipp_action_id_t aid,
void *ptr)
{
ipp_action_t *ap;
ap = hold_action(aid);
ASSERT(ap != NULL);
ap->ippa_ptr = ptr;
rele_action(ap);
}
#undef __FN__
#define __FN__ "ipp_action_get_ptr"
void *
ipp_action_get_ptr(
ipp_action_id_t aid)
{
ipp_action_t *ap;
void *ptr;
ap = hold_action(aid);
ASSERT(ap != NULL);
ptr = ap->ippa_ptr;
rele_action(ap);
return (ptr);
}
#undef __FN__
#define __FN__ "ipp_action_ref"
int
ipp_action_ref(
ipp_action_id_t aid,
ipp_action_id_t ref_aid,
ipp_flags_t flags)
{
ipp_action_t *ap;
ipp_action_t *ref_ap;
int rc;
if (aid == ref_aid)
return (EINVAL);
switch (ref_aid) {
case IPP_ACTION_CONT:
case IPP_ACTION_DEFER:
case IPP_ACTION_DROP:
return (0);
default:
break;
}
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
if ((ref_ap = hold_action(ref_aid)) == NULL) {
rele_action(ap);
return (ENOENT);
}
LOCK_ACTION(ap, RW_WRITER);
LOCK_ACTION(ref_ap, RW_WRITER);
if (ref_ap->ippa_state != IPP_ASTATE_AVAILABLE) {
UNLOCK_ACTION(ref_ap);
UNLOCK_ACTION(ap);
rele_action(ref_ap);
rele_action(ap);
return (EPROTO);
}
rc = ref_action(ap, ref_ap);
UNLOCK_ACTION(ref_ap);
UNLOCK_ACTION(ap);
rele_action(ref_ap);
rele_action(ap);
return (rc);
}
#undef __FN__
#define __FN__ "ipp_action_unref"
int
ipp_action_unref(
ipp_action_id_t aid,
ipp_action_id_t ref_aid,
ipp_flags_t flags)
{
ipp_action_t *ap;
ipp_action_t *ref_ap;
int ref_is_busy;
int rc;
if (aid == ref_aid)
return (EINVAL);
switch (ref_aid) {
case IPP_ACTION_CONT:
case IPP_ACTION_DEFER:
case IPP_ACTION_DROP:
return (0);
default:
break;
}
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
if ((ref_ap = hold_action(ref_aid)) == NULL) {
rele_action(ap);
return (ENOENT);
}
LOCK_ACTION(ap, RW_WRITER);
LOCK_ACTION(ref_ap, RW_WRITER);
if ((rc = unref_action(ap, ref_ap)) != 0) {
UNLOCK_ACTION(ref_ap);
UNLOCK_ACTION(ap);
rele_action(ref_ap);
rele_action(ap);
return (rc);
}
ref_is_busy = is_action_refd(ref_ap);
UNLOCK_ACTION(ref_ap);
UNLOCK_ACTION(ap);
if (flags & IPP_DESTROY_REF) {
if (!ref_is_busy) {
(void) condemn_action(ap->ippa_condemned, ref_ap);
return (0);
}
}
rele_action(ref_ap);
rele_action(ap);
return (0);
}
#undef __FN__
#define __FN__ "ipp_packet_alloc"
int
ipp_packet_alloc(
ipp_packet_t **ppp,
const char *name,
ipp_action_id_t aid)
{
ipp_packet_t *pp;
int rc;
ASSERT(ppp != NULL);
if (name == NULL || strlen(name) > MAXNAMELEN - 1)
return (EINVAL);
if ((rc = alloc_packet(name, aid, &pp)) != 0)
return (rc);
if (ipp_packet_logging != 0 && pp->ippp_log == NULL) {
if ((pp->ippp_log = kmem_alloc(
ipp_packet_log_entries * sizeof (ipp_log_t),
KM_NOSLEEP)) != NULL) {
pp->ippp_log_limit = ipp_packet_log_entries - 1;
pp->ippp_log_windex = 0;
}
} else if (ipp_packet_logging == 0 && pp->ippp_log != NULL) {
kmem_free(pp->ippp_log,
(pp->ippp_log_limit + 1) * sizeof (ipp_log_t));
pp->ippp_log = NULL;
pp->ippp_log_limit = 0;
pp->ippp_log_windex = 0;
}
*ppp = pp;
return (0);
}
#undef __FN__
#define __FN__ "ipp_packet_free"
void
ipp_packet_free(
ipp_packet_t *pp)
{
ASSERT(pp != NULL);
if (pp->ippp_private) {
pp->ippp_private_free(pp->ippp_private);
pp->ippp_private = NULL;
pp->ippp_private_free = NULL;
}
free_packet(pp);
}
#undef __FN__
#define __FN__ "ipp_packet_add_class"
int
ipp_packet_add_class(
ipp_packet_t *pp,
const char *name,
ipp_action_id_t aid)
{
ipp_class_t *cp;
int rc;
ASSERT(pp != NULL);
if (name == NULL || strlen(name) > MAXNAMELEN - 1)
return (EINVAL);
if (pp->ippp_class_windex == pp->ippp_class_limit) {
if ((rc = realloc_packet(pp)) != 0)
return (rc);
}
ASSERT(pp->ippp_class_windex < pp->ippp_class_limit);
cp = &(pp->ippp_class_array[pp->ippp_class_windex++]);
(void) strcpy(cp->ippc_name, name);
cp->ippc_aid = aid;
return (0);
}
#undef __FN__
#define __FN__ "ipp_packet_process"
int
ipp_packet_process(
ipp_packet_t **ppp)
{
ipp_packet_t *pp;
ipp_action_id_t aid;
ipp_class_t *cp;
ipp_log_t *lp;
ipp_action_t *ap;
ipp_mod_t *imp;
ipp_ops_t *ippo;
int rc;
ASSERT(ppp != NULL);
pp = *ppp;
ASSERT(pp != NULL);
while (pp->ippp_class_rindex < pp->ippp_class_windex) {
cp = &(pp->ippp_class_array[pp->ippp_class_rindex]);
aid = cp->ippc_aid;
while (aid != IPP_ACTION_CONT &&
aid != IPP_ACTION_DEFER &&
aid != IPP_ACTION_DROP) {
ASSERT(aid != IPP_ACTION_INVAL);
if ((ap = hold_action(aid)) == NULL) {
DBG1(DBG_PACKET,
"action id '%d' not found\n", aid);
return (ENOENT);
}
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_state != IPP_ASTATE_AVAILABLE) {
UNLOCK_ACTION(ap);
rele_action(ap);
return (EPROTO);
}
atomic_inc_32(&(ap->ippa_packets));
imp = ap->ippa_mod;
ASSERT(imp != NULL);
UNLOCK_ACTION(ap);
ippo = imp->ippm_ops;
ASSERT(ippo != NULL);
if (pp->ippp_log != NULL &&
pp->ippp_log_windex <= pp->ippp_log_limit) {
lp = &(pp->ippp_log[pp->ippp_log_windex++]);
lp->ippl_aid = aid;
(void) strcpy(lp->ippl_name, cp->ippc_name);
gethrestime(&lp->ippl_begin);
} else {
lp = NULL;
}
rc = ippo->ippo_action_invoke(aid, pp);
if (lp != NULL)
gethrestime(&lp->ippl_end);
atomic_dec_32(&(ap->ippa_packets));
if (cp->ippc_aid == aid) {
DBG1(DBG_PACKET,
"action '%s' did not set next action\n",
ap->ippa_name);
rele_action(ap);
return (EPROTO);
}
if (rc != 0) {
DBG2(DBG_PACKET,
"action error '%d' from action '%s'\n",
rc, ap->ippa_name);
rele_action(ap);
return (rc);
}
rele_action(ap);
aid = cp->ippc_aid;
}
if (cp->ippc_aid == IPP_ACTION_DEFER) {
*ppp = NULL;
return (0);
}
if (cp->ippc_aid == IPP_ACTION_DROP) {
freemsg(pp->ippp_data);
ipp_packet_free(pp);
*ppp = NULL;
return (0);
}
ASSERT(cp->ippc_aid == IPP_ACTION_CONT);
pp->ippp_class_rindex++;
}
return (0);
}
#undef __FN__
#define __FN__ "ipp_packet_next"
int
ipp_packet_next(
ipp_packet_t *pp,
ipp_action_id_t aid)
{
ipp_action_t *ap;
ipp_class_t *cp;
ASSERT(pp != NULL);
cp = &(pp->ippp_class_array[pp->ippp_class_rindex]);
ASSERT(cp != NULL);
switch (aid) {
case IPP_ACTION_INVAL:
return (EINVAL);
case IPP_ACTION_DEFER:
case IPP_ACTION_CONT:
case IPP_ACTION_DROP:
break;
default:
if ((ap = hold_action(aid)) == NULL) {
DBG0(DBG_PACKET, "invalid action\n");
return (ENOENT);
}
rele_action(ap);
break;
}
cp->ippc_aid = aid;
return (0);
}
#undef __FN__
#define __FN__ "ipp_packet_set_data"
void
ipp_packet_set_data(
ipp_packet_t *pp,
mblk_t *data)
{
ASSERT(pp != NULL);
pp->ippp_data = data;
}
#undef __FN__
#define __FN__ "ipp_packet_get_data"
mblk_t *
ipp_packet_get_data(
ipp_packet_t *pp)
{
ASSERT(pp != NULL);
return (pp->ippp_data);
}
#undef __FN__
#define __FN__ "ipp_packet_set_private"
void
ipp_packet_set_private(
ipp_packet_t *pp,
void *buf,
void (*free_func)(void *))
{
ASSERT(pp != NULL);
ASSERT(free_func != NULL);
pp->ippp_private = buf;
pp->ippp_private_free = free_func;
}
#undef __FN__
#define __FN__ "ipp_packet_get_private"
void *
ipp_packet_get_private(
ipp_packet_t *pp)
{
ASSERT(pp != NULL);
return (pp->ippp_private);
}
#undef __FN__
#define __FN__ "ipp_stat_create"
int
ipp_stat_create(
ipp_action_id_t aid,
const char *name,
int nstat,
int (*update)(ipp_stat_t *, void *, int),
void *arg,
ipp_stat_t **spp)
{
ipp_action_t *ap;
ipp_mod_t *imp;
ipp_stat_impl_t *sip;
ipp_stat_t *sp;
kstat_t *ksp;
char *class;
char *modname;
int instance;
ASSERT(spp != NULL);
if (name == NULL || nstat <= 0 || update == NULL)
return (EINVAL);
if ((ap = hold_action(aid)) == NULL)
return (ENOENT);
LOCK_ACTION(ap, RW_READER);
class = ap->ippa_name;
instance = (int)ap->ippa_id;
imp = ap->ippa_mod;
ASSERT(imp != NULL);
LOCK_MOD(imp, RW_READER);
modname = imp->ippm_name;
if ((sip = kmem_alloc(sizeof (ipp_stat_impl_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
DBG2(DBG_STATS, "creating stat set '%s' for action '%s'\n",
name, class);
if ((ksp = kstat_create(modname, instance, name, class,
KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_WRITABLE)) == NULL) {
kmem_free(sip, sizeof (ipp_stat_impl_t));
UNLOCK_ACTION(ap);
UNLOCK_MOD(imp);
return (EINVAL);
}
UNLOCK_ACTION(ap);
UNLOCK_MOD(imp);
DBG1(DBG_STATS, "ks_data = %p\n", ksp->ks_data);
ksp->ks_update = update_stats;
ksp->ks_private = (void *)sip;
sip->ippsi_ksp = ksp;
sip->ippsi_data = ksp->ks_data;
(void) strcpy(sip->ippsi_name, name);
sip->ippsi_arg = arg;
sip->ippsi_update = update;
sip->ippsi_limit = nstat;
sip->ippsi_count = 0;
mutex_init(sip->ippsi_lock, NULL, MUTEX_ADAPTIVE,
(void *)ipltospl(LOCK_LEVEL));
sp = (ipp_stat_t *)sip;
ASSERT(sp->ipps_data == sip->ippsi_data);
*spp = sp;
rele_action(ap);
return (0);
}
#undef __FN__
#define __FN__ "ipp_stat_install"
void
ipp_stat_install(
ipp_stat_t *sp)
{
ipp_stat_impl_t *sip = (ipp_stat_impl_t *)sp;
ASSERT(sp != NULL);
DBG1(DBG_STATS, "installing stat set '%s'\n", sip->ippsi_name);
kstat_install(sip->ippsi_ksp);
}
#undef __FN__
#define __FN__ "ipp_stat_destroy"
void
ipp_stat_destroy(
ipp_stat_t *sp)
{
ipp_stat_impl_t *sip = (ipp_stat_impl_t *)sp;
ASSERT(sp != NULL);
DBG1(DBG_STATS, "destroying stat set '%s'\n", sip->ippsi_name);
kstat_delete(sip->ippsi_ksp);
mutex_destroy(sip->ippsi_lock);
kmem_free(sip, sizeof (ipp_stat_impl_t));
}
#undef __FN__
#define __FN__ "ipp_stat_named_init"
int
ipp_stat_named_init(
ipp_stat_t *sp,
const char *name,
uchar_t type,
ipp_named_t *np)
{
ipp_stat_impl_t *sip = (ipp_stat_impl_t *)sp;
uchar_t ktype;
ASSERT(sp != NULL);
ASSERT(np != NULL);
if (name == NULL)
return (EINVAL);
if ((type & IPP_STAT_TAG) == 0)
return (EINVAL);
ktype = type & ~IPP_STAT_TAG;
mutex_enter(sip->ippsi_lock);
if (sip->ippsi_count >= sip->ippsi_limit) {
mutex_exit(sip->ippsi_lock);
return (ENOSPC);
}
sip->ippsi_count++;
DBG3(DBG_STATS, "%s.%s: knp = %p\n", sip->ippsi_name, name, np);
kstat_named_init(np, name, ktype);
mutex_exit(sip->ippsi_lock);
return (0);
}
#undef __FN__
#define __FN__ "ipp_stat_named_op"
int
ipp_stat_named_op(
ipp_named_t *np,
void *valp,
int rw)
{
kstat_named_t *knp;
uchar_t type;
int rc = 0;
ASSERT(np != NULL);
ASSERT(valp != NULL);
knp = np;
type = knp->data_type | IPP_STAT_TAG;
switch (rw) {
case IPP_STAT_WRITE:
switch (type) {
case IPP_STAT_INT32:
*(int32_t *)valp = knp->value.i32;
break;
case IPP_STAT_UINT32:
*(uint32_t *)valp = knp->value.ui32;
break;
case IPP_STAT_INT64:
*(int64_t *)valp = knp->value.i64;
break;
case IPP_STAT_UINT64:
*(uint64_t *)valp = knp->value.ui64;
break;
case IPP_STAT_STRING:
(void) strncpy(valp, knp->value.c, 16);
break;
default:
ASSERT(0);
break;
}
break;
case IPP_STAT_READ:
switch (type) {
case IPP_STAT_INT32:
knp->value.i32 = *(int32_t *)valp;
break;
case IPP_STAT_UINT32:
knp->value.ui32 = *(uint32_t *)valp;
break;
case IPP_STAT_INT64:
knp->value.i64 = *(int64_t *)valp;
break;
case IPP_STAT_UINT64:
knp->value.ui64 = *(uint64_t *)valp;
break;
case IPP_STAT_STRING:
(void) strncpy(knp->value.c, valp, 16);
break;
default:
ASSERT(0);
break;
}
break;
default:
rc = EINVAL;
}
return (rc);
}
#undef __FN__
#define __FN__ "ref_mod"
static int
ref_mod(
ipp_action_t *ap,
ipp_mod_t *imp)
{
ipp_ref_t **rpp;
ipp_ref_t *rp;
ASSERT(rw_write_held(ap->ippa_lock));
ASSERT(rw_write_held(imp->ippm_lock));
rpp = &(imp->ippm_action);
while ((rp = *rpp) != NULL) {
ASSERT(rp->ippr_action != ap);
rpp = &(rp->ippr_nextp);
}
if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
rp->ippr_action = ap;
*rpp = rp;
ap->ippa_mod = imp;
return (0);
}
#undef __FN__
#define __FN__ "unref_mod"
static void
unref_mod(
ipp_action_t *ap,
ipp_mod_t *imp)
{
ipp_ref_t **rpp;
ipp_ref_t *rp;
ASSERT(rw_write_held(ap->ippa_lock));
ASSERT(rw_write_held(imp->ippm_lock));
rpp = &(imp->ippm_action);
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == ap)
break;
rpp = &(rp->ippr_nextp);
}
ASSERT(rp != NULL);
*rpp = rp->ippr_nextp;
kmem_free(rp, sizeof (ipp_ref_t));
ap->ippa_mod = NULL;
}
#undef __FN__
#define __FN__ "is_mod_busy"
static int
is_mod_busy(
ipp_mod_t *imp)
{
return (imp->ippm_action != NULL);
}
#undef __FN__
#define __FN__ "get_mod_ref"
static int
get_mod_ref(
ipp_mod_t *imp,
ipp_action_id_t **bufp,
int *neltp)
{
ipp_ref_t *rp;
int nelt;
ipp_action_t *ap;
ipp_action_id_t *buf;
int length;
ASSERT(rw_lock_held(imp->ippm_lock));
nelt = 0;
for (rp = imp->ippm_action; rp != NULL; rp = rp->ippr_nextp) {
nelt++;
}
DBG1(DBG_LIST, "%d actions found\n", nelt);
if (nelt == 0) {
*bufp = NULL;
*neltp = 0;
return (0);
}
length = nelt * sizeof (ipp_action_id_t);
if ((buf = kmem_alloc(length, KM_NOSLEEP)) == NULL)
return (ENOMEM);
*bufp = buf;
*neltp = nelt;
for (rp = imp->ippm_action; rp != NULL; rp = rp->ippr_nextp) {
ap = rp->ippr_action;
*buf++ = ap->ippa_id;
}
ASSERT((uintptr_t)buf == (uintptr_t)*bufp + length);
return (0);
}
#undef __FN__
#define __FN__ "get_mods"
static int
get_mods(
ipp_mod_id_t **bufp,
int *neltp)
{
ipp_mod_id_t *buf;
int length;
ipp_mod_id_t mid;
ipp_mod_t *imp;
rw_enter(ipp_mod_byname_lock, RW_READER);
if (ipp_mod_count == 0) {
DBG0(DBG_LIST, "no modules registered\n");
*bufp = NULL;
*neltp = 0;
rw_exit(ipp_mod_byname_lock);
return (0);
}
DBG1(DBG_LIST, "%d modules registered\n", ipp_mod_count);
length = ipp_mod_count * sizeof (ipp_mod_id_t);
if ((buf = kmem_alloc(length, KM_NOSLEEP)) == NULL) {
rw_exit(ipp_mod_byname_lock);
return (ENOMEM);
}
rw_enter(ipp_mod_byid_lock, RW_READER);
*bufp = buf;
*neltp = ipp_mod_count;
for (mid = IPP_MOD_RESERVED + 1; mid <= ipp_mid_limit; mid++) {
if ((imp = ipp_mod_byid[mid]) == NULL)
continue;
LOCK_MOD(imp, RW_READER);
ASSERT(imp->ippm_id == mid);
if (imp->ippm_destruct_pending) {
UNLOCK_MOD(imp);
continue;
}
UNLOCK_MOD(imp);
*buf++ = mid;
}
rw_exit(ipp_mod_byid_lock);
rw_exit(ipp_mod_byname_lock);
ASSERT((uintptr_t)buf == (uintptr_t)*bufp + length);
return (0);
}
#undef __FN__
#define __FN__ "find_mod"
static ipp_mod_id_t
find_mod(
const char *modname)
{
ipp_mod_id_t mid;
ipp_mod_t *imp;
ipp_ref_t *rp;
int hb;
ASSERT(modname != NULL);
rw_enter(ipp_mod_byname_lock, RW_READER);
if (ipp_mod_count == 0) {
rw_exit(ipp_mod_byname_lock);
return (IPP_MOD_INVAL);
}
hb = hash(modname);
rp = ipp_mod_byname[hb];
while (rp != NULL) {
imp = rp->ippr_mod;
if (strcmp(imp->ippm_name, modname) == 0)
break;
rp = rp->ippr_nextp;
}
if (rp == NULL) {
rw_exit(ipp_mod_byname_lock);
return (IPP_MOD_INVAL);
}
if (imp->ippm_state == IPP_MODSTATE_PROTO) {
rw_exit(ipp_mod_byname_lock);
return (IPP_MOD_INVAL);
}
mid = imp->ippm_id;
rw_exit(ipp_mod_byname_lock);
return (mid);
}
#undef __FN__
#define __FN__ "alloc_mod"
static int
alloc_mod(
const char *modname,
ipp_mod_id_t *midp)
{
ipp_mod_t *imp;
ipp_ref_t **rpp;
ipp_ref_t *rp;
int hb;
ASSERT(modname != NULL);
ASSERT(midp != NULL);
rw_enter(ipp_mod_byname_lock, RW_WRITER);
hb = hash(modname);
rpp = &ipp_mod_byname[hb];
while ((rp = *rpp) != NULL) {
imp = rp->ippr_mod;
if (strcmp(imp->ippm_name, modname) == 0) {
DBG1(DBG_MOD, "module '%s' already exists\n", modname);
rw_exit(ipp_mod_byname_lock);
return (EEXIST);
}
rpp = &(rp->ippr_nextp);
}
if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL) {
rw_exit(ipp_mod_byname_lock);
return (ENOMEM);
}
if ((imp = kmem_cache_alloc(ipp_mod_cache, KM_NOSLEEP)) == NULL) {
kmem_free(rp, sizeof (ipp_ref_t));
rw_exit(ipp_mod_byname_lock);
return (ENOMEM);
}
(void) strcpy(imp->ippm_name, modname);
LOCK_MOD(imp, RW_WRITER);
imp->ippm_destruct_pending = B_FALSE;
UNLOCK_MOD(imp);
rp->ippr_mod = imp;
*rpp = rp;
ipp_mod_count++;
*midp = imp->ippm_id;
rw_exit(ipp_mod_byname_lock);
return (0);
}
#undef __FN__
#define __FN__ "free_mod"
static void
free_mod(
ipp_mod_t *imp)
{
ipp_ref_t **rpp;
ipp_ref_t *rp;
int hb;
rw_enter(ipp_mod_byname_lock, RW_WRITER);
hb = hash(imp->ippm_name);
rpp = &ipp_mod_byname[hb];
while ((rp = *rpp) != NULL) {
if (rp->ippr_mod == imp)
break;
rpp = &(rp->ippr_nextp);
}
ASSERT(rp != NULL);
*rpp = rp->ippr_nextp;
kmem_free(rp, sizeof (ipp_ref_t));
ipp_mod_count--;
*imp->ippm_name = '\0';
LOCK_MOD(imp, RW_WRITER);
imp->ippm_destruct_pending = B_TRUE;
if (imp->ippm_hold_count == 0) {
UNLOCK_MOD(imp);
kmem_cache_free(ipp_mod_cache, imp);
rw_exit(ipp_mod_byname_lock);
return;
}
UNLOCK_MOD(imp);
rw_exit(ipp_mod_byname_lock);
}
#undef __FN__
#define __FN__ "hold_mod"
static ipp_mod_t *
hold_mod(
ipp_mod_id_t mid)
{
ipp_mod_t *imp;
if (mid < 0)
return (NULL);
rw_enter(ipp_mod_byid_lock, RW_READER);
if ((imp = ipp_mod_byid[mid]) == NULL) {
rw_exit(ipp_mod_byid_lock);
return (NULL);
}
ASSERT(imp->ippm_id == mid);
LOCK_MOD(imp, RW_READER);
if (imp->ippm_destruct_pending) {
UNLOCK_MOD(imp);
rw_exit(ipp_mod_byid_lock);
return (NULL);
}
UNLOCK_MOD(imp);
atomic_inc_32(&(imp->ippm_hold_count));
rw_exit(ipp_mod_byid_lock);
return (imp);
}
#undef __FN__
#define __FN__ "rele_mod"
static void
rele_mod(
ipp_mod_t *imp)
{
ASSERT(imp->ippm_hold_count != 0);
atomic_dec_32(&(imp->ippm_hold_count));
LOCK_MOD(imp, RW_READER);
if (imp->ippm_destruct_pending && imp->ippm_hold_count == 0) {
UNLOCK_MOD(imp);
kmem_cache_free(ipp_mod_cache, imp);
return;
}
UNLOCK_MOD(imp);
}
#undef __FN__
#define __FN__ "get_mid"
static ipp_mod_id_t
get_mid(
void)
{
int index;
int start;
int limit;
ASSERT(rw_write_held(ipp_mod_byid_lock));
start = (int)ipp_next_mid;
limit = (int)ipp_mid_limit;
index = start;
while (ipp_mod_byid[index] != NULL) {
index++;
if (index > limit)
index = IPP_MOD_RESERVED + 1;
if (index == start)
return (IPP_MOD_INVAL);
}
index++;
if (index > limit) {
ipp_next_mid = IPP_MOD_RESERVED + 1;
} else
ipp_next_mid = (ipp_mod_id_t)index;
return ((ipp_mod_id_t)(--index));
}
#undef __FN__
#define __FN__ "condemn_action"
static int
condemn_action(
ipp_ref_t **rpp,
ipp_action_t *ap)
{
ipp_ref_t *rp;
DBG1(DBG_ACTION, "condemning action '%s'\n", ap->ippa_name);
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == ap)
break;
rpp = &(rp->ippr_nextp);
}
if (rp == NULL) {
if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
rp->ippr_action = ap;
*rpp = rp;
}
return (0);
}
#undef __FN__
#define __FN__ "destroy_action"
static int
destroy_action(
ipp_action_t *ap,
ipp_flags_t flags)
{
ipp_ops_t *ippo;
ipp_mod_t *imp;
#define MAXWAIT 10
uint32_t wait;
int rc;
LOCK_ACTION(ap, RW_WRITER);
if (ap->ippa_state != IPP_ASTATE_AVAILABLE) {
UNLOCK_ACTION(ap);
rele_action(ap);
return (EPROTO);
}
ap->ippa_state = IPP_ASTATE_CONFIG_PENDING;
for (wait = 0; ap->ippa_packets > 0 && wait < (MAXWAIT * 1000000);
wait += 1000) {
drv_usecwait(1000);
}
if (ap->ippa_packets > 0) {
ap->ippa_state = IPP_ASTATE_AVAILABLE;
UNLOCK_ACTION(ap);
rele_action(ap);
return (EAGAIN);
}
if (is_action_refd(ap)) {
ap->ippa_state = IPP_ASTATE_AVAILABLE;
UNLOCK_ACTION(ap);
rele_action(ap);
return (EBUSY);
}
imp = ap->ippa_mod;
ASSERT(imp != NULL);
UNLOCK_ACTION(ap);
ippo = imp->ippm_ops;
ASSERT(ippo != NULL);
CONFIG_WRITE_START(ap);
DBG1(DBG_ACTION, "destroying action '%s'\n", ap->ippa_name);
if ((rc = ippo->ippo_action_destroy(ap->ippa_id, flags)) != 0) {
LOCK_ACTION(ap, RW_WRITER);
ap->ippa_state = IPP_ASTATE_AVAILABLE;
UNLOCK_ACTION(ap);
CONFIG_WRITE_END(ap);
rele_action(ap);
return (rc);
}
CONFIG_WRITE_END(ap);
LOCK_ACTION(ap, RW_WRITER);
LOCK_MOD(imp, RW_WRITER);
unref_mod(ap, imp);
UNLOCK_MOD(imp);
ap->ippa_state = IPP_ASTATE_PROTO;
UNLOCK_ACTION(ap);
ASSERT(ap->ippa_ref == NULL);
free_action(ap);
rele_action(ap);
return (0);
#undef MAXWAIT
}
#undef __FN__
#define __FN__ "ref_action"
static int
ref_action(
ipp_action_t *refby_ap,
ipp_action_t *ref_ap)
{
ipp_ref_t **rpp;
ipp_ref_t **save_rpp;
ipp_ref_t *rp;
ASSERT(rw_write_held(refby_ap->ippa_lock));
ASSERT(rw_write_held(ref_ap->ippa_lock));
rpp = &(refby_ap->ippa_ref);
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == ref_ap)
break;
rpp = &(rp->ippr_nextp);
}
if ((rp = *rpp) != NULL) {
rp->ippr_count++;
rp = ref_ap->ippa_refby;
while (rp != NULL) {
if (rp->ippr_action == refby_ap)
break;
rp = rp->ippr_nextp;
}
ASSERT(rp != NULL);
rp->ippr_count++;
} else {
if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL)
return (ENOMEM);
rp->ippr_action = ref_ap;
rp->ippr_count = 1;
*rpp = rp;
save_rpp = rpp;
rpp = &(ref_ap->ippa_refby);
while ((rp = *rpp) != NULL) {
ASSERT(rp->ippr_action != refby_ap);
rpp = &(rp->ippr_nextp);
}
if ((rp = kmem_zalloc(sizeof (ipp_ref_t),
KM_NOSLEEP)) == NULL) {
rpp = save_rpp;
rp = *rpp;
*rpp = NULL;
kmem_free(rp, sizeof (ipp_ref_t));
return (ENOMEM);
}
rp->ippr_action = refby_ap;
rp->ippr_count = 1;
*rpp = rp;
}
return (0);
}
#undef __FN__
#define __FN__ "unref_action"
static int
unref_action(
ipp_action_t *refby_ap,
ipp_action_t *ref_ap)
{
ipp_ref_t **rpp;
ipp_ref_t *rp;
ASSERT(rw_write_held(refby_ap->ippa_lock));
ASSERT(rw_write_held(ref_ap->ippa_lock));
rpp = &(refby_ap->ippa_ref);
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == ref_ap)
break;
rpp = &(rp->ippr_nextp);
}
if (rp == NULL)
return (ENOENT);
if (rp->ippr_count > 1) {
rp->ippr_count--;
rp = ref_ap->ippa_refby;
while (rp != NULL) {
if (rp->ippr_action == refby_ap)
break;
rp = rp->ippr_nextp;
}
ASSERT(rp != NULL);
rp->ippr_count--;
} else {
*rpp = rp->ippr_nextp;
kmem_free(rp, sizeof (ipp_ref_t));
rpp = &(ref_ap->ippa_refby);
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == refby_ap)
break;
rpp = &(rp->ippr_nextp);
}
ASSERT(rp != NULL);
*rpp = rp->ippr_nextp;
kmem_free(rp, sizeof (ipp_ref_t));
}
return (0);
}
#undef __FN__
#define __FN__ "is_action_refd"
static int
is_action_refd(
ipp_action_t *ap)
{
return (ap->ippa_refby != NULL);
}
#undef __FN__
#define __FN__ "find_action"
static ipp_action_id_t
find_action(
const char *aname)
{
ipp_action_id_t aid;
ipp_action_t *ap;
ipp_ref_t *rp;
int hb;
ASSERT(aname != NULL);
rw_enter(ipp_action_byname_lock, RW_READER);
if (ipp_action_count == 0) {
rw_exit(ipp_action_byname_lock);
return (IPP_ACTION_INVAL);
}
hb = hash(aname);
rp = ipp_action_byname[hb];
while (rp != NULL) {
ap = rp->ippr_action;
if (strcmp(ap->ippa_name, aname) == 0)
break;
rp = rp->ippr_nextp;
}
if (rp == NULL) {
rw_exit(ipp_action_byname_lock);
return (IPP_ACTION_INVAL);
}
if (ap->ippa_state == IPP_ASTATE_PROTO) {
rw_exit(ipp_action_byname_lock);
return (IPP_ACTION_INVAL);
}
aid = ap->ippa_id;
rw_exit(ipp_action_byname_lock);
return (aid);
}
#undef __FN__
#define __FN__ "alloc_action"
static int
alloc_action(
const char *aname,
ipp_action_id_t *aidp)
{
ipp_action_t *ap;
ipp_ref_t **rpp;
ipp_ref_t *rp;
int hb;
ASSERT(aidp != NULL);
rw_enter(ipp_action_byname_lock, RW_WRITER);
if (aname != NULL) {
hb = hash(aname);
rpp = &ipp_action_byname[hb];
} else
rpp = &ipp_action_noname;
while ((rp = *rpp) != NULL) {
ap = rp->ippr_action;
if (aname != NULL && strcmp(ap->ippa_name, aname) == 0) {
DBG1(DBG_ACTION, "action '%s' already exists\n",
aname);
rw_exit(ipp_action_byname_lock);
return (EEXIST);
}
rpp = &(rp->ippr_nextp);
}
if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL) {
rw_exit(ipp_action_byname_lock);
return (ENOMEM);
}
if ((ap = kmem_cache_alloc(ipp_action_cache, KM_NOSLEEP)) == NULL) {
kmem_free(rp, sizeof (ipp_ref_t));
rw_exit(ipp_action_byname_lock);
return (ENOMEM);
}
if (aname == NULL) {
(void) sprintf(ap->ippa_name, "$%08X", ap->ippa_id);
ap->ippa_nameless = B_TRUE;
} else
(void) strcpy(ap->ippa_name, aname);
LOCK_ACTION(ap, RW_WRITER);
ap->ippa_destruct_pending = B_FALSE;
UNLOCK_ACTION(ap);
rp->ippr_action = ap;
*rpp = rp;
ipp_action_count++;
*aidp = ap->ippa_id;
rw_exit(ipp_action_byname_lock);
return (0);
}
#undef __FN__
#define __FN__ "free_action"
static void
free_action(
ipp_action_t *ap)
{
ipp_ref_t **rpp;
ipp_ref_t *rp;
int hb;
rw_enter(ipp_action_byname_lock, RW_WRITER);
if (!ap->ippa_nameless) {
hb = hash(ap->ippa_name);
rpp = &ipp_action_byname[hb];
} else
rpp = &ipp_action_noname;
while ((rp = *rpp) != NULL) {
if (rp->ippr_action == ap)
break;
rpp = &(rp->ippr_nextp);
}
ASSERT(rp != NULL);
*rpp = rp->ippr_nextp;
kmem_free(rp, sizeof (ipp_ref_t));
ipp_action_count--;
*ap->ippa_name = '\0';
LOCK_ACTION(ap, RW_WRITER);
ap->ippa_destruct_pending = B_TRUE;
if (ap->ippa_hold_count == 0) {
UNLOCK_ACTION(ap);
kmem_cache_free(ipp_action_cache, ap);
rw_exit(ipp_action_byname_lock);
return;
}
UNLOCK_ACTION(ap);
rw_exit(ipp_action_byname_lock);
}
#undef __FN__
#define __FN__ "hold_action"
static ipp_action_t *
hold_action(
ipp_action_id_t aid)
{
ipp_action_t *ap;
if (aid < 0)
return (NULL);
rw_enter(ipp_action_byid_lock, RW_READER);
if ((ap = ipp_action_byid[aid]) == NULL) {
rw_exit(ipp_action_byid_lock);
return (NULL);
}
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_destruct_pending) {
UNLOCK_ACTION(ap);
rw_exit(ipp_action_byid_lock);
return (NULL);
}
UNLOCK_ACTION(ap);
atomic_inc_32(&(ap->ippa_hold_count));
rw_exit(ipp_action_byid_lock);
return (ap);
}
#undef __FN__
#define __FN__ "rele_action"
static void
rele_action(
ipp_action_t *ap)
{
ASSERT(ap->ippa_hold_count != 0);
atomic_dec_32(&(ap->ippa_hold_count));
LOCK_ACTION(ap, RW_READER);
if (ap->ippa_destruct_pending && ap->ippa_hold_count == 0) {
UNLOCK_ACTION(ap);
kmem_cache_free(ipp_action_cache, ap);
return;
}
UNLOCK_ACTION(ap);
}
#undef __FN__
#define __FN__ "get_aid"
static ipp_action_id_t
get_aid(
void)
{
int index;
int start;
int limit;
ASSERT(rw_write_held(ipp_action_byid_lock));
start = (int)ipp_next_aid;
limit = (int)ipp_aid_limit;
index = start;
while (ipp_action_byid[index] != NULL) {
index++;
if (index > limit)
index = IPP_ACTION_RESERVED + 1;
if (index == start)
return (IPP_ACTION_INVAL);
}
index++;
if (index > limit)
ipp_next_aid = IPP_ACTION_RESERVED + 1;
else
ipp_next_aid = (ipp_action_id_t)index;
return ((ipp_action_id_t)(--index));
}
#undef __FN__
#define __FN__ "alloc_packet"
static int
alloc_packet(
const char *name,
ipp_action_id_t aid,
ipp_packet_t **ppp)
{
ipp_packet_t *pp;
ipp_class_t *cp;
if ((pp = kmem_cache_alloc(ipp_packet_cache, KM_NOSLEEP)) == NULL)
return (ENOMEM);
cp = &(pp->ippp_class_array[0]);
pp->ippp_class_windex = 1;
(void) strcpy(cp->ippc_name, name);
cp->ippc_aid = aid;
*ppp = pp;
return (0);
}
#undef __FN__
#define __FN__ "realloc_packet"
static int
realloc_packet(
ipp_packet_t *pp)
{
uint_t length;
ipp_class_t *array;
length = (pp->ippp_class_limit + 1) << 1;
if ((array = kmem_alloc(length * sizeof (ipp_class_t),
KM_NOSLEEP)) == NULL)
return (ENOMEM);
bcopy(pp->ippp_class_array, array,
(length >> 1) * sizeof (ipp_class_t));
kmem_free(pp->ippp_class_array,
(length >> 1) * sizeof (ipp_class_t));
pp->ippp_class_array = array;
pp->ippp_class_limit = length - 1;
return (0);
}
#undef __FN__
#define __FN__ "free_packet"
static void
free_packet(
ipp_packet_t *pp)
{
pp->ippp_class_windex = 0;
pp->ippp_class_rindex = 0;
pp->ippp_data = NULL;
pp->ippp_private = NULL;
kmem_cache_free(ipp_packet_cache, pp);
}
#undef __FN__
#define __FN__ "hash"
static int
hash(
const char *name)
{
int val = 0;
char *ptr;
for (ptr = (char *)name; *ptr != '\0'; ptr++) {
val ^= *ptr;
}
return (val % IPP_NBUCKET);
}
#undef __FN__
#define __FN__ "update_stats"
static int
update_stats(
kstat_t *ksp,
int rw)
{
ipp_stat_impl_t *sip;
ASSERT(ksp->ks_private != NULL);
sip = (ipp_stat_impl_t *)ksp->ks_private;
return (sip->ippsi_update((ipp_stat_t *)sip, sip->ippsi_arg, rw));
}
#undef __FN__
#define __FN__ "init_mods"
static void
init_mods(
void)
{
rw_init(ipp_mod_byid_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
ipp_mod_byid = kmem_zalloc(sizeof (ipp_mod_t *) * (ipp_max_mod + 1),
KM_SLEEP);
ipp_mod_byid[ipp_max_mod] = (ipp_mod_t *)-1;
ipp_mid_limit = (ipp_mod_id_t)(ipp_max_mod - 1);
ipp_mod_cache = kmem_cache_create("ipp_mod", sizeof (ipp_mod_t),
IPP_ALIGN, mod_constructor, mod_destructor, NULL, NULL, NULL, 0);
ASSERT(ipp_mod_cache != NULL);
rw_init(ipp_mod_byname_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
bzero(ipp_mod_byname, IPP_NBUCKET * sizeof (ipp_ref_t *));
}
#undef __FN__
#define __FN__ "init_actions"
static void
init_actions(
void)
{
rw_init(ipp_action_byid_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
ipp_action_byid = kmem_zalloc(sizeof (ipp_action_t *) *
(ipp_max_action + 1), KM_SLEEP);
ipp_action_byid[ipp_max_action] = (ipp_action_t *)-1;
ipp_aid_limit = (ipp_action_id_t)(ipp_max_action - 1);
ipp_action_cache = kmem_cache_create("ipp_action",
sizeof (ipp_action_t), IPP_ALIGN, action_constructor,
action_destructor, NULL, NULL, NULL, 0);
ASSERT(ipp_action_cache != NULL);
rw_init(ipp_action_byname_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
bzero(ipp_action_byname, IPP_NBUCKET * sizeof (ipp_ref_t *));
ipp_action_noname = NULL;
}
#undef __FN__
#define __FN__ "init_packets"
static void
init_packets(
void)
{
ipp_packet_cache = kmem_cache_create("ipp_packet",
sizeof (ipp_packet_t), IPP_ALIGN, packet_constructor,
packet_destructor, NULL, NULL, NULL, 0);
ASSERT(ipp_packet_cache != NULL);
}
#undef __FN__
#define __FN__ "mod_constructor"
static int
mod_constructor(
void *buf,
void *cdrarg,
int kmflags)
{
ipp_mod_t *imp;
ipp_mod_id_t mid;
ASSERT(buf != NULL);
bzero(buf, sizeof (ipp_mod_t));
imp = (ipp_mod_t *)buf;
rw_enter(ipp_mod_byid_lock, RW_WRITER);
if ((mid = get_mid()) <= IPP_MOD_RESERVED) {
rw_exit(ipp_mod_byid_lock);
return (-1);
}
imp->ippm_destruct_pending = B_TRUE;
imp->ippm_state = IPP_MODSTATE_PROTO;
rw_init(imp->ippm_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
imp->ippm_id = mid;
ipp_mod_byid[mid] = imp;
rw_exit(ipp_mod_byid_lock);
return (0);
}
#undef __FN__
#define __FN__ "mod_destructor"
static void
mod_destructor(
void *buf,
void *cdrarg)
{
ipp_mod_t *imp;
ASSERT(buf != NULL);
imp = (ipp_mod_t *)buf;
ASSERT(imp->ippm_state == IPP_MODSTATE_PROTO);
ASSERT(imp->ippm_action == NULL);
ASSERT(*imp->ippm_name == '\0');
ASSERT(imp->ippm_destruct_pending);
rw_enter(ipp_mod_byid_lock, RW_WRITER);
ASSERT(imp->ippm_hold_count == 0);
ipp_mod_byid[imp->ippm_id] = NULL;
rw_destroy(imp->ippm_lock);
rw_exit(ipp_mod_byid_lock);
}
#undef __FN__
#define __FN__ "action_constructor"
static int
action_constructor(
void *buf,
void *cdrarg,
int kmflags)
{
ipp_action_t *ap;
ipp_action_id_t aid;
ASSERT(buf != NULL);
bzero(buf, sizeof (ipp_action_t));
ap = (ipp_action_t *)buf;
rw_enter(ipp_action_byid_lock, RW_WRITER);
if ((aid = get_aid()) <= IPP_ACTION_RESERVED) {
rw_exit(ipp_action_byid_lock);
return (-1);
}
ap->ippa_state = IPP_ASTATE_PROTO;
ap->ippa_destruct_pending = B_TRUE;
rw_init(ap->ippa_lock, NULL, RW_DEFAULT,
(void *)ipltospl(LOCK_LEVEL));
CONFIG_LOCK_INIT(ap->ippa_config_lock);
ap->ippa_id = aid;
ipp_action_byid[aid] = ap;
rw_exit(ipp_action_byid_lock);
return (0);
}
#undef __FN__
#define __FN__ "action_destructor"
static void
action_destructor(
void *buf,
void *cdrarg)
{
ipp_action_t *ap;
ASSERT(buf != NULL);
ap = (ipp_action_t *)buf;
ASSERT(ap->ippa_state == IPP_ASTATE_PROTO);
ASSERT(ap->ippa_ref == NULL);
ASSERT(ap->ippa_refby == NULL);
ASSERT(ap->ippa_packets == 0);
ASSERT(*ap->ippa_name == '\0');
ASSERT(ap->ippa_destruct_pending);
rw_enter(ipp_action_byid_lock, RW_WRITER);
ASSERT(ap->ippa_hold_count == 0);
ipp_action_byid[ap->ippa_id] = NULL;
CONFIG_LOCK_FINI(ap->ippa_config_lock);
rw_destroy(ap->ippa_lock);
rw_exit(ipp_action_byid_lock);
}
#undef __FN__
#define __FN__ "packet_constructor"
static int
packet_constructor(
void *buf,
void *cdrarg,
int kmflags)
{
ipp_packet_t *pp;
ipp_class_t *cp;
ASSERT(buf != NULL);
bzero(buf, sizeof (ipp_packet_t));
pp = (ipp_packet_t *)buf;
if ((cp = kmem_alloc(ipp_packet_classes * sizeof (ipp_class_t),
KM_NOSLEEP)) == NULL)
return (ENOMEM);
pp->ippp_class_array = cp;
pp->ippp_class_windex = 0;
pp->ippp_class_rindex = 0;
pp->ippp_class_limit = ipp_packet_classes - 1;
return (0);
}
#undef __FN__
#define __FN__ "packet_destructor"
static void
packet_destructor(
void *buf,
void *cdrarg)
{
ipp_packet_t *pp;
ASSERT(buf != NULL);
pp = (ipp_packet_t *)buf;
ASSERT(pp->ippp_data == NULL);
ASSERT(pp->ippp_class_windex == 0);
ASSERT(pp->ippp_class_rindex == 0);
ASSERT(pp->ippp_private == NULL);
ASSERT(pp->ippp_private_free == NULL);
kmem_free(pp->ippp_class_array,
(pp->ippp_class_limit + 1) * sizeof (ipp_class_t));
if (pp->ippp_log != NULL) {
kmem_free(pp->ippp_log,
(pp->ippp_log_limit + 1) * sizeof (ipp_log_t));
}
}
#undef __FN__
#ifdef IPP_DBG
static void
ipp_debug(
uint64_t type,
const char *fn,
char *fmt,
...)
{
char buf[255];
va_list adx;
if ((type & ipp_debug_flags) == 0)
return;
mutex_enter(debug_mutex);
va_start(adx, fmt);
(void) vsnprintf(buf, 255, fmt, adx);
va_end(adx);
printf("(%llx) %s: %s", (unsigned long long)curthread->t_did, fn,
buf);
mutex_exit(debug_mutex);
}
#endif