summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
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