#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dm_pdev.c,v 1.23 2021/08/21 22:23:33 andvar Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/fcntl.h>
#include <sys/kmem.h>
#include <sys/namei.h>
#include <dev/dkvar.h>
#include "dm.h"
static SLIST_HEAD(dm_pdevs, dm_pdev) dm_pdev_list;
static kmutex_t dm_pdev_mutex;
static dm_pdev_t *dm_pdev_alloc(const char *);
static int dm_pdev_rem(dm_pdev_t *);
static dm_pdev_t *dm_pdev_lookup_name(const char *);
dm_pdev_t *
dm_pdev_lookup_name(const char *dm_pdev_name)
{
dm_pdev_t *dm_pdev;
size_t dlen;
size_t slen;
KASSERT(dm_pdev_name != NULL);
slen = strlen(dm_pdev_name);
SLIST_FOREACH(dm_pdev, &dm_pdev_list, next_pdev) {
dlen = strlen(dm_pdev->name);
if (slen != dlen)
continue;
if (strncmp(dm_pdev_name, dm_pdev->name, slen) == 0)
return dm_pdev;
}
return NULL;
}
dm_pdev_t *
dm_pdev_insert(const char *dev_name)
{
struct pathbuf *dev_pb;
dm_pdev_t *dmp;
int error;
KASSERT(dev_name != NULL);
mutex_enter(&dm_pdev_mutex);
dmp = dm_pdev_lookup_name(dev_name);
if (dmp != NULL) {
dmp->ref_cnt++;
aprint_debug("%s: pdev %s already in tree\n",
__func__, dev_name);
mutex_exit(&dm_pdev_mutex);
return dmp;
}
if ((dmp = dm_pdev_alloc(dev_name)) == NULL) {
mutex_exit(&dm_pdev_mutex);
return NULL;
}
dev_pb = pathbuf_create(dev_name);
if (dev_pb == NULL) {
aprint_debug("%s: pathbuf_create on device: %s failed!\n",
__func__, dev_name);
mutex_exit(&dm_pdev_mutex);
kmem_free(dmp, sizeof(dm_pdev_t));
return NULL;
}
error = vn_bdev_openpath(dev_pb, &dmp->pdev_vnode, curlwp);
pathbuf_destroy(dev_pb);
if (error) {
aprint_debug("%s: lookup on device: %s (error %d)\n",
__func__, dev_name, error);
mutex_exit(&dm_pdev_mutex);
kmem_free(dmp, sizeof(dm_pdev_t));
return NULL;
}
getdisksize(dmp->pdev_vnode, &dmp->pdev_numsec, &dmp->pdev_secsize);
dmp->ref_cnt = 1;
snprintf(dmp->udev_name, sizeof(dmp->udev_name), "%d:%d",
major(dmp->pdev_vnode->v_rdev), minor(dmp->pdev_vnode->v_rdev));
aprint_debug("%s: %s %s\n", __func__, dev_name, dmp->udev_name);
SLIST_INSERT_HEAD(&dm_pdev_list, dmp, next_pdev);
mutex_exit(&dm_pdev_mutex);
return dmp;
}
int
dm_pdev_init(void)
{
SLIST_INIT(&dm_pdev_list);
mutex_init(&dm_pdev_mutex, MUTEX_DEFAULT, IPL_NONE);
return 0;
}
static dm_pdev_t *
dm_pdev_alloc(const char *name)
{
dm_pdev_t *dmp;
dmp = kmem_zalloc(sizeof(*dmp), KM_SLEEP);
strlcpy(dmp->name, name, sizeof(dmp->name));
dmp->ref_cnt = 0;
dmp->pdev_vnode = NULL;
return dmp;
}
static int
dm_pdev_rem(dm_pdev_t *dmp)
{
KASSERT(dmp != NULL);
if (dmp->pdev_vnode != NULL) {
int error = vn_close(dmp->pdev_vnode, FREAD | FWRITE, FSCRED);
if (error != 0) {
kmem_free(dmp, sizeof(*dmp));
return error;
}
}
kmem_free(dmp, sizeof(*dmp));
return 0;
}
int
dm_pdev_destroy(void)
{
dm_pdev_t *dmp;
mutex_enter(&dm_pdev_mutex);
while ((dmp = SLIST_FIRST(&dm_pdev_list)) != NULL) {
SLIST_REMOVE(&dm_pdev_list, dmp, dm_pdev, next_pdev);
dm_pdev_rem(dmp);
}
KASSERT(SLIST_EMPTY(&dm_pdev_list));
mutex_exit(&dm_pdev_mutex);
mutex_destroy(&dm_pdev_mutex);
return 0;
}
int
dm_pdev_decr(dm_pdev_t *dmp)
{
KASSERT(dmp != NULL);
mutex_enter(&dm_pdev_mutex);
if (--dmp->ref_cnt == 0) {
SLIST_REMOVE(&dm_pdev_list, dmp, dm_pdev, next_pdev);
mutex_exit(&dm_pdev_mutex);
dm_pdev_rem(dmp);
return 0;
}
mutex_exit(&dm_pdev_mutex);
return 0;
}
#if 0
static int
dm_pdev_dump_list(void)
{
dm_pdev_t *dmp;
aprint_verbose("Dumping dm_pdev_list\n");
SLIST_FOREACH(dmp, &dm_pdev_list, next_pdev) {
aprint_verbose("dm_pdev_name %s ref_cnt %d list_rf_cnt %d\n",
dmp->name, dmp->ref_cnt, dmp->list_ref_cnt);
}
return 0;
}
#endif