#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/linker.h>
#include <cpu/atomic.h>
#include <dev/disk/dm/dm.h>
#include <dev/disk/dm/netbsd-dm.h>
static dm_target_t *dm_target_lookup_name(const char *);
static TAILQ_HEAD(, dm_target) dm_target_list;
static struct lock dm_target_mutex;
void
dm_target_busy(dm_target_t *target)
{
atomic_add_int(&target->ref_cnt, 1);
}
void
dm_target_unbusy(dm_target_t *target)
{
KKASSERT(target->ref_cnt > 0);
atomic_subtract_int(&target->ref_cnt, 1);
}
dm_target_t *
dm_target_autoload(const char *dm_target_name)
{
char mod_name[128];
dm_target_t *dmt;
linker_file_t linker_file;
int error;
ksnprintf(mod_name, sizeof(mod_name), "dm_target_%s", dm_target_name);
error = linker_reference_module(mod_name, NULL, &linker_file);
if (error != 0) {
kprintf("dm: could not autoload module for target %s\n",
dm_target_name);
return NULL;
}
dmt = dm_target_lookup(dm_target_name);
if (dmt == NULL) {
linker_release_module(NULL, NULL, linker_file);
return NULL;
}
linker_file->userrefs = 1;
return dmt;
}
dm_target_t *
dm_target_lookup(const char *dm_target_name)
{
dm_target_t *dmt;
dmt = NULL;
if (dm_target_name == NULL)
return NULL;
lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
dmt = dm_target_lookup_name(dm_target_name);
if (dmt != NULL)
dm_target_busy(dmt);
lockmgr(&dm_target_mutex, LK_RELEASE);
return dmt;
}
static dm_target_t *
dm_target_lookup_name(const char *dm_target_name)
{
dm_target_t *dm_target;
TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) {
if (strcmp(dm_target_name, dm_target->name) == 0)
return dm_target;
}
return NULL;
}
int
dm_target_insert(dm_target_t *dm_target)
{
dm_target_t *dmt;
if (dm_target->strategy == NULL) {
kprintf("dm: %s missing strategy\n", dm_target->name);
return EINVAL;
}
lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
dmt = dm_target_lookup_name(dm_target->name);
if (dmt != NULL) {
kprintf("uhoh, target_insert EEXIST\n");
lockmgr(&dm_target_mutex, LK_RELEASE);
return EEXIST;
}
TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next);
lockmgr(&dm_target_mutex, LK_RELEASE);
return 0;
}
int
dm_target_remove(char *dm_target_name)
{
dm_target_t *dmt;
KKASSERT(dm_target_name != NULL);
lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
dmt = dm_target_lookup_name(dm_target_name);
if (dmt == NULL) {
lockmgr(&dm_target_mutex, LK_RELEASE);
return ENOENT;
}
if (dmt->ref_cnt > 0) {
lockmgr(&dm_target_mutex, LK_RELEASE);
return EBUSY;
}
TAILQ_REMOVE(&dm_target_list, dmt, dm_target_next);
lockmgr(&dm_target_mutex, LK_RELEASE);
dm_target_free(dmt);
return 0;
}
dm_target_t *
dm_target_alloc(const char *name)
{
dm_target_t *dmt;
dmt = kmalloc(sizeof(*dmt), M_DM, M_WAITOK | M_ZERO);
if (dmt == NULL)
return NULL;
if (name)
strlcpy(dmt->name, name, sizeof(dmt->name));
return dmt;
}
int
dm_target_free(dm_target_t *dmt)
{
KKASSERT(dmt != NULL);
kfree(dmt, M_DM);
return 0;
}
prop_array_t
dm_target_prop_list(void)
{
prop_array_t target_array, ver;
prop_dictionary_t target_dict;
dm_target_t *dm_target;
size_t i;
target_array = prop_array_create();
lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) {
target_dict = prop_dictionary_create();
ver = prop_array_create();
prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME,
dm_target->name);
for (i = 0; i < 3; i++)
prop_array_add_uint32(ver, dm_target->version[i]);
prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver);
prop_array_add(target_array, target_dict);
prop_object_release(ver);
prop_object_release(target_dict);
}
lockmgr(&dm_target_mutex, LK_RELEASE);
return target_array;
}
int
dm_target_init(void)
{
TAILQ_INIT(&dm_target_list);
lockinit(&dm_target_mutex, "dmtrgt", 0, LK_CANRECURSE);
return 0;
}
int
dm_target_uninit(void)
{
dm_target_t *dm_target;
lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
while ((dm_target = TAILQ_FIRST(&dm_target_list)) != NULL) {
TAILQ_REMOVE(&dm_target_list, dm_target, dm_target_next);
dm_target_free(dm_target);
}
KKASSERT(TAILQ_EMPTY(&dm_target_list));
lockmgr(&dm_target_mutex, LK_RELEASE);
lockuninit(&dm_target_mutex);
return 0;
}