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.
This commit is contained in:
Poul-Henning Kamp 2000-09-06 11:26:43 +00:00
parent 819e370c3b
commit 93bcdfe270
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=65515
5 changed files with 298 additions and 71 deletions

View File

@ -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

View File

@ -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_ */

View File

@ -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;

View File

@ -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);
}

View File

@ -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 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, stru
*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 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, stru
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 @@ devfs_lookup(ap)
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);
}