#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: subr_devsw.c,v 1.55 2026/01/04 03:15:58 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_dtrace.h"
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/atomic.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <sys/localcount.h>
#include <sys/poll.h>
#include <sys/pserialize.h>
#include <sys/reboot.h>
#include <sys/sdt.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/xcall.h>
#ifdef DEVSW_DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif
#define MAXDEVSW 512
#define BDEVSW_SIZE (sizeof(struct bdevsw *))
#define CDEVSW_SIZE (sizeof(struct cdevsw *))
#define DEVSWCONV_SIZE (sizeof(struct devsw_conv))
struct devswref {
struct localcount *dr_lc;
};
extern const struct bdevsw **bdevsw, *bdevsw0[];
extern const struct cdevsw **cdevsw, *cdevsw0[];
extern struct devsw_conv *devsw_conv, devsw_conv0[];
extern const int sys_bdevsws, sys_cdevsws;
extern int max_bdevsws, max_cdevsws, max_devsw_convs;
static struct devswref *cdevswref;
static struct devswref *bdevswref;
static kcondvar_t devsw_cv;
static int bdevsw_attach(const struct bdevsw *, devmajor_t *);
static int cdevsw_attach(const struct cdevsw *, devmajor_t *);
static void devsw_detach_locked(const struct bdevsw *, const struct cdevsw *);
kmutex_t device_lock;
void (*biodone_vfs)(buf_t *) = (void *)nullop;
SDT_PROBE_DEFINE6(sdt, bdev, open, acquire,
"struct bdevsw *",
"dev_t",
"int",
"int",
"int",
"device_t");
SDT_PROBE_DEFINE4(sdt, bdev, open, entry,
"struct bdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, bdev, open, return,
"struct bdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE6(sdt, bdev, open, release,
"struct bdevsw *",
"dev_t",
"int",
"int",
"int",
"device_t");
SDT_PROBE_DEFINE4(sdt, bdev, cancel, entry,
"struct bdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, bdev, cancel, return,
"struct bdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, bdev, close, entry,
"struct bdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, bdev, close, return,
"struct bdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE3(sdt, bdev, strategy, entry,
"struct bdevsw *",
"dev_t",
"struct buf *");
SDT_PROBE_DEFINE3(sdt, bdev, strategy, return,
"struct bdevsw *",
"dev_t",
"struct buf *");
SDT_PROBE_DEFINE5(sdt, bdev, ioctl, entry,
"struct bdevsw *",
"dev_t",
"unsigned long",
"void *",
"int");
SDT_PROBE_DEFINE6(sdt, bdev, ioctl, return,
"struct bdevsw *",
"dev_t",
"unsigned long",
"void *",
"int",
"int");
SDT_PROBE_DEFINE2(sdt, bdev, psize, entry,
"struct bdevsw *",
"dev_t");
SDT_PROBE_DEFINE3(sdt, bdev, psize, return,
"struct bdevsw *",
"dev_t",
"int");
SDT_PROBE_DEFINE4(sdt, bdev, discard, entry,
"struct bdevsw *",
"dev_t",
"off_t",
"off_t");
SDT_PROBE_DEFINE5(sdt, bdev, discard, return,
"struct bdevsw *",
"dev_t",
"off_t",
"off_t",
"int");
SDT_PROBE_DEFINE6(sdt, cdev, open, acquire,
"struct cdevsw *",
"dev_t",
"int",
"int",
"int",
"device_t");
SDT_PROBE_DEFINE4(sdt, cdev, open, entry,
"struct cdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, open, return,
"struct cdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE6(sdt, cdev, open, release,
"struct cdevsw *",
"dev_t",
"int",
"int",
"int",
"device_t");
SDT_PROBE_DEFINE4(sdt, cdev, cancel, entry,
"struct cdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, cancel, return,
"struct cdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, close, entry,
"struct cdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, close, return,
"struct cdevsw *",
"dev_t",
"int",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, read, entry,
"struct cdevsw *",
"dev_t",
"struct uio *",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, read, return,
"struct cdevsw *",
"dev_t",
"struct uio *",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, write, entry,
"struct cdevsw *",
"dev_t",
"struct uio *",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, write, return,
"struct cdevsw *",
"dev_t",
"struct uio *",
"int",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, ioctl, entry,
"struct cdevsw *",
"dev_t",
"unsigned long",
"void *",
"int");
SDT_PROBE_DEFINE6(sdt, cdev, ioctl, return,
"struct cdevsw *",
"dev_t",
"unsigned long",
"void *",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, stop, entry,
"struct cdevsw *",
"dev_t",
"struct tty *",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, stop, return,
"struct cdevsw *",
"dev_t",
"struct tty *",
"int");
SDT_PROBE_DEFINE3(sdt, cdev, poll, entry,
"struct cdevsw *",
"dev_t",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, poll, return,
"struct cdevsw *",
"dev_t",
"int",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, mmap, entry,
"struct cdevsw *",
"dev_t",
"off_t",
"int");
SDT_PROBE_DEFINE5(sdt, cdev, mmap, return,
"struct cdevsw *",
"dev_t",
"off_t",
"int",
"paddr_t");
SDT_PROBE_DEFINE3(sdt, cdev, kqfilter, entry,
"struct cdevsw *",
"dev_t",
"struct knote *");
SDT_PROBE_DEFINE4(sdt, cdev, kqfilter, return,
"struct cdevsw *",
"dev_t",
"struct knote *",
"int");
SDT_PROBE_DEFINE4(sdt, cdev, discard, entry,
"struct cdevsw *",
"dev_t",
"off_t",
"off_t");
SDT_PROBE_DEFINE5(sdt, cdev, discard, return,
"struct cdevsw *",
"dev_t",
"off_t",
"off_t",
"int");
void
devsw_init(void)
{
KASSERT(sys_bdevsws < MAXDEVSW - 1);
KASSERT(sys_cdevsws < MAXDEVSW - 1);
mutex_init(&device_lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&devsw_cv, "devsw");
}
int
devsw_attach(const char *devname,
const struct bdevsw *bdev, devmajor_t *bmajor,
const struct cdevsw *cdev, devmajor_t *cmajor)
{
struct devsw_conv *conv;
char *name;
int error, i;
if (devname == NULL || cdev == NULL)
return SET_ERROR(EINVAL);
mutex_enter(&device_lock);
for (i = 0; i < max_devsw_convs; i++) {
conv = &devsw_conv[i];
if (conv->d_name == NULL || strcmp(devname, conv->d_name) != 0)
continue;
if ((bdev != NULL) && (*bmajor < 0))
*bmajor = conv->d_bmajor;
if (*cmajor < 0)
*cmajor = conv->d_cmajor;
if (*bmajor != conv->d_bmajor || *cmajor != conv->d_cmajor) {
error = SET_ERROR(EINVAL);
goto out;
}
if ((*bmajor >= 0 && bdev == NULL) || *cmajor < 0) {
error = SET_ERROR(EINVAL);
goto out;
}
if ((*bmajor >= 0 && bdevsw[*bmajor] != NULL) ||
cdevsw[*cmajor] != NULL) {
error = SET_ERROR(EEXIST);
goto out;
}
break;
}
error = bdevsw_attach(bdev, bmajor);
if (error != 0)
goto out;
error = cdevsw_attach(cdev, cmajor);
if (error != 0) {
devsw_detach_locked(bdev, NULL);
goto out;
}
if (i < max_devsw_convs) {
error = 0;
goto out;
}
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_name == NULL)
break;
}
if (i == max_devsw_convs) {
struct devsw_conv *newptr;
int old_convs, new_convs;
old_convs = max_devsw_convs;
new_convs = old_convs + 1;
newptr = kmem_zalloc(new_convs * DEVSWCONV_SIZE, KM_NOSLEEP);
if (newptr == NULL) {
devsw_detach_locked(bdev, cdev);
error = SET_ERROR(ENOMEM);
goto out;
}
newptr[old_convs].d_name = NULL;
newptr[old_convs].d_bmajor = -1;
newptr[old_convs].d_cmajor = -1;
memcpy(newptr, devsw_conv, old_convs * DEVSWCONV_SIZE);
if (devsw_conv != devsw_conv0)
kmem_free(devsw_conv, old_convs * DEVSWCONV_SIZE);
devsw_conv = newptr;
max_devsw_convs = new_convs;
}
name = kmem_strdupsize(devname, NULL, KM_NOSLEEP);
if (name == NULL) {
devsw_detach_locked(bdev, cdev);
error = SET_ERROR(ENOMEM);
goto out;
}
devsw_conv[i].d_name = name;
devsw_conv[i].d_bmajor = *bmajor;
devsw_conv[i].d_cmajor = *cmajor;
error = 0;
out:
mutex_exit(&device_lock);
return error;
}
static int
bdevsw_attach(const struct bdevsw *devsw, devmajor_t *devmajor)
{
const struct bdevsw **newbdevsw = NULL;
struct devswref *newbdevswref = NULL;
struct localcount *lc;
devmajor_t bmajor;
int i;
KASSERT(mutex_owned(&device_lock));
if (devsw == NULL)
return 0;
if (*devmajor < 0) {
for (bmajor = sys_bdevsws; bmajor < max_bdevsws; bmajor++) {
if (bdevsw[bmajor] != NULL)
continue;
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_bmajor == bmajor)
break;
}
if (i != max_devsw_convs)
continue;
break;
}
*devmajor = bmajor;
}
if (*devmajor >= MAXDEVSW) {
printf("%s: block majors exhausted\n", __func__);
return SET_ERROR(ENOMEM);
}
if (bdevswref == NULL) {
newbdevswref = kmem_zalloc(MAXDEVSW * sizeof(newbdevswref[0]),
KM_NOSLEEP);
if (newbdevswref == NULL)
return SET_ERROR(ENOMEM);
atomic_store_release(&bdevswref, newbdevswref);
}
if (*devmajor >= max_bdevsws) {
KASSERT(bdevsw == bdevsw0);
newbdevsw = kmem_zalloc(MAXDEVSW * sizeof(newbdevsw[0]),
KM_NOSLEEP);
if (newbdevsw == NULL)
return SET_ERROR(ENOMEM);
memcpy(newbdevsw, bdevsw, max_bdevsws * sizeof(bdevsw[0]));
atomic_store_release(&bdevsw, newbdevsw);
atomic_store_release(&max_bdevsws, MAXDEVSW);
}
if (bdevsw[*devmajor] != NULL)
return SET_ERROR(EEXIST);
KASSERT(bdevswref[*devmajor].dr_lc == NULL);
lc = kmem_zalloc(sizeof(*lc), KM_SLEEP);
localcount_init(lc);
bdevswref[*devmajor].dr_lc = lc;
atomic_store_release(&bdevsw[*devmajor], devsw);
return 0;
}
static int
cdevsw_attach(const struct cdevsw *devsw, devmajor_t *devmajor)
{
const struct cdevsw **newcdevsw = NULL;
struct devswref *newcdevswref = NULL;
struct localcount *lc;
devmajor_t cmajor;
int i;
KASSERT(mutex_owned(&device_lock));
if (*devmajor < 0) {
for (cmajor = sys_cdevsws; cmajor < max_cdevsws; cmajor++) {
if (cdevsw[cmajor] != NULL)
continue;
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_cmajor == cmajor)
break;
}
if (i != max_devsw_convs)
continue;
break;
}
*devmajor = cmajor;
}
if (*devmajor >= MAXDEVSW) {
printf("%s: character majors exhausted\n", __func__);
return SET_ERROR(ENOMEM);
}
if (cdevswref == NULL) {
newcdevswref = kmem_zalloc(MAXDEVSW * sizeof(newcdevswref[0]),
KM_NOSLEEP);
if (newcdevswref == NULL)
return SET_ERROR(ENOMEM);
atomic_store_release(&cdevswref, newcdevswref);
}
if (*devmajor >= max_cdevsws) {
KASSERT(cdevsw == cdevsw0);
newcdevsw = kmem_zalloc(MAXDEVSW * sizeof(newcdevsw[0]),
KM_NOSLEEP);
if (newcdevsw == NULL)
return SET_ERROR(ENOMEM);
memcpy(newcdevsw, cdevsw, max_cdevsws * sizeof(cdevsw[0]));
atomic_store_release(&cdevsw, newcdevsw);
atomic_store_release(&max_cdevsws, MAXDEVSW);
}
if (cdevsw[*devmajor] != NULL)
return SET_ERROR(EEXIST);
KASSERT(cdevswref[*devmajor].dr_lc == NULL);
lc = kmem_zalloc(sizeof(*lc), KM_SLEEP);
localcount_init(lc);
cdevswref[*devmajor].dr_lc = lc;
atomic_store_release(&cdevsw[*devmajor], devsw);
return 0;
}
static void
devsw_detach_locked(const struct bdevsw *bdev, const struct cdevsw *cdev)
{
int bi = -1, ci = -1, di;
struct cfdriver *cd;
device_t dv;
KASSERT(mutex_owned(&device_lock));
if (cdev != NULL && (cd = cdev->d_cfdriver) != NULL) {
for (di = 0; di < cd->cd_ndevs; di++) {
KASSERTMSG((dv = cd->cd_devs[di]) == NULL,
"detaching character device driver %s"
" still has attached unit %s",
cd->cd_name, device_xname(dv));
}
}
if (bdev != NULL && (cd = bdev->d_cfdriver) != NULL) {
for (di = 0; di < cd->cd_ndevs; di++) {
KASSERTMSG((dv = cd->cd_devs[di]) == NULL,
"detaching block device driver %s"
" still has attached unit %s",
cd->cd_name, device_xname(dv));
}
}
if (bdev != NULL) {
for (bi = 0; bi < max_bdevsws; bi++) {
if (bdevsw[bi] != bdev)
continue;
atomic_store_relaxed(&bdevsw[bi], NULL);
break;
}
KASSERT(bi < max_bdevsws);
}
if (cdev != NULL) {
for (ci = 0; ci < max_cdevsws; ci++) {
if (cdevsw[ci] != cdev)
continue;
atomic_store_relaxed(&cdevsw[ci], NULL);
break;
}
KASSERT(ci < max_cdevsws);
}
if (bdev == NULL && cdev == NULL)
return;
xc_barrier(0);
if (bdev != NULL) {
localcount_drain(bdevswref[bi].dr_lc,
&devsw_cv, &device_lock);
localcount_fini(bdevswref[bi].dr_lc);
kmem_free(bdevswref[bi].dr_lc, sizeof(*bdevswref[bi].dr_lc));
bdevswref[bi].dr_lc = NULL;
}
if (cdev != NULL) {
localcount_drain(cdevswref[ci].dr_lc,
&devsw_cv, &device_lock);
localcount_fini(cdevswref[ci].dr_lc);
kmem_free(cdevswref[ci].dr_lc, sizeof(*cdevswref[ci].dr_lc));
cdevswref[ci].dr_lc = NULL;
}
}
void
devsw_detach(const struct bdevsw *bdev, const struct cdevsw *cdev)
{
mutex_enter(&device_lock);
devsw_detach_locked(bdev, cdev);
mutex_exit(&device_lock);
}
const struct bdevsw *
bdevsw_lookup(dev_t dev)
{
devmajor_t bmajor;
if (dev == NODEV)
return NULL;
bmajor = major(dev);
if (bmajor < 0 || bmajor >= atomic_load_relaxed(&max_bdevsws))
return NULL;
return atomic_load_consume(&bdevsw)[bmajor];
}
static const struct bdevsw *
bdevsw_lookup_acquire(dev_t dev, struct localcount **lcp)
{
devmajor_t bmajor;
const struct bdevsw *bdev = NULL, *const *curbdevsw;
struct devswref *curbdevswref;
int s;
if (dev == NODEV)
return NULL;
bmajor = major(dev);
if (bmajor < 0)
return NULL;
s = pserialize_read_enter();
if (bmajor >= sys_bdevsws &&
bmajor >= atomic_load_acquire(&max_bdevsws))
goto out;
curbdevsw = atomic_load_consume(&bdevsw);
if ((bdev = atomic_load_consume(&curbdevsw[bmajor])) == NULL)
goto out;
curbdevswref = atomic_load_consume(&bdevswref);
if (curbdevswref == NULL) {
*lcp = NULL;
} else if ((*lcp = curbdevswref[bmajor].dr_lc) != NULL) {
localcount_acquire(*lcp);
}
out:
pserialize_read_exit(s);
return bdev;
}
static void
bdevsw_release(const struct bdevsw *bdev, struct localcount *lc)
{
if (lc == NULL)
return;
localcount_release(lc, &devsw_cv, &device_lock);
}
const struct cdevsw *
cdevsw_lookup(dev_t dev)
{
devmajor_t cmajor;
if (dev == NODEV)
return NULL;
cmajor = major(dev);
if (cmajor < 0 || cmajor >= atomic_load_relaxed(&max_cdevsws))
return NULL;
return atomic_load_consume(&cdevsw)[cmajor];
}
static const struct cdevsw *
cdevsw_lookup_acquire(dev_t dev, struct localcount **lcp)
{
devmajor_t cmajor;
const struct cdevsw *cdev = NULL, *const *curcdevsw;
struct devswref *curcdevswref;
int s;
if (dev == NODEV)
return NULL;
cmajor = major(dev);
if (cmajor < 0)
return NULL;
s = pserialize_read_enter();
if (cmajor >= sys_cdevsws &&
cmajor >= atomic_load_acquire(&max_cdevsws))
goto out;
curcdevsw = atomic_load_consume(&cdevsw);
if ((cdev = atomic_load_consume(&curcdevsw[cmajor])) == NULL)
goto out;
curcdevswref = atomic_load_consume(&cdevswref);
if (curcdevswref == NULL) {
*lcp = NULL;
} else if ((*lcp = curcdevswref[cmajor].dr_lc) != NULL) {
localcount_acquire(*lcp);
}
out:
pserialize_read_exit(s);
return cdev;
}
static void
cdevsw_release(const struct cdevsw *cdev, struct localcount *lc)
{
if (lc == NULL)
return;
localcount_release(lc, &devsw_cv, &device_lock);
}
devmajor_t
bdevsw_lookup_major(const struct bdevsw *bdev)
{
const struct bdevsw *const *curbdevsw;
devmajor_t bmajor, bmax;
bmax = atomic_load_acquire(&max_bdevsws);
curbdevsw = atomic_load_consume(&bdevsw);
for (bmajor = 0; bmajor < bmax; bmajor++) {
if (atomic_load_relaxed(&curbdevsw[bmajor]) == bdev)
return bmajor;
}
return NODEVMAJOR;
}
devmajor_t
cdevsw_lookup_major(const struct cdevsw *cdev)
{
const struct cdevsw *const *curcdevsw;
devmajor_t cmajor, cmax;
cmax = atomic_load_acquire(&max_cdevsws);
curcdevsw = atomic_load_consume(&cdevsw);
for (cmajor = 0; cmajor < cmax; cmajor++) {
if (atomic_load_relaxed(&curcdevsw[cmajor]) == cdev)
return cmajor;
}
return NODEVMAJOR;
}
const char *
devsw_blk2name(devmajor_t bmajor)
{
const char *name;
devmajor_t cmajor;
int i;
name = NULL;
cmajor = -1;
mutex_enter(&device_lock);
if (bmajor < 0 || bmajor >= max_bdevsws || bdevsw[bmajor] == NULL) {
mutex_exit(&device_lock);
return NULL;
}
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_bmajor == bmajor) {
cmajor = devsw_conv[i].d_cmajor;
break;
}
}
if (cmajor >= 0 && cmajor < max_cdevsws && cdevsw[cmajor] != NULL)
name = devsw_conv[i].d_name;
mutex_exit(&device_lock);
return name;
}
const char *
cdevsw_getname(devmajor_t major)
{
const char *name;
int i;
name = NULL;
if (major < 0)
return NULL;
mutex_enter(&device_lock);
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_cmajor == major) {
name = devsw_conv[i].d_name;
break;
}
}
mutex_exit(&device_lock);
return name;
}
const char *
bdevsw_getname(devmajor_t major)
{
const char *name;
int i;
name = NULL;
if (major < 0)
return NULL;
mutex_enter(&device_lock);
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_bmajor == major) {
name = devsw_conv[i].d_name;
break;
}
}
mutex_exit(&device_lock);
return name;
}
devmajor_t
devsw_name2blk(const char *name, char *devname, size_t devnamelen)
{
struct devsw_conv *conv;
devmajor_t bmajor;
int i;
if (name == NULL)
return NODEVMAJOR;
mutex_enter(&device_lock);
for (i = 0; i < max_devsw_convs; i++) {
size_t len;
conv = &devsw_conv[i];
if (conv->d_name == NULL)
continue;
len = strlen(conv->d_name);
if (strncmp(conv->d_name, name, len) != 0)
continue;
if (name[len] != '\0' && !isdigit((unsigned char)name[len]))
continue;
bmajor = conv->d_bmajor;
if (bmajor < 0 || bmajor >= max_bdevsws ||
bdevsw[bmajor] == NULL)
break;
if (devname != NULL) {
#ifdef DEVSW_DEBUG
if (strlen(conv->d_name) >= devnamelen)
printf("%s: too short buffer\n", __func__);
#endif
strncpy(devname, conv->d_name, devnamelen);
devname[devnamelen - 1] = '\0';
}
mutex_exit(&device_lock);
return bmajor;
}
mutex_exit(&device_lock);
return NODEVMAJOR;
}
devmajor_t
devsw_name2chr(const char *name, char *devname, size_t devnamelen)
{
struct devsw_conv *conv;
devmajor_t cmajor;
int i;
if (name == NULL)
return NODEVMAJOR;
mutex_enter(&device_lock);
for (i = 0; i < max_devsw_convs; i++) {
size_t len;
conv = &devsw_conv[i];
if (conv->d_name == NULL)
continue;
len = strlen(conv->d_name);
if (strncmp(conv->d_name, name, len) != 0)
continue;
if (name[len] != '\0' && !isdigit((unsigned char)name[len]))
continue;
cmajor = conv->d_cmajor;
if (cmajor < 0 || cmajor >= max_cdevsws ||
cdevsw[cmajor] == NULL)
break;
if (devname != NULL) {
#ifdef DEVSW_DEBUG
if (strlen(conv->d_name) >= devnamelen)
printf("%s: too short buffer", __func__);
#endif
strncpy(devname, conv->d_name, devnamelen);
devname[devnamelen - 1] = '\0';
}
mutex_exit(&device_lock);
return cmajor;
}
mutex_exit(&device_lock);
return NODEVMAJOR;
}
dev_t
devsw_chr2blk(dev_t cdev)
{
devmajor_t bmajor, cmajor;
int i;
dev_t rv;
cmajor = major(cdev);
bmajor = NODEVMAJOR;
rv = NODEV;
mutex_enter(&device_lock);
if (cmajor < 0 || cmajor >= max_cdevsws || cdevsw[cmajor] == NULL) {
mutex_exit(&device_lock);
return NODEV;
}
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_cmajor == cmajor) {
bmajor = devsw_conv[i].d_bmajor;
break;
}
}
if (bmajor >= 0 && bmajor < max_bdevsws && bdevsw[bmajor] != NULL)
rv = makedev(bmajor, minor(cdev));
mutex_exit(&device_lock);
return rv;
}
dev_t
devsw_blk2chr(dev_t bdev)
{
devmajor_t bmajor, cmajor;
int i;
dev_t rv;
bmajor = major(bdev);
cmajor = NODEVMAJOR;
rv = NODEV;
mutex_enter(&device_lock);
if (bmajor < 0 || bmajor >= max_bdevsws || bdevsw[bmajor] == NULL) {
mutex_exit(&device_lock);
return NODEV;
}
for (i = 0; i < max_devsw_convs; i++) {
if (devsw_conv[i].d_bmajor == bmajor) {
cmajor = devsw_conv[i].d_cmajor;
break;
}
}
if (cmajor >= 0 && cmajor < max_cdevsws && cdevsw[cmajor] != NULL)
rv = makedev(cmajor, minor(bdev));
mutex_exit(&device_lock);
return rv;
}
#define DEV_LOCK(d) \
if ((mpflag = (d->d_flag & D_MPSAFE)) == 0) { \
KERNEL_LOCK(1, NULL); \
}
#define DEV_UNLOCK(d) \
if (mpflag == 0) { \
KERNEL_UNLOCK_ONE(NULL); \
}
int
bdev_open(dev_t dev, int flag, int devtype, lwp_t *l)
{
const struct bdevsw *d;
struct localcount *lc;
device_t dv = NULL;
int unit = -1, rv, mpflag;
d = bdevsw_lookup_acquire(dev, &lc);
if (d == NULL)
return SET_ERROR(ENXIO);
if (d->d_devtounit) {
if ((unit = (*d->d_devtounit)(dev)) == -1) {
rv = SET_ERROR(ENXIO);
goto out;
}
if ((dv = device_lookup_acquire(d->d_cfdriver, unit)) ==
NULL) {
rv = SET_ERROR(ENXIO);
goto out;
}
SDT_PROBE6(sdt, bdev, open, acquire,
d, dev, flag, devtype, unit, dv);
}
DEV_LOCK(d);
SDT_PROBE4(sdt, bdev, open, entry, d, dev, flag, devtype);
rv = (*d->d_open)(dev, flag, devtype, l);
SDT_PROBE5(sdt, bdev, open, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
if (d->d_devtounit) {
SDT_PROBE6(sdt, bdev, open, release,
d, dev, flag, devtype, unit, dv);
device_release(dv);
}
out: bdevsw_release(d, lc);
return rv;
}
int
bdev_cancel(dev_t dev, int flag, int devtype, struct lwp *l)
{
const struct bdevsw *d;
int rv, mpflag;
if ((d = bdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
if (d->d_cancel == NULL)
return SET_ERROR(ENODEV);
DEV_LOCK(d);
SDT_PROBE4(sdt, bdev, cancel, entry, d, dev, flag, devtype);
rv = (*d->d_cancel)(dev, flag, devtype, l);
SDT_PROBE5(sdt, bdev, cancel, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
return rv;
}
int
bdev_close(dev_t dev, int flag, int devtype, lwp_t *l)
{
const struct bdevsw *d;
int rv, mpflag;
if ((d = bdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, bdev, close, entry, d, dev, flag, devtype);
rv = (*d->d_close)(dev, flag, devtype, l);
SDT_PROBE5(sdt, bdev, close, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
return rv;
}
SDT_PROVIDER_DECLARE(io);
SDT_PROBE_DEFINE1(io, kernel, , start, "struct buf *");
void
bdev_strategy(struct buf *bp)
{
const struct bdevsw *d;
int mpflag;
SDT_PROBE1(io, kernel, , start, bp);
if ((d = bdevsw_lookup(bp->b_dev)) == NULL) {
bp->b_error = SET_ERROR(ENXIO);
bp->b_resid = bp->b_bcount;
biodone_vfs(bp);
return;
}
DEV_LOCK(d);
SDT_PROBE3(sdt, bdev, strategy, entry, d, bp->b_dev, bp);
(*d->d_strategy)(bp);
SDT_PROBE3(sdt, bdev, strategy, return, d, bp->b_dev, bp);
DEV_UNLOCK(d);
}
int
bdev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
{
const struct bdevsw *d;
int rv, mpflag;
if ((d = bdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE5(sdt, bdev, ioctl, entry, d, dev, cmd, data, flag);
rv = (*d->d_ioctl)(dev, cmd, data, flag, l);
SDT_PROBE6(sdt, bdev, ioctl, return, d, dev, cmd, data, flag, rv);
DEV_UNLOCK(d);
return rv;
}
int
bdev_dump(dev_t dev, daddr_t addr, void *data, size_t sz)
{
const struct bdevsw *d;
int rv;
if ((d = bdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
rv = (*d->d_dump)(dev, addr, data, sz);
return rv;
}
int
bdev_flags(dev_t dev)
{
const struct bdevsw *d;
if ((d = bdevsw_lookup(dev)) == NULL)
return 0;
return d->d_flag & ~D_TYPEMASK;
}
int
bdev_type(dev_t dev)
{
const struct bdevsw *d;
if ((d = bdevsw_lookup(dev)) == NULL)
return D_OTHER;
return d->d_flag & D_TYPEMASK;
}
int
bdev_size(dev_t dev)
{
const struct bdevsw *d;
int rv, mpflag = 0;
if ((d = bdevsw_lookup(dev)) == NULL ||
d->d_psize == NULL)
return -1;
if ((boothowto & RB_DUMP) == 0)
DEV_LOCK(d);
SDT_PROBE2(sdt, bdev, psize, entry, d, dev);
rv = (*d->d_psize)(dev);
SDT_PROBE3(sdt, bdev, psize, return, d, dev, rv);
if ((boothowto & RB_DUMP) == 0)
DEV_UNLOCK(d);
return rv;
}
int
bdev_discard(dev_t dev, off_t pos, off_t len)
{
const struct bdevsw *d;
int rv, mpflag;
if ((d = bdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, bdev, discard, entry, d, dev, pos, len);
rv = (*d->d_discard)(dev, pos, len);
SDT_PROBE5(sdt, bdev, discard, return, d, dev, pos, len, rv);
DEV_UNLOCK(d);
return rv;
}
void
bdev_detached(dev_t dev)
{
const struct bdevsw *d;
device_t dv;
int unit;
if ((d = bdevsw_lookup(dev)) == NULL)
return;
if (d->d_devtounit == NULL)
return;
if ((unit = (*d->d_devtounit)(dev)) == -1)
return;
if ((dv = device_lookup(d->d_cfdriver, unit)) == NULL)
return;
config_detach_commit(dv);
}
int
cdev_open(dev_t dev, int flag, int devtype, lwp_t *l)
{
const struct cdevsw *d;
struct localcount *lc;
device_t dv = NULL;
int unit = -1, rv, mpflag;
d = cdevsw_lookup_acquire(dev, &lc);
if (d == NULL)
return SET_ERROR(ENXIO);
if (d->d_devtounit) {
if ((unit = (*d->d_devtounit)(dev)) == -1) {
rv = SET_ERROR(ENXIO);
goto out;
}
if ((dv = device_lookup_acquire(d->d_cfdriver, unit)) ==
NULL) {
rv = SET_ERROR(ENXIO);
goto out;
}
SDT_PROBE6(sdt, cdev, open, acquire,
d, dev, flag, devtype, unit, dv);
}
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, open, entry, d, dev, flag, devtype);
rv = (*d->d_open)(dev, flag, devtype, l);
SDT_PROBE5(sdt, cdev, open, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
if (d->d_devtounit) {
SDT_PROBE6(sdt, cdev, open, release,
d, dev, flag, devtype, unit, dv);
device_release(dv);
}
out: cdevsw_release(d, lc);
return rv;
}
int
cdev_cancel(dev_t dev, int flag, int devtype, struct lwp *l)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
if (d->d_cancel == NULL)
return SET_ERROR(ENODEV);
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, cancel, entry, d, dev, flag, devtype);
rv = (*d->d_cancel)(dev, flag, devtype, l);
SDT_PROBE5(sdt, cdev, cancel, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_close(dev_t dev, int flag, int devtype, lwp_t *l)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, close, entry, d, dev, flag, devtype);
rv = (*d->d_close)(dev, flag, devtype, l);
SDT_PROBE5(sdt, cdev, close, return, d, dev, flag, devtype, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_read(dev_t dev, struct uio *uio, int flag)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, read, entry, d, dev, uio, flag);
rv = (*d->d_read)(dev, uio, flag);
SDT_PROBE5(sdt, cdev, read, return, d, dev, uio, flag, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_write(dev_t dev, struct uio *uio, int flag)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, write, entry, d, dev, uio, flag);
rv = (*d->d_write)(dev, uio, flag);
SDT_PROBE5(sdt, cdev, write, return, d, dev, uio, flag, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE5(sdt, cdev, ioctl, entry, d, dev, cmd, data, flag);
rv = (*d->d_ioctl)(dev, cmd, data, flag, l);
SDT_PROBE6(sdt, cdev, ioctl, return, d, dev, cmd, data, flag, rv);
DEV_UNLOCK(d);
return rv;
}
void
cdev_stop(struct tty *tp, int flag)
{
const struct cdevsw *d;
int mpflag;
if ((d = cdevsw_lookup(tp->t_dev)) == NULL)
return;
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, stop, entry, d, tp->t_dev, tp, flag);
(*d->d_stop)(tp, flag);
SDT_PROBE4(sdt, cdev, stop, return, d, tp->t_dev, tp, flag);
DEV_UNLOCK(d);
}
struct tty *
cdev_tty(dev_t dev)
{
const struct cdevsw *d;
if ((d = cdevsw_lookup(dev)) == NULL)
return NULL;
if (d->d_tty == NULL)
return NULL;
return (*d->d_tty)(dev);
}
int
cdev_poll(dev_t dev, int flag, lwp_t *l)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return POLLERR;
DEV_LOCK(d);
SDT_PROBE3(sdt, cdev, poll, entry, d, dev, flag);
rv = (*d->d_poll)(dev, flag, l);
SDT_PROBE4(sdt, cdev, poll, return, d, dev, flag, rv);
DEV_UNLOCK(d);
return rv;
}
paddr_t
cdev_mmap(dev_t dev, off_t off, int flag)
{
const struct cdevsw *d;
paddr_t rv;
int mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return (paddr_t)-1LL;
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, mmap, entry, d, dev, off, flag);
rv = (*d->d_mmap)(dev, off, flag);
SDT_PROBE5(sdt, cdev, mmap, return, d, dev, off, flag, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_kqfilter(dev_t dev, struct knote *kn)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE3(sdt, cdev, kqfilter, entry, d, dev, kn);
rv = (*d->d_kqfilter)(dev, kn);
SDT_PROBE4(sdt, cdev, kqfilter, return, d, dev, kn, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_discard(dev_t dev, off_t pos, off_t len)
{
const struct cdevsw *d;
int rv, mpflag;
if ((d = cdevsw_lookup(dev)) == NULL)
return SET_ERROR(ENXIO);
DEV_LOCK(d);
SDT_PROBE4(sdt, cdev, discard, entry, d, dev, pos, len);
rv = (*d->d_discard)(dev, pos, len);
SDT_PROBE5(sdt, cdev, discard, return, d, dev, pos, len, rv);
DEV_UNLOCK(d);
return rv;
}
int
cdev_flags(dev_t dev)
{
const struct cdevsw *d;
if ((d = cdevsw_lookup(dev)) == NULL)
return 0;
return d->d_flag & ~D_TYPEMASK;
}
int
cdev_type(dev_t dev)
{
const struct cdevsw *d;
if ((d = cdevsw_lookup(dev)) == NULL)
return D_OTHER;
return d->d_flag & D_TYPEMASK;
}
void
cdev_detached(dev_t dev)
{
const struct cdevsw *d;
device_t dv;
int unit;
if ((d = cdevsw_lookup(dev)) == NULL)
return;
if (d->d_devtounit == NULL)
return;
if ((unit = (*d->d_devtounit)(dev)) == -1)
return;
if ((dv = device_lookup(d->d_cfdriver, unit)) == NULL)
return;
config_detach_commit(dv);
}
paddr_t
nommap(dev_t dev, off_t off, int prot)
{
return (paddr_t)-1;
}
int
dev_minor_unit(dev_t dev)
{
return minor(dev);
}