diff options
Diffstat (limited to 'sys/fs/devfs')
-rw-r--r-- | sys/fs/devfs/devfs.h | 63 | ||||
-rw-r--r-- | sys/fs/devfs/devfs_devs.c | 234 | ||||
-rw-r--r-- | sys/fs/devfs/devfs_vfsops.c | 8 | ||||
-rw-r--r-- | sys/fs/devfs/devfs_vnops.c | 60 |
4 files changed, 294 insertions, 71 deletions
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); } |