summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2000-09-06 11:26:43 +0000
committerphk <phk@FreeBSD.org>2000-09-06 11:26:43 +0000
commitc9cb5c289d5aaaea9a1cb017bd8b2f24c3483f8e (patch)
treef3e131458b74087067e30359371eb0ee0414890b /sys
parent6c07bccbf798848cb394146e35ef94c64121accd (diff)
downloadFreeBSD-src-c9cb5c289d5aaaea9a1cb017bd8b2f24c3483f8e.zip
FreeBSD-src-c9cb5c289d5aaaea9a1cb017bd8b2f24c3483f8e.tar.gz
Add refcounts to the "global" DEVFS inode slots, this allows us
to recycle inodes after a destroy_dev() but not until all mounts have picked up the change. Add support for an overflow table for DEVFS inodes. The static table defaults to 1024 inodes, if that fills, an overflow table of 32k inodes is allocated. Both numbers can be changed at compile time, the size of the overflow table also with the sysctl vfs.devfs.noverflow. Use atomic instructions to barrier between make_dev()/destroy_dev() and the mounts. Add lockmgr() locking of directories for operations accessing or modifying the directory TAILQs. Various nitpicking here and there.
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/options4
-rw-r--r--sys/fs/devfs/devfs.h63
-rw-r--r--sys/fs/devfs/devfs_devs.c234
-rw-r--r--sys/fs/devfs/devfs_vfsops.c8
-rw-r--r--sys/fs/devfs/devfs_vnops.c60
5 files changed, 298 insertions, 71 deletions
diff --git a/sys/conf/options b/sys/conf/options
index 7a0dd1e..ddd04a3 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -459,3 +459,7 @@ ACPI_DEBUG opt_acpi.h
AML_DEBUG opt_acpi.h
ACPI_NO_ENABLE_ON_BOOT opt_acpi.h
ACPI_NO_OSDFUNC_INLINE opt_acpi.h
+
+# options for DEVFS, see sys/fs/devfs/devfs.h
+NDEVFSINO opt_devfs.h
+NDEVFSOVERFLOW opt_devfs.h
diff --git a/sys/fs/devfs/devfs.h b/sys/fs/devfs/devfs.h
index b00a913..12c4e65 100644
--- a/sys/fs/devfs/devfs.h
+++ b/sys/fs/devfs/devfs.h
@@ -34,11 +34,31 @@
* $FreeBSD$
*/
-#ifdef _KERNEL
+#ifndef _FS_DEVFS_DEVFS_H_
+#define _FS_DEVFS_DEVFS_H_
-#ifdef DEVFS_INTERN
+#ifdef _KERNEL /* No userland stuff in here... */
-#define NDEVINO 1024
+/*
+ * These are default sizes for the DEVFS inode table and the overflow
+ * table. If the default table overflows we allocate the overflow
+ * table, the size of which can also be set with a sysctl. If the
+ * overflow table fills you're toast.
+ */
+#ifndef NDEVFSINO
+#define NDEVFSINO 1024
+#endif
+
+#ifndef NDEVFSOVERFLOW
+#define NDEVFSOVERFLOW 32768
+#endif
+
+/*
+ * This is the first "per mount" inode, these are used for directories
+ * and symlinks and the like. Must be larger than the number of "true"
+ * device nodes and symlinks. It is.
+ */
+#define DEVFSINOMOUNT 0x2000000
MALLOC_DECLARE(M_DEVFS);
@@ -63,37 +83,36 @@ struct devfs_dirent {
char * de_symlink;
};
-struct devfs_node {
- struct kern_target *kf_kt;
-};
-
struct devfs_mount {
struct vnode *dm_root; /* Root node */
struct devfs_dirent *dm_rootdir;
struct devfs_dirent *dm_basedir;
unsigned dm_generation;
- struct devfs_dirent *dm_dirent[NDEVINO];
-#define DE_DELETED ((struct devfs_dirent *)&devfs_inot[0])
+ struct devfs_dirent *dm_dirent[NDEVFSINO];
+ struct devfs_dirent **dm_overflow;
int dm_inode;
+ struct lock dm_lock;
};
-
-extern dev_t devfs_inot[NDEVINO];
-extern int devfs_nino;
-extern unsigned devfs_generation;
-
+/*
+ * This is what we fill in dm_dirent[N] for a deleted entry.
+ */
+#define DE_DELETED ((struct devfs_dirent *)sizeof(struct devfs_dirent))
#define VFSTODEVFS(mp) ((struct devfs_mount *)((mp)->mnt_data))
extern vop_t **devfs_vnodeop_p;
extern vop_t **devfs_specop_p;
-int devfs_allocv __P((struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p));
-struct devfs_dirent * devfs_find __P((struct devfs_dirent *dd, const char *name, int namelen));
-int devfs_populate __P((struct devfs_mount *dm));
-struct devfs_dirent * devfs_newdirent __P((char *name, int namelen));
-void devfs_purge __P((struct devfs_dirent *dd));
-struct devfs_dirent * devfs_vmkdir __P((char *name, int namelen,
- struct devfs_dirent *dotdot));
-#endif /* DEVFS_INTERN */
+int devfs_allocv (struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p);
+void devfs_attemptoverflow(int insist);
+struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
+dev_t *devfs_itod (int inode);
+struct devfs_dirent **devfs_itode (struct devfs_mount *dm, int inode);
+int devfs_populate (struct devfs_mount *dm);
+struct devfs_dirent *devfs_newdirent (char *name, int namelen);
+void devfs_purge (struct devfs_dirent *dd);
+struct devfs_dirent *devfs_vmkdir (char *name, int namelen, struct devfs_dirent *dotdot);
+
#endif /* _KERNEL */
+#endif /* _FS_DEVFS_DEVFS_H_ */
diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c
index 41a48c7..49854a0 100644
--- a/sys/fs/devfs/devfs_devs.c
+++ b/sys/fs/devfs/devfs_devs.c
@@ -29,18 +29,149 @@
* $FreeBSD$
*/
+#include "opt_devfs.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dirent.h>
#include <sys/conf.h>
#include <sys/vnode.h>
+#include <sys/proc.h>
#include <sys/malloc.h>
-#include <sys/eventhandler.h>
-#include <sys/ctype.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+
+#include <machine/atomic.h>
-#define DEVFS_INTERN
#include <fs/devfs/devfs.h>
+static dev_t devfs_inot[NDEVFSINO];
+static dev_t *devfs_overflow;
+static int devfs_ref[NDEVFSINO];
+static int *devfs_refoverflow;
+static int devfs_nextino = 3;
+static int devfs_numino;
+static int devfs_topino;
+static int devfs_noverflowwant = NDEVFSOVERFLOW;
+static int devfs_noverflow;
+static unsigned devfs_generation;
+
+SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
+SYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
+ &devfs_noverflowwant, 0, "Size of DEVFS overflow table");
+SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
+ &devfs_generation, 0, "DEVFS generation number");
+SYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
+ &devfs_numino, 0, "DEVFS inodes");
+SYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
+ &devfs_topino, 0, "DEVFS highest inode#");
+
+static int *
+devfs_itor(int inode)
+{
+ if (inode < NDEVFSINO)
+ return (&devfs_ref[inode]);
+ else if (inode < NDEVFSINO + devfs_noverflow)
+ return (&devfs_refoverflow[inode - NDEVFSINO]);
+ else
+ panic ("YRK!");
+}
+
+static void
+devfs_dropref(int inode)
+{
+ int *ip;
+
+ ip = devfs_itor(inode);
+ atomic_add_int(ip, -1);
+}
+
+static int
+devfs_getref(int inode)
+{
+ int *ip, i, j;
+ dev_t *dp;
+
+ ip = devfs_itor(inode);
+ dp = devfs_itod(inode);
+ for (;;) {
+ i = *ip;
+ j = i + 1;
+ if (!atomic_cmpset_int(ip, i, j))
+ continue;
+ if (*dp != NULL)
+ return (1);
+ atomic_add_int(ip, -1);
+ return(0);
+ }
+}
+
+struct devfs_dirent **
+devfs_itode (struct devfs_mount *dm, int inode)
+{
+
+ if (inode < NDEVFSINO)
+ return (&dm->dm_dirent[inode]);
+ if (devfs_overflow == NULL)
+ return (NULL);
+ if (inode < NDEVFSINO + devfs_noverflow)
+ return (&dm->dm_overflow[inode - NDEVFSINO]);
+ return (NULL);
+}
+
+dev_t *
+devfs_itod (int inode)
+{
+
+ if (inode < NDEVFSINO)
+ return (&devfs_inot[inode]);
+ if (devfs_overflow == NULL)
+ return (NULL);
+ if (inode < NDEVFSINO + devfs_noverflow)
+ return (&devfs_overflow[inode - NDEVFSINO]);
+ return (NULL);
+}
+
+void
+devfs_attemptoverflow(int insist)
+{
+ dev_t **ot;
+ int *or;
+ int n, nb;
+
+ /* Check if somebody beat us to it */
+ if (devfs_overflow != NULL)
+ return;
+ ot = NULL;
+ or = NULL;
+ n = devfs_noverflowwant;
+ nb = sizeof (struct dev_t *) * n;
+ MALLOC(ot, dev_t **, nb, M_DEVFS, insist ? M_WAITOK : M_NOWAIT);
+ if (ot == NULL)
+ goto bail;
+ bzero(ot, nb);
+ nb = sizeof (int) * n;
+ MALLOC(or, int *, nb, M_DEVFS, insist ? M_WAITOK : M_NOWAIT);
+ if (or == NULL)
+ goto bail;
+ bzero(or, nb);
+ if (!atomic_cmpset_ptr(&devfs_overflow, NULL, ot))
+ goto bail;
+ devfs_refoverflow = or;
+ devfs_noverflow = n;
+ printf("DEVFS Overflow table with %d entries allocated when %d in use\n", n, devfs_numino);
+ return;
+
+bail:
+ /* Somebody beat us to it, or something went wrong. */
+ if (ot != NULL)
+ FREE(ot, M_DEVFS);
+ if (or != NULL)
+ FREE(or, M_DEVFS);
+ return;
+}
+
struct devfs_dirent *
devfs_find(struct devfs_dirent *dd, const char *name, int namelen)
{
@@ -147,33 +278,46 @@ devfs_populate(struct devfs_mount *dm)
int i, j;
dev_t dev, pdev;
struct devfs_dirent *dd;
- struct devfs_dirent *de;
+ struct devfs_dirent *de, **dep;
char *q, *s;
+ if (dm->dm_generation == devfs_generation)
+ return (0);
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curproc);
+ if (devfs_noverflow && dm->dm_overflow == NULL) {
+ i = devfs_noverflow * sizeof (struct devfs_dirent *);
+ MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
+ M_DEVFS, M_WAITOK);
+ bzero(dm->dm_overflow, i);
+ }
while (dm->dm_generation != devfs_generation) {
dm->dm_generation = devfs_generation;
- for (i = 0; i < NDEVINO; i++) {
- dev = devfs_inot[i];
- de = dm->dm_dirent[i];
+ for (i = 0; i <= devfs_topino; i++) {
+ dev = *devfs_itod(i);
+ dep = devfs_itode(dm, i);
+ de = *dep;
if (dev == NULL && de == DE_DELETED) {
- dm->dm_dirent[i] = NULL;
+ *dep = NULL;
continue;
}
if (dev == NULL && de != NULL) {
dd = de->de_dir;
- dm->dm_dirent[i] = NULL;
+ *dep = NULL;
TAILQ_REMOVE(&dd->de_dlist, de, de_list);
if (de->de_vnode) {
de->de_vnode->v_data = NULL;
vdrop(de->de_vnode);
}
FREE(de, M_DEVFS);
+ devfs_dropref(i);
continue;
}
if (dev == NULL)
continue;
if (de != NULL)
continue;
+ if (!devfs_getref(i))
+ continue;
dd = dm->dm_basedir;
s = dev->si_name;
for (;;) {
@@ -209,7 +353,7 @@ devfs_populate(struct devfs_mount *dm)
de->de_mode = dev->si_mode;
de->de_dirent->d_type = DT_CHR;
}
- dm->dm_dirent[i] = de;
+ *dep = de;
de->de_dir = dd;
TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
#if 0
@@ -217,31 +361,75 @@ devfs_populate(struct devfs_mount *dm)
#endif
}
}
+ lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curproc);
return (0);
}
-dev_t devfs_inot[NDEVINO];
-int devfs_nino = 3;
-unsigned devfs_generation;
-
static void
devfs_create(dev_t dev)
{
- if (dev->si_inode == 0 && devfs_nino < NDEVINO)
- dev->si_inode = devfs_nino++;
- if (dev->si_inode == 0) {
- printf("NDEVINO too small\n");
- return;
+ int ino, i, *ip;
+ dev_t *dp;
+
+ for (;;) {
+ /* Grab the next inode number */
+ ino = devfs_nextino;
+ i = ino + 1;
+ /* wrap around when we reach the end */
+ if (i >= NDEVFSINO + devfs_noverflow)
+ i = 3;
+ if (!atomic_cmpset_int(&devfs_nextino, ino, i))
+ continue;
+
+ /* see if it was occupied */
+ dp = devfs_itod(ino);
+ if (dp == NULL)
+ Debugger("dp == NULL\n");
+ if (*dp != NULL)
+ continue;
+ ip = devfs_itor(ino);
+ if (ip == NULL)
+ Debugger("ip == NULL\n");
+ if (*ip != 0)
+ continue;
+
+ if (!atomic_cmpset_ptr(dp, NULL, dev))
+ continue;
+
+ dev->si_inode = ino;
+ for (;;) {
+ i = devfs_topino;
+ if (i >= ino)
+ break;
+ if (atomic_cmpset_int(&devfs_topino, i, ino))
+ break;
+ printf("failed topino %d %d\n", i, ino);
+ }
+ break;
}
- devfs_inot[dev->si_inode] = dev;
- devfs_generation++;
+
+ atomic_add_int(&devfs_numino, 1);
+ atomic_add_int(&devfs_generation, 1);
+ if (devfs_overflow == NULL && devfs_numino + 100 > NDEVFSINO)
+ devfs_attemptoverflow(0);
}
static void
devfs_destroy(dev_t dev)
{
- devfs_inot[dev->si_inode] = NULL;
- devfs_generation++;
+ int ino, i;
+
+ ino = dev->si_inode;
+ dev->si_inode = 0;
+ if (ino == 0)
+ return;
+ if (atomic_cmpset_ptr(devfs_itod(ino), dev, NULL)) {
+ atomic_add_int(&devfs_generation, 1);
+ atomic_add_int(&devfs_numino, -1);
+ i = devfs_nextino;
+ if (ino < i)
+ atomic_cmpset_int(&devfs_nextino, i, ino);
+ }
}
devfs_create_t *devfs_create_hook = devfs_create;
diff --git a/sys/fs/devfs/devfs_vfsops.c b/sys/fs/devfs/devfs_vfsops.c
index 72f2630..131fd47 100644
--- a/sys/fs/devfs/devfs_vfsops.c
+++ b/sys/fs/devfs/devfs_vfsops.c
@@ -34,6 +34,8 @@
* $FreeBSD$
*/
+#include "opt_devfs.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -41,9 +43,7 @@
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/malloc.h>
-#include <sys/eventhandler.h>
-#define DEVFS_INTERN
#include <fs/devfs/devfs.h>
MALLOC_DEFINE(M_DEVFS, "DEVFS", "DEVFS data");
@@ -82,12 +82,13 @@ devfs_mount(mp, path, data, ndp, p)
MALLOC(fmp, struct devfs_mount *, sizeof(struct devfs_mount),
M_DEVFS, M_WAITOK);
bzero(fmp, sizeof(*fmp));
+ lockinit(&fmp->dm_lock, PVFS, "devfs", 0, LK_NOPAUSE);
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) fmp;
vfs_getnewfsid(mp);
- fmp->dm_inode = NDEVINO;
+ fmp->dm_inode = DEVFSINOMOUNT;
fmp->dm_rootdir = devfs_vmkdir("(root)", 6, NULL);
fmp->dm_rootdir->de_inode = 2;
@@ -110,7 +111,6 @@ devfs_mount(mp, path, data, ndp, p)
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
bcopy("devfs", mp->mnt_stat.f_mntfromname, sizeof("devfs"));
(void)devfs_statfs(mp, &mp->mnt_stat, p);
- devfs_populate(fmp);
return (0);
}
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index e96dafb..db95406 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -43,19 +43,16 @@
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/proc.h>
-#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/dirent.h>
-#include <sys/eventhandler.h>
-#define DEVFS_INTERN
#include <fs/devfs/devfs.h>
static int devfs_access __P((struct vop_access_args *ap));
static int devfs_badop __P((void));
static int devfs_getattr __P((struct vop_getattr_args *ap));
-static int devfs_lookup __P((struct vop_lookup_args *ap));
+static int devfs_lookupx __P((struct vop_lookup_args *ap));
static int devfs_print __P((struct vop_print_args *ap));
static int devfs_read __P((struct vop_read_args *ap));
static int devfs_readdir __P((struct vop_readdir_args *ap));
@@ -66,12 +63,12 @@ static int devfs_revoke __P((struct vop_revoke_args *ap));
static int devfs_setattr __P((struct vop_setattr_args *ap));
static int devfs_symlink __P((struct vop_symlink_args *ap));
-
int
devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p)
{
int error;
struct vnode *vp;
+ dev_t dev;
if (p == NULL)
p = curproc; /* XXX */
@@ -83,6 +80,13 @@ loop:
*vpp = vp;
return (0);
}
+ if (de->de_dirent->d_type == DT_CHR) {
+ dev = *devfs_itod(de->de_inode);
+ if (dev == NULL)
+ return (ENOENT);
+ } else {
+ dev = NODEV;
+ }
error = getnewvnode(VT_DEVFS, mp, devfs_vnodeop_p, &vp);
if (error != 0) {
printf("devfs_allocv: failed to allocate new vnode\n");
@@ -91,7 +95,7 @@ loop:
if (de->de_dirent->d_type == DT_CHR) {
vp->v_type = VCHR;
- vp = addaliasu(vp, devfs_inot[de->de_inode]->si_udev);
+ vp = addaliasu(vp, dev->si_udev);
vp->v_op = devfs_specop_p;
} else if (de->de_dirent->d_type == DT_DIR) {
vp->v_type = VDIR;
@@ -153,6 +157,7 @@ devfs_getattr(ap)
vap->va_mode = de->de_mode;
vap->va_size = 0;
vap->va_blocksize = DEV_BSIZE;
+ vap->va_type = vp->v_type;
if (vp->v_type != VCHR) {
vap->va_atime = de->de_atime;
vap->va_mtime = de->de_mtime;
@@ -162,23 +167,14 @@ devfs_getattr(ap)
vap->va_atime = dev->si_atime;
vap->va_mtime = dev->si_mtime;
vap->va_ctime = dev->si_ctime;
+ vap->va_rdev = dev->si_udev;
}
vap->va_gen = 0;
vap->va_flags = 0;
- vap->va_rdev = 0;
vap->va_bytes = 0;
vap->va_nlink = de->de_links;
vap->va_fileid = de->de_inode;
- if (de->de_dirent->d_type == DT_DIR) {
- vap->va_type = VDIR;
- } else if (de->de_dirent->d_type == DT_LNK) {
- vap->va_type = VLNK;
- } else if (de->de_dirent->d_type == DT_CHR) {
- vap->va_type = VCHR;
- vap->va_rdev = devfs_inot[de->de_inode]->si_udev;
- }
-
#ifdef DEBUG
if (error)
printf("devfs_getattr: return error %d\n", error);
@@ -187,7 +183,7 @@ devfs_getattr(ap)
}
static int
-devfs_lookup(ap)
+devfs_lookupx(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
@@ -358,6 +354,19 @@ found:
return (0);
}
+static int
+devfs_lookup(struct vop_lookup_args *ap)
+{
+ int j;
+ struct devfs_mount *dmp;
+
+ dmp = VFSTODEVFS(ap->a_dvp->v_mount);
+ lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc);
+ j = devfs_lookupx(ap);
+ lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
+ return (j);
+}
+
/* ARGSUSED */
static int
devfs_print(ap)
@@ -415,6 +424,7 @@ devfs_readdir(ap)
return (EINVAL);
dmp = VFSTODEVFS(ap->a_vp->v_mount);
+ lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc);
devfs_populate(dmp);
error = 0;
de = ap->a_vp->v_data;
@@ -437,6 +447,7 @@ devfs_readdir(ap)
off += dp->d_reclen;
dd = TAILQ_NEXT(dd, de_list);
}
+ lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
uio->uio_offset = off;
return (error);
}
@@ -486,16 +497,19 @@ devfs_remove(ap)
{
struct vnode *vp = ap->a_vp;
struct devfs_dirent *dd;
- struct devfs_dirent *de;
- struct devfs_mount *dm = VFSTODEVFS(vp->v_mount);
+ struct devfs_dirent *de, **dep;
+ struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
+ lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc);
dd = ap->a_dvp->v_data;
de = vp->v_data;
- de->de_flags |= DE_ORPHAN;
TAILQ_REMOVE(&dd->de_dlist, de, de_list);
- if (de->de_inode < NDEVINO)
- dm->dm_dirent[de->de_inode] = DE_DELETED;
+ dep = devfs_itode(dmp, de->de_inode);
+ if (dep != NULL)
+ *dep = DE_DELETED;
+ de->de_flags |= DE_ORPHAN;
vdrop(de->de_vnode);
+ lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
return (0);
}
@@ -589,8 +603,10 @@ devfs_symlink(ap)
i = strlen(ap->a_target) + 1;
MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK);
bcopy(ap->a_target, de->de_symlink, i);
+ lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc);
TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0);
+ lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
return (0);
}
OpenPOWER on IntegriCloud