#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/modctl.h>
#include <sys/overlay_impl.h>
static kmem_cache_t *overlay_plugin_cache;
static kmutex_t overlay_plugin_lock;
static list_t overlay_plugin_list;
#define OVERLAY_MODDIR "overlay"
static int
overlay_plugin_cache_constructor(void *buf, void *arg, int kmflags)
{
overlay_plugin_t *opp = buf;
mutex_init(&opp->ovp_mutex, NULL, MUTEX_DRIVER, NULL);
list_link_init(&opp->ovp_link);
return (0);
}
static void
overlay_plugin_cache_destructor(void *buf, void *arg)
{
overlay_plugin_t *opp = buf;
ASSERT(list_link_active(&opp->ovp_link) == 0);
mutex_destroy(&opp->ovp_mutex);
}
void
overlay_plugin_init(void)
{
mutex_init(&overlay_plugin_lock, NULL, MUTEX_DRIVER, 0);
overlay_plugin_cache = kmem_cache_create("overlay_plugin_cache",
sizeof (overlay_plugin_t), 0, overlay_plugin_cache_constructor,
overlay_plugin_cache_destructor, NULL, NULL, NULL, 0);
list_create(&overlay_plugin_list, sizeof (overlay_plugin_t),
offsetof(overlay_plugin_t, ovp_link));
}
void
overlay_plugin_fini(void)
{
mutex_enter(&overlay_plugin_lock);
VERIFY(list_is_empty(&overlay_plugin_list));
mutex_exit(&overlay_plugin_lock);
list_destroy(&overlay_plugin_list);
kmem_cache_destroy(overlay_plugin_cache);
mutex_destroy(&overlay_plugin_lock);
}
overlay_plugin_register_t *
overlay_plugin_alloc(uint_t version)
{
overlay_plugin_register_t *ovrp;
if (version != OVEP_VERSION_ONE)
return (NULL);
ovrp = kmem_zalloc(sizeof (overlay_plugin_register_t), KM_SLEEP);
ovrp->ovep_version = version;
return (ovrp);
}
void
overlay_plugin_free(overlay_plugin_register_t *ovrp)
{
kmem_free(ovrp, sizeof (overlay_plugin_register_t));
}
int
overlay_plugin_register(overlay_plugin_register_t *ovrp)
{
overlay_plugin_t *opp, *ipp;
if (ovrp->ovep_version != OVEP_VERSION_ONE)
return (EINVAL);
if (ovrp->ovep_name == NULL || ovrp->ovep_ops == NULL)
return (EINVAL);
if ((ovrp->ovep_flags & ~(OVEP_F_VLAN_TAG)) != 0)
return (EINVAL);
if (ovrp->ovep_id_size < 1)
return (EINVAL);
if (ovrp->ovep_id_size > 8)
return (ENOTSUP);
if (ovrp->ovep_dest == OVERLAY_PLUGIN_D_INVALID)
return (EINVAL);
if ((ovrp->ovep_dest & ~OVERLAY_PLUGIN_D_MASK) != 0)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_callbacks != 0)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_init == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_fini == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_encap == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_decap == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_socket == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_getprop == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_setprop == NULL)
return (EINVAL);
if (ovrp->ovep_ops->ovpo_propinfo == NULL)
return (EINVAL);
opp = kmem_cache_alloc(overlay_plugin_cache, KM_SLEEP);
opp->ovp_active = 0;
opp->ovp_name = ovrp->ovep_name;
opp->ovp_ops = ovrp->ovep_ops;
opp->ovp_props = ovrp->ovep_props;
opp->ovp_id_size = ovrp->ovep_id_size;
opp->ovp_flags = ovrp->ovep_flags;
opp->ovp_dest = ovrp->ovep_dest;
opp->ovp_nprops = 0;
if (ovrp->ovep_props != NULL) {
while (ovrp->ovep_props[opp->ovp_nprops] != NULL) {
if (strlen(ovrp->ovep_props[opp->ovp_nprops]) >=
OVERLAY_PROP_NAMELEN) {
mutex_exit(&overlay_plugin_lock);
kmem_cache_free(overlay_plugin_cache, opp);
return (EINVAL);
}
opp->ovp_nprops++;
}
}
mutex_enter(&overlay_plugin_lock);
for (ipp = list_head(&overlay_plugin_list); ipp != NULL;
ipp = list_next(&overlay_plugin_list, ipp)) {
if (strcmp(ipp->ovp_name, opp->ovp_name) == 0) {
mutex_exit(&overlay_plugin_lock);
kmem_cache_free(overlay_plugin_cache, opp);
return (EEXIST);
}
}
list_insert_tail(&overlay_plugin_list, opp);
mutex_exit(&overlay_plugin_lock);
return (0);
}
int
overlay_plugin_unregister(const char *name)
{
overlay_plugin_t *opp;
mutex_enter(&overlay_plugin_lock);
for (opp = list_head(&overlay_plugin_list); opp != NULL;
opp = list_next(&overlay_plugin_list, opp)) {
if (strcmp(opp->ovp_name, name) == 0)
break;
}
if (opp == NULL) {
mutex_exit(&overlay_plugin_lock);
return (ENOENT);
}
mutex_enter(&opp->ovp_mutex);
if (opp->ovp_active > 0) {
mutex_exit(&opp->ovp_mutex);
mutex_exit(&overlay_plugin_lock);
return (EBUSY);
}
mutex_exit(&opp->ovp_mutex);
list_remove(&overlay_plugin_list, opp);
mutex_exit(&overlay_plugin_lock);
kmem_cache_free(overlay_plugin_cache, opp);
return (0);
}
overlay_plugin_t *
overlay_plugin_lookup(const char *name)
{
overlay_plugin_t *opp;
boolean_t trymodload = B_FALSE;
for (;;) {
mutex_enter(&overlay_plugin_lock);
for (opp = list_head(&overlay_plugin_list); opp != NULL;
opp = list_next(&overlay_plugin_list, opp)) {
if (strcmp(name, opp->ovp_name) == 0) {
mutex_enter(&opp->ovp_mutex);
opp->ovp_active++;
mutex_exit(&opp->ovp_mutex);
mutex_exit(&overlay_plugin_lock);
return (opp);
}
}
mutex_exit(&overlay_plugin_lock);
if (trymodload == B_TRUE)
return (NULL);
if (modload(OVERLAY_MODDIR, (char *)name) == -1)
return (NULL);
trymodload = B_TRUE;
}
}
void
overlay_plugin_rele(overlay_plugin_t *opp)
{
mutex_enter(&opp->ovp_mutex);
ASSERT(opp->ovp_active > 0);
opp->ovp_active--;
mutex_exit(&opp->ovp_mutex);
}
void
overlay_plugin_walk(overlay_plugin_walk_f func, void *arg)
{
overlay_plugin_t *opp;
mutex_enter(&overlay_plugin_lock);
for (opp = list_head(&overlay_plugin_list); opp != NULL;
opp = list_next(&overlay_plugin_list, opp)) {
if (func(opp, arg) != 0) {
mutex_exit(&overlay_plugin_lock);
return;
}
}
mutex_exit(&overlay_plugin_lock);
}