/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Debug switch. */ #if defined(DEBUG) #define IPP_DBG #endif /* * Globals */ /* * ipp_action_count is not static because it is imported by inet/ipp_common.h */ 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; /* * Prototypes */ 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 *); /* * Debug message macros */ #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 = /* * DBG_PACKET | * DBG_STATS | * DBG_LIST | * DBG_MOD | * DBG_ACTION | */ 0; static kmutex_t debug_mutex[1]; /*PRINTFLIKE3*/ 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 /* IPP_DBG */ #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 /* IPP_DBG */ /* * Lock macros */ #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) /* * Exported functions */ #define __FN__ "ipp_init" void ipp_init( void) { #ifdef IPP_DBG mutex_init(debug_mutex, NULL, MUTEX_ADAPTIVE, (void *)ipltospl(LOCK_LEVEL)); #endif /* IPP_DBG */ /* * Initialize module and action structure caches and associated locks. */ 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__ /* * Module manipulation interface. */ #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; /* * Sanity check the module name. */ if (modname == NULL || strlen(modname) > MAXNAMELEN - 1) return (IPP_MOD_INVAL); try_again: if ((mid = find_mod(modname)) == IPP_MOD_INVAL) { /* * Module not installed. */ if (try++ == FIRST_TIME) { /* * This is the first attempt to find the module so * try to 'demand load' it. */ 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); /* * Translate the module id into the module pointer. */ if ((imp = hold_mod(mid)) == NULL) return (ENOENT); LOCK_MOD(imp, RW_READER); modname = imp->ippm_name; /* * Allocate a buffer to pass back to the caller. */ if ((buf = kmem_zalloc(strlen(modname) + 1, KM_NOSLEEP)) == NULL) { UNLOCK_MOD(imp); rele_mod(imp); return (ENOMEM); } /* * Copy the module name into the buffer. */ (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); /* * Sanity check the module name. */ if (modname == NULL || strlen(modname) > MAXNAMELEN - 1) return (EINVAL); /* * Allocate a module structure. */ if ((rc = alloc_mod(modname, &mid)) != 0) return (rc); imp = hold_mod(mid); ASSERT(imp != NULL); /* * Make module available for use. */ 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; /* * Translate the module id into the module pointer. */ if ((imp = hold_mod(mid)) == NULL) return (ENOENT); LOCK_MOD(imp, RW_WRITER); ASSERT(imp->ippm_state == IPP_MODSTATE_AVAILABLE); /* * Check to see if there are any actions that reference the module. */ if (is_mod_busy(imp)) { UNLOCK_MOD(imp); rele_mod(imp); return (EBUSY); } /* * Prevent further use of the module. */ DBG1(DBG_MOD, "unregistering module '%s'\n", imp->ippm_name); imp->ippm_state = IPP_MODSTATE_PROTO; imp->ippm_ops = NULL; UNLOCK_MOD(imp); /* * Free the module structure. */ 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); /* * Translate the module id into the module pointer. */ if ((imp = hold_mod(mid)) == NULL) return (ENOENT); /* * Get the list of actions referencing the module. */ LOCK_MOD(imp, RW_READER); rc = get_mod_ref(imp, bufp, neltp); UNLOCK_MOD(imp); rele_mod(imp); return (rc); } #undef __FN__ /* * Action manipulation interface. */ #define __FN__ "ipp_action_lookup" ipp_action_id_t ipp_action_lookup( const char *aname) { if (aname == NULL) return (IPP_ACTION_INVAL); /* * Check for special case 'virtual action' names. */ 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); /* * Now check real actions. */ 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); /* * Check for special case 'virtual action' ids. */ 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: /* * Not a special case. Check for a real action. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); LOCK_ACTION(ap, RW_READER); aname = ap->ippa_name; break; } /* * Allocate a buffer to pass back to the caller. */ if ((buf = kmem_zalloc(strlen(aname) + 1, KM_NOSLEEP)) == NULL) { rc = ENOMEM; goto done; } /* * Copy the action name into the buffer. */ (void) strcpy(buf, aname); *anamep = buf; rc = 0; done: /* * Unlock the action if necessary (i.e. it wasn't a virtual action). */ 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); /* * Return an error for 'virtual action' ids. */ switch (aid) { case IPP_ACTION_CONT: /*FALLTHRU*/ case IPP_ACTION_DEFER: /*FALLTHRU*/ case IPP_ACTION_DROP: return (EINVAL); default: break; } /* * This is a real action. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); /* * Check that the action is not in prototype state. */ 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); /* * Sanity check the action name (NULL means the framework chooses the * name). */ if (aname != NULL && strlen(aname) > MAXNAMELEN - 1) return (EINVAL); /* * Translate the module id into the module pointer. */ if ((imp = hold_mod(mid)) == NULL) return (ENOENT); /* * Allocate an action. */ if ((rc = alloc_action(aname, &aid)) != 0) { rele_mod(imp); return (rc); } ap = hold_action(aid); ASSERT(ap != NULL); /* * Note that the action is in the process of creation/destruction. */ LOCK_ACTION(ap, RW_WRITER); ap->ippa_state = IPP_ASTATE_CONFIG_PENDING; /* * Reference the module for which the action is being created. */ 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); /* * Call into the module to create the action context. */ 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); /* * Make the action available for use. */ 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; /* * Translate the action id into the action pointer. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); /* * Set the condemned action list pointer and destroy the action. */ ap->ippa_condemned = &rp; if ((rc = destroy_action(ap, flags)) == 0) { /* * Destroy any other actions condemned by the destruction of * the first action. */ for (tmp = rp; tmp != NULL; tmp = tmp->ippr_nextp) { ap = tmp->ippr_action; ap->ippa_condemned = &rp; (void) destroy_action(ap, flags); } } else { /* * Unreference any condemned actions since the destruction of * the first action failed. */ for (tmp = rp; tmp != NULL; tmp = tmp->ippr_nextp) { ap = tmp->ippr_action; rele_action(ap); } } /* * Clean up the condemned list. */ 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); /* * Translate the action id into the action pointer. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); /* * Check that the action is either available for use or is in the * process of creation/destruction. * * NOTE: It is up to the module to lock multiple configuration * operations against each other if necessary. */ 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); /* * Call into the module to modify the action context. */ 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; /* * Translate the action id into the action pointer. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); /* * Check that the action is available for use. We don't want to * read back parameters while the action is in the process of * creation/destruction. */ 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); /* * Call into the module to get the action configuration information. */ 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; /* * Translate the action id into the action pointer. */ ap = hold_action(aid); ASSERT(ap != NULL); /* * Set the private data pointer. */ 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; /* * Translate the action id into the action pointer. */ ap = hold_action(aid); ASSERT(ap != NULL); /* * Return the private data pointer. */ ptr = ap->ippa_ptr; rele_action(ap); return (ptr); } #undef __FN__ #define __FN__ "ipp_action_ref" /*ARGSUSED*/ 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; /* * Actions are not allowed to reference themselves. */ if (aid == ref_aid) return (EINVAL); /* * Check for a special case 'virtual action' id. */ switch (ref_aid) { case IPP_ACTION_CONT: /*FALLTHRU*/ case IPP_ACTION_DEFER: /*FALLTHRU*/ case IPP_ACTION_DROP: return (0); default: break; } /* * Translate the action ids into action pointers. */ 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); } /* * Create references between the two actions. */ 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); /* * Check for a special case 'virtual action' id. */ switch (ref_aid) { case IPP_ACTION_CONT: /*FALLTHRU*/ case IPP_ACTION_DEFER: /*FALLTHRU*/ case IPP_ACTION_DROP: return (0); default: break; } /* * Translate the action ids into action pointers. */ 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); /* * Remove the reference between the actions. */ 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) { /* * Condemn the action so that it will be destroyed. */ (void) condemn_action(ap->ippa_condemned, ref_ap); return (0); } } rele_action(ref_ap); rele_action(ap); return (0); } #undef __FN__ /* * Packet manipulation interface. */ #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); /* * A name is required. */ if (name == NULL || strlen(name) > MAXNAMELEN - 1) return (EINVAL); /* * Allocate a packet structure from the cache. */ if ((rc = alloc_packet(name, aid, &pp)) != 0) return (rc); if (ipp_packet_logging != 0 && pp->ippp_log == NULL) { /* * Logging is turned on but there's no log buffer. We need * to allocate one. */ 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) { /* * A log buffer is present but logging has been turned off. * Free the buffer now, */ 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 there is a private structure pointer set, call its free * function. */ if (pp->ippp_private) { pp->ippp_private_free(pp->ippp_private); pp->ippp_private = NULL; pp->ippp_private_free = NULL; } /* * Free the packet structure back to the cache. */ 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); /* * A name is required. */ if (name == NULL || strlen(name) > MAXNAMELEN - 1) return (EINVAL); /* * Check if there is an available class structure. */ if (pp->ippp_class_windex == pp->ippp_class_limit) { /* * No more structures. Re-allocate the array. */ if ((rc = realloc_packet(pp)) != 0) return (rc); } ASSERT(pp->ippp_class_windex < pp->ippp_class_limit); /* * Set up a new class structure. */ 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); /* * Walk the class list. */ while (pp->ippp_class_rindex < pp->ippp_class_windex) { cp = &(pp->ippp_class_array[pp->ippp_class_rindex]); /* * While there is a real action to invoke... */ aid = cp->ippc_aid; while (aid != IPP_ACTION_CONT && aid != IPP_ACTION_DEFER && aid != IPP_ACTION_DROP) { ASSERT(aid != IPP_ACTION_INVAL); /* * Translate the action id to the action pointer. */ if ((ap = hold_action(aid)) == NULL) { DBG1(DBG_PACKET, "action id '%d' not found\n", aid); return (ENOENT); } /* * Check that the action is available for use... */ LOCK_ACTION(ap, RW_READER); if (ap->ippa_state != IPP_ASTATE_AVAILABLE) { UNLOCK_ACTION(ap); rele_action(ap); return (EPROTO); } /* * Increment the action's packet count to note that * it's being used. * * NOTE: We only have a read lock, so we need to use * atomic_add_32(). The read lock is still * important though as it is crucial to block * out a destroy operation between the action * state being checked and the packet count * being incremented. */ atomic_inc_32(&(ap->ippa_packets)); imp = ap->ippa_mod; ASSERT(imp != NULL); UNLOCK_ACTION(ap); ippo = imp->ippm_ops; ASSERT(ippo != NULL); /* * If there's a log, grab the next entry and fill it * in. */ 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; } /* * Invoke the action. */ rc = ippo->ippo_action_invoke(aid, pp); /* * Also log the time that the action finished * processing. */ if (lp != NULL) gethrestime(&lp->ippl_end); /* * Decrement the packet count. */ atomic_dec_32(&(ap->ippa_packets)); /* * If the class' action id is the same now as it was * before then clearly no 'next action' has been set. * This is a protocol error. */ if (cp->ippc_aid == aid) { DBG1(DBG_PACKET, "action '%s' did not set next action\n", ap->ippa_name); rele_action(ap); return (EPROTO); } /* * The action did not complete successfully. Terminate * packet processing. */ 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); /* * Look at the next action. */ aid = cp->ippc_aid; } /* * No more real actions to invoke, check for 'virtual' ones. */ /* * Packet deferred: module has held onto packet for processing * later. */ if (cp->ippc_aid == IPP_ACTION_DEFER) { *ppp = NULL; return (0); } /* * Packet dropped: free the packet and discontinue processing. */ if (cp->ippc_aid == IPP_ACTION_DROP) { freemsg(pp->ippp_data); ipp_packet_free(pp); *ppp = NULL; return (0); } /* * Must be 'continue processing': move onto the next class. */ 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); /* * Check for a special case 'virtual action' id. */ switch (aid) { case IPP_ACTION_INVAL: return (EINVAL); case IPP_ACTION_DEFER: /*FALLTHRU*/ case IPP_ACTION_CONT: /*FALLTHRU*/ case IPP_ACTION_DROP: break; default: /* * Not a virtual action so try to translate the action id * into the action pointer to confirm the actions existence. */ if ((ap = hold_action(aid)) == NULL) { DBG0(DBG_PACKET, "invalid action\n"); return (ENOENT); } rele_action(ap); break; } /* * Set the class' new action id. */ 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__ /* * Statistics interface. */ #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); /* * Sanity check the arguments. */ if (name == NULL || nstat <= 0 || update == NULL) return (EINVAL); /* * Translate the action id into the action pointer. */ if ((ap = hold_action(aid)) == NULL) return (ENOENT); /* * Grab relevant action and module information. */ 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; /* * Allocate a stats info structure. */ if ((sip = kmem_alloc(sizeof (ipp_stat_impl_t), KM_NOSLEEP)) == NULL) return (ENOMEM); /* * Create a set of kstats. */ 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); /* Assume EINVAL was the cause */ } UNLOCK_ACTION(ap); UNLOCK_MOD(imp); DBG1(DBG_STATS, "ks_data = %p\n", ksp->ks_data); /* * Set up the kstats structure with a private data pointer and an * 'update' function. */ ksp->ks_update = update_stats; ksp->ks_private = (void *)sip; /* * Keep a reference to the kstats structure in our own stats info * structure. */ sip->ippsi_ksp = ksp; sip->ippsi_data = ksp->ks_data; /* * Fill in the rest of the stats info structure. */ (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)); /* * Case the stats info structure to a semi-opaque structure that * we pass back to the caller. */ 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); /* * Install the set of kstats referenced by the stats info structure. */ 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); /* * Destroy the set of kstats referenced by the stats info structure. */ DBG1(DBG_STATS, "destroying stat set '%s'\n", sip->ippsi_name); kstat_delete(sip->ippsi_ksp); /* * Destroy the stats info structure itself. */ 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; /* * Check we will not exceed the maximum number of a stats that was * indicated during set creation. */ mutex_enter(sip->ippsi_lock); if (sip->ippsi_count >= sip->ippsi_limit) { mutex_exit(sip->ippsi_lock); return (ENOSPC); } /* * Bump the count. */ sip->ippsi_count++; /* * Create a new named kstat. */ 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; /* * Copy data to or from the named kstat, depending on the specified * opcode. */ 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); /* should not reach here */ 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); /* should not reach here */ break; } break; default: rc = EINVAL; } return (rc); } #undef __FN__ /* * Local functions (for local people. There's nothing for you here!) */ #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)); /* * Add the new reference at the end of the module's list. */ rpp = &(imp->ippm_action); while ((rp = *rpp) != NULL) { ASSERT(rp->ippr_action != ap); rpp = &(rp->ippr_nextp); } /* * Allocate a reference structure. */ if ((rp = kmem_zalloc(sizeof (ipp_ref_t), KM_NOSLEEP)) == NULL) return (ENOMEM); /* * Set the reference to the action and link it onto the module's list. */ rp->ippr_action = ap; *rpp = rp; /* * Keep a 'back pointer' from the action structure to the module * structure. */ 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)); /* * Scan the module's list for the reference to the action. */ rpp = &(imp->ippm_action); while ((rp = *rpp) != NULL) { if (rp->ippr_action == ap) break; rpp = &(rp->ippr_nextp); } ASSERT(rp != NULL); /* * Unlink the reference structure and free it. */ *rpp = rp->ippr_nextp; kmem_free(rp, sizeof (ipp_ref_t)); /* * NULL the 'back pointer'. */ ap->ippa_mod = NULL; } #undef __FN__ #define __FN__ "is_mod_busy" static int is_mod_busy( ipp_mod_t *imp) { /* * Return a value which is true (non-zero) iff the module refers * to no actions. */ 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)); /* * Count the number of actions referred to from the module structure. */ nelt = 0; for (rp = imp->ippm_action; rp != NULL; rp = rp->ippr_nextp) { nelt++; } DBG1(DBG_LIST, "%d actions found\n", nelt); /* * If there are no actions referred to then there's nothing to do. */ if (nelt == 0) { *bufp = NULL; *neltp = 0; return (0); } /* * Allocate a buffer to pass back to the caller. */ length = nelt * sizeof (ipp_action_id_t); if ((buf = kmem_alloc(length, KM_NOSLEEP)) == NULL) return (ENOMEM); /* * Fill the buffer with an array of action ids. */ *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 there are no modules registered then there's nothing to do. */ if (ipp_mod_count == 0) { DBG0(DBG_LIST, "no modules registered\n"); *bufp = NULL; *neltp = 0; rw_exit(ipp_mod_byname_lock); return (0); } /* * Allocate a buffer to pass back to the caller. */ 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); /* * Search the array of all modules. */ *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; /* * If the module has 'destruct pending' set then it means it * is either still in the cache (i.e not allocated) or in the * process of being set up by alloc_mod(). */ 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); /* * Quick return if no modules are registered. */ if (ipp_mod_count == 0) { rw_exit(ipp_mod_byname_lock); return (IPP_MOD_INVAL); } /* * Find the hash bucket where the module structure should be. */ hb = hash(modname); rp = ipp_mod_byname[hb]; /* * Scan the bucket for a match. */ 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); /* * Find the right hash bucket for a module of the given name. */ hb = hash(modname); rpp = &ipp_mod_byname[hb]; /* * Scan the bucket making sure the module isn't already * registered. */ 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); } /* * Allocate a new reference structure and a new module structure. */ 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); } /* * Set up the name of the new structure. */ (void) strcpy(imp->ippm_name, modname); /* * Make sure the 'destruct pending' flag is clear. This indicates * that the structure is no longer part of the cache. */ LOCK_MOD(imp, RW_WRITER); imp->ippm_destruct_pending = B_FALSE; UNLOCK_MOD(imp); /* * Set the reference and link it into the hash bucket. */ rp->ippr_mod = imp; *rpp = rp; /* * Increment the module count. */ 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); /* * Find the hash bucket where the module structure should be. */ hb = hash(imp->ippm_name); rpp = &ipp_mod_byname[hb]; /* * Scan the bucket for a match. */ while ((rp = *rpp) != NULL) { if (rp->ippr_mod == imp) break; rpp = &(rp->ippr_nextp); } ASSERT(rp != NULL); /* * Unlink the reference structure and free it. */ *rpp = rp->ippr_nextp; kmem_free(rp, sizeof (ipp_ref_t)); /* * Decrement the module count. */ ipp_mod_count--; /* * Empty the name. */ *imp->ippm_name = '\0'; /* * If the hold count is zero then we can free the structure * immediately, otherwise we defer to rele_mod(). */ 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); /* * Use the module id as an index into the array of all module * structures. */ 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); /* * If the modul has 'destruct pending' set then it means it is either * still in the cache (i.e not allocated) or in the process of * being set up by alloc_mod(). */ LOCK_MOD(imp, RW_READER); if (imp->ippm_destruct_pending) { UNLOCK_MOD(imp); rw_exit(ipp_mod_byid_lock); return (NULL); } UNLOCK_MOD(imp); /* * Increment the hold count to prevent the structure from being * freed. */ 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) { /* * This call means we're done with the pointer so we can drop the * hold count. */ ASSERT(imp->ippm_hold_count != 0); atomic_dec_32(&(imp->ippm_hold_count)); /* * If the structure has 'destruct pending' set then we tried to free * it but couldn't, so do it now. */ 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 searching after the last module id we allocated. */ start = (int)ipp_next_mid; limit = (int)ipp_mid_limit; /* * Look for a spare slot in the array. */ index = start; while (ipp_mod_byid[index] != NULL) { index++; if (index > limit) index = IPP_MOD_RESERVED + 1; if (index == start) return (IPP_MOD_INVAL); } /* * Note that we've just allocated a new module id so that we can * start our search there next time. */ 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); /* * Check to see if the action is already condemned. */ while ((rp = *rpp) != NULL) { if (rp->ippr_action == ap) break; rpp = &(rp->ippr_nextp); } /* * Create a new entry for the action. */ 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; /* * Check that the action is available. */ LOCK_ACTION(ap, RW_WRITER); if (ap->ippa_state != IPP_ASTATE_AVAILABLE) { UNLOCK_ACTION(ap); rele_action(ap); return (EPROTO); } /* * Note that the action is in the process of creation/destruction. */ ap->ippa_state = IPP_ASTATE_CONFIG_PENDING; /* * Wait for the in-transit packet count for this action to fall to * zero (checking at millisecond intervals). * * NOTE: no new packets will enter the action now that the * state has been changed. */ for (wait = 0; ap->ippa_packets > 0 && wait < (MAXWAIT * 1000000); wait += 1000) { /* * NOTE: We can hang onto the lock because the packet count is * decremented without needing to take the lock. */ drv_usecwait(1000); } /* * The packet count did not fall to zero. */ if (ap->ippa_packets > 0) { ap->ippa_state = IPP_ASTATE_AVAILABLE; UNLOCK_ACTION(ap); rele_action(ap); return (EAGAIN); } /* * Check to see if any other action has a dependency on this one. */ 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); /* * Call into the module to destroy the action context. */ 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); /* * Free the action structure. */ 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)); /* * We want to add the new reference at the end of the refering * action's list. */ rpp = &(refby_ap->ippa_ref); while ((rp = *rpp) != NULL) { if (rp->ippr_action == ref_ap) break; rpp = &(rp->ippr_nextp); } if ((rp = *rpp) != NULL) { /* * There is an existing reference so increment its counter. */ rp->ippr_count++; /* * Find the 'back pointer' and increment its counter too. */ 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 { /* * Allocate, fill in and link a new reference structure. */ 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; /* * We keep a 'back pointer' which we want to add at the end of * a list in the referred action's structure. */ rpp = &(ref_ap->ippa_refby); while ((rp = *rpp) != NULL) { ASSERT(rp->ippr_action != refby_ap); rpp = &(rp->ippr_nextp); } /* * Allocate another reference structure and, if this fails, * remember to clean up the first reference structure we * allocated. */ 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); } /* * Fill in the reference structure with the 'back pointer' and * link it into the list. */ 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)); /* * Scan for the reference in the referring action's list. */ 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) { /* * There are currently multiple references so decrement the * count. */ rp->ippr_count--; /* * Find the 'back pointer' and decrement its counter too. */ 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 { /* * There is currently only a single reference, so unlink and * free the reference structure. */ *rpp = rp->ippr_nextp; kmem_free(rp, sizeof (ipp_ref_t)); /* * Scan for the 'back pointer' in the referred action's list. */ rpp = &(ref_ap->ippa_refby); while ((rp = *rpp) != NULL) { if (rp->ippr_action == refby_ap) break; rpp = &(rp->ippr_nextp); } ASSERT(rp != NULL); /* * Unlink and free this reference structure too. */ *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 a value which is true (non-zero) iff the action is not * referred to by any other actions. */ 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); /* * Quick return if there are no actions defined at all. */ if (ipp_action_count == 0) { rw_exit(ipp_action_byname_lock); return (IPP_ACTION_INVAL); } /* * Find the hash bucket where the action structure should be. */ hb = hash(aname); rp = ipp_action_byname[hb]; /* * Scan the bucket looking for a match. */ 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); /* * Find the right hash bucket for an action of the given name. * (Nameless actions always go in a special bucket). */ if (aname != NULL) { hb = hash(aname); rpp = &ipp_action_byname[hb]; } else rpp = &ipp_action_noname; /* * Scan the bucket to make sure that an action with the given name * does not already exist. */ 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); } /* * Allocate a new reference structure and a new action structure. */ 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); } /* * Dream up a name if there isn't a real one and note that the action is * really nameless. */ if (aname == NULL) { (void) sprintf(ap->ippa_name, "$%08X", ap->ippa_id); ap->ippa_nameless = B_TRUE; } else (void) strcpy(ap->ippa_name, aname); /* * Make sure the 'destruct pending' flag is clear. This indicates that * the structure is no longer part of the cache. */ LOCK_ACTION(ap, RW_WRITER); ap->ippa_destruct_pending = B_FALSE; UNLOCK_ACTION(ap); /* * Fill in the reference structure and lint it onto the list. */ rp->ippr_action = ap; *rpp = rp; /* * Increment the action count. */ 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); /* * Find the hash bucket where the action structure should be. */ if (!ap->ippa_nameless) { hb = hash(ap->ippa_name); rpp = &ipp_action_byname[hb]; } else rpp = &ipp_action_noname; /* * Scan the bucket for a match. */ while ((rp = *rpp) != NULL) { if (rp->ippr_action == ap) break; rpp = &(rp->ippr_nextp); } ASSERT(rp != NULL); /* * Unlink and free the reference structure. */ *rpp = rp->ippr_nextp; kmem_free(rp, sizeof (ipp_ref_t)); /* * Decrement the action count. */ ipp_action_count--; /* * Empty the name. */ *ap->ippa_name = '\0'; /* * If the hold count is zero then we can free the structure * immediately, otherwise we defer to rele_action(). */ 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); /* * Use the action id as an index into the array of all action * structures. */ rw_enter(ipp_action_byid_lock, RW_READER); if ((ap = ipp_action_byid[aid]) == NULL) { rw_exit(ipp_action_byid_lock); return (NULL); } /* * If the action has 'destruct pending' set then it means it is either * still in the cache (i.e not allocated) or in the process of * being set up by alloc_action(). */ LOCK_ACTION(ap, RW_READER); if (ap->ippa_destruct_pending) { UNLOCK_ACTION(ap); rw_exit(ipp_action_byid_lock); return (NULL); } UNLOCK_ACTION(ap); /* * Increment the hold count to prevent the structure from being * freed. */ 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) { /* * This call means we're done with the pointer so we can drop the * hold count. */ ASSERT(ap->ippa_hold_count != 0); atomic_dec_32(&(ap->ippa_hold_count)); /* * If the structure has 'destruct pending' set then we tried to free * it but couldn't, so do it now. */ 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 searching after the last action id that we allocated. */ start = (int)ipp_next_aid; limit = (int)ipp_aid_limit; /* * Look for a spare slot in the array. */ index = start; while (ipp_action_byid[index] != NULL) { index++; if (index > limit) index = IPP_ACTION_RESERVED + 1; if (index == start) return (IPP_ACTION_INVAL); } /* * Note that we've just allocated a new action id so that we can * start our search there next time. */ 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); /* * Set the packet up with a single class. */ 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; /* * Make a hash value by XORing all the ascii codes in the text string. */ for (ptr = (char *)name; *ptr != '\0'; ptr++) { val ^= *ptr; } /* * Return the value modulo the number of hash buckets we allow. */ 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; /* * Call the update function passed to ipp_stat_create() for the given * set of kstats. */ return (sip->ippsi_update((ipp_stat_t *)sip, sip->ippsi_arg, rw)); } #undef __FN__ #define __FN__ "init_mods" static void init_mods( void) { /* * Initialise the array of all module structures and the module * structure kmem cache. */ 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); /* * Initialize the 'module by name' hash bucket array. */ 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) { /* * Initialise the array of all action structures and the action * structure cache. */ 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); /* * Initialize the 'action by name' hash bucket array (and the special * 'hash' bucket for nameless actions). */ 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) { /* * Initialise the packet structure cache. */ 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__ /* * Kmem cache constructor/destructor functions. */ #define __FN__ "mod_constructor" /*ARGSUSED*/ 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); /* * Get a new module id. */ if ((mid = get_mid()) <= IPP_MOD_RESERVED) { rw_exit(ipp_mod_byid_lock); return (-1); } /* * Initialize the buffer as a module structure in PROTO form. */ imp->ippm_destruct_pending = B_TRUE; imp->ippm_state = IPP_MODSTATE_PROTO; rw_init(imp->ippm_lock, NULL, RW_DEFAULT, (void *)ipltospl(LOCK_LEVEL)); /* * Insert it into the array of all module structures. */ imp->ippm_id = mid; ipp_mod_byid[mid] = imp; rw_exit(ipp_mod_byid_lock); return (0); } #undef __FN__ #define __FN__ "mod_destructor" /*ARGSUSED*/ 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); /* * NULL the entry in the array of all module structures. */ ipp_mod_byid[imp->ippm_id] = NULL; /* * Clean up any remnants of the module structure as the buffer is * about to disappear. */ rw_destroy(imp->ippm_lock); rw_exit(ipp_mod_byid_lock); } #undef __FN__ #define __FN__ "action_constructor" /*ARGSUSED*/ 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); /* * Get a new action id. */ if ((aid = get_aid()) <= IPP_ACTION_RESERVED) { rw_exit(ipp_action_byid_lock); return (-1); } /* * Initialize the buffer as an action structure in PROTO form. */ 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); /* * Insert it into the array of all action structures. */ ap->ippa_id = aid; ipp_action_byid[aid] = ap; rw_exit(ipp_action_byid_lock); return (0); } #undef __FN__ #define __FN__ "action_destructor" /*ARGSUSED*/ 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); /* * NULL the entry in the array of all action structures. */ ipp_action_byid[ap->ippa_id] = NULL; /* * Clean up any remnants of the action structure as the buffer is * about to disappear. */ CONFIG_LOCK_FINI(ap->ippa_config_lock); rw_destroy(ap->ippa_lock); rw_exit(ipp_action_byid_lock); } #undef __FN__ #define __FN__ "packet_constructor" /*ARGSUSED*/ 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" /*ARGSUSED*/ 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__ /* * Debug message printout code. */ #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 /* IPP_DBG */