kern/99758: chown/chmod pty slave side in kernel

Atsuo Ohki ohki at gssm.otsuka.tsukuba.ac.jp
Wed Jul 19 07:12:50 UTC 2006


Hi.

 I think I made it (but, still running stress2).
 (thanks to those who introduced `struct mtx devfs_de_interlock'!)

 following patch may make things running.

 I left many printf, counter variables for debugging :-)


--- ./fs/devfs/devfs_devs.c-ORIG	Wed Feb 22 18:05:40 2006
+++ ./fs/devfs/devfs_devs.c	Wed Jul 19 15:51:49 2006
@@ -23,9 +23,9 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
+ + From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
  *
- * $FreeBSD: src/sys/fs/devfs/devfs_devs.c,v 1.45 2006/02/22 09:05:40 jeff Exp $
+ + $FreeBSD: src/sys/fs/devfs/devfs_devs.c,v 1.45 2006/02/22 09:05:40 jeff Exp $
  */
 
 #include "opt_devfs.h"
@@ -93,9 +93,13 @@
 	ud ^ devfs_random();
 */
 	dev_lock();
-	TAILQ_FOREACH(cdp, &cdevp_list, cdp_list)
+	TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) {
+		if (!(cdp->cdp_flags & CDP_ACTIVE)
+		    || (cdp->cdp_flags & CDP_HID))
+			continue;
 		if (cdp->cdp_inode == ud)
 			break;
+	}
 	dev_unlock();
 	if (cdp == NULL)
 		return(ENOENT);
@@ -112,6 +116,9 @@
 SYSCTL_INT(_debug_sizeof, OID_AUTO, cdev_priv, CTLFLAG_RD,
     0, sizeof(struct cdev_priv), "sizeof(struct cdev_priv)");
 
+static int devfs_alloc_cnt = 0;
+static int devfs_free_cnt = 0;
+
 struct cdev *
 devfs_alloc(void)
 {
@@ -129,6 +136,7 @@
 
 	cdev->si_name = cdev->__si_namebuf;
 	LIST_INIT(&cdev->si_children);
+	devfs_alloc_cnt++;
 	return (cdev);
 }
 
@@ -136,8 +144,16 @@
 devfs_free(struct cdev *cdev)
 {
 	struct cdev_priv *cdp;
+	u_int i;
+	u_int p;
 
 	cdp = cdev->si_priv;
+
+	for (p=0, i=0; i <= cdp->cdp_maxdirent; i++)
+		p |= (u_int)cdp->cdp_dirents[i];
+	KASSERT(cdp->cdp_inuse == 0, ("cdp_inuse != 0 @%p", cdev));
+	KASSERT(p == 0, ("cdp_dirents[] != NULL @%p", cdev));
+
 	if (cdev->si_cred != NULL)
 		crfree(cdev->si_cred);
 	if (cdp->cdp_inode > 0)
@@ -145,6 +161,7 @@
 	if (cdp->cdp_maxdirent > 0) 
 		free(cdp->cdp_dirents, M_DEVFS2);
 	free(cdp, M_CDEVP);
+	devfs_free_cnt++;
 }
 
 struct devfs_dirent *
@@ -153,6 +170,9 @@
 	struct devfs_dirent *de;
 
 	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
+		if (de->de_cdp && (!(de->de_cdp->cdp_flags & CDP_ACTIVE) ||
+				    (de->de_cdp->cdp_flags & CDP_HID)))
+			continue;
 		if (namelen != de->de_dirent->d_namlen)
 			continue;
 		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
@@ -233,18 +253,27 @@
 void
 devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de)
 {
+	struct vnode *vp;
 
+	sx_assert(&dm->dm_lock, SX_XLOCKED);
 	if (de->de_symlink) {
 		free(de->de_symlink, M_DEVFS);
 		de->de_symlink = NULL;
 	}
-	if (de->de_vnode != NULL) {
-		vhold(de->de_vnode);
-		de->de_vnode->v_data = NULL;
-		vgone(de->de_vnode);
-		vdrop(de->de_vnode);
+	devfs_de_lock();
+	vp = de->de_vnode;
+	if (vp != NULL) {
 		de->de_vnode = NULL;
-	}
+		vp->v_data = NULL;
+		if (vp->v_rdev)
+			dev_rel(vp->v_rdev);
+		vp->v_rdev = NULL;
+		devfs_de_unlock();
+		vhold(vp);
+		vgone(vp);
+		vdrop(vp);
+	} else
+		devfs_de_unlock();
 #ifdef MAC
 	mac_destroy_devfsdirent(de);
 #endif
@@ -314,6 +343,8 @@
 	dev_unlock();
 }
 
+static int devfs_populate_gc = 0;
+
 static int
 devfs_populate_loop(struct devfs_mount *dm, int cleanup)
 {
@@ -361,6 +392,7 @@
 			TAILQ_REMOVE(&cdevp_list, cdp, cdp_list);
 			dev_unlock();
 			dev_rel(&cdp->cdp_c);
+			devfs_populate_gc++;
 			return (1);
 		}
 		/*
@@ -440,13 +472,21 @@
 void
 devfs_populate(struct devfs_mount *dm)
 {
+	unsigned old_generation;
 
 	sx_assert(&dm->dm_lock, SX_XLOCKED);
-	if (dm->dm_generation == devfs_generation)
-		return;
-	while (devfs_populate_loop(dm, 0))
-		continue;
-	dm->dm_generation = devfs_generation;
+	while(1) {
+		dev_lock();
+		if (dm->dm_generation == devfs_generation) {
+			dev_unlock();
+			return;
+		}
+		old_generation = devfs_generation;
+		dev_unlock();
+		while (devfs_populate_loop(dm, 0))
+			continue;
+		dm->dm_generation = old_generation;
+	}
 }
 
 void
@@ -487,6 +527,17 @@
 	mtx_assert(&devmtx, MA_OWNED);
 	cdp = dev->si_priv;
 	cdp->cdp_flags &= ~CDP_ACTIVE;
+	devfs_generation++;
+}
+                 
+void
+devfs_hide(struct cdev *dev)
+{
+	struct cdev_priv *cdp;
+
+	mtx_assert(&devmtx, MA_OWNED);
+	cdp = dev->si_priv;
+	cdp->cdp_flags |= CDP_HID;
 	devfs_generation++;
 }
 
--- ./fs/devfs/devfs_vnops.c-ORIG	Fri Jul  7 16:28:01 2006
+++ ./fs/devfs/devfs_vnops.c	Wed Jul 19 15:51:55 2006
@@ -29,9 +29,9 @@
  * SUCH DAMAGE.
  *
  *	@(#)kernfs_vnops.c	8.15 (Berkeley) 5/21/95
- * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
+ + From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
  *
- * $FreeBSD: src/sys/fs/devfs/devfs_vnops.c,v 1.131 2006/07/06 13:22:08 rwatson Exp $
+ + $FreeBSD: src/sys/fs/devfs/devfs_vnops.c,v 1.131 2006/07/06 13:22:08 rwatson Exp $
  */
 
 /*
@@ -72,6 +72,21 @@
 #include <fs/devfs/devfs.h>
 #include <fs/devfs/devfs_int.h>
 
+static struct mtx	devfs_de_interlock;
+MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF);
+
+void
+devfs_de_lock(void)
+{
+	mtx_lock(&devfs_de_interlock);
+}
+
+void
+devfs_de_unlock(void)
+{
+	mtx_unlock(&devfs_de_interlock);
+}
+
 static int
 devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp)
 {
@@ -124,24 +139,52 @@
 }
 
 int
-devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td)
+devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td, int lkflag)
 {
 	int error;
 	struct vnode *vp;
 	struct cdev *dev;
+	struct devfs_mount *dmp;
 
 	KASSERT(td == curthread, ("devfs_allocv: td != curthread"));
+	dmp = VFSTODEVFS(mp);
+	sx_assert(&dmp->dm_lock, SX_XLOCKED);
+	lkflag = LK_EXCLUSIVE;
 loop:
+	devfs_de_lock();
 	vp = de->de_vnode;
 	if (vp != NULL) {
-		if (vget(vp, LK_EXCLUSIVE, td))
+		VI_LOCK(vp);
+		devfs_de_unlock();
+		sx_xunlock(&dmp->dm_lock);
+		error=vget(vp, lkflag|LK_INTERLOCK, td);
+		if (error) {
+			if (error == ENOENT) {
+				/* vnode has gone! */
+				devfs_de_lock();
+				de->de_vnode = NULL;
+				devfs_de_unlock();
+				printf("devfs_allocv(): vnode %p error#%d\n",
+					vp, error);
+			}
+			sx_xlock(&dmp->dm_lock);
 			goto loop;
+		}
+		sx_xlock(&dmp->dm_lock);
+		devfs_de_lock();
+		KASSERT(vp == de->de_vnode,
+			("devfs_allocv(): vp(%p) changed!", vp));
+		devfs_de_unlock();
 		*vpp = vp;
 		return (0);
 	}
+	devfs_de_unlock();
 	if (de->de_dirent->d_type == DT_CHR) {
-		if (!(de->de_cdp->cdp_flags & CDP_ACTIVE))
+		/* should not happen, but ... paranoia */
+		if (!(de->de_cdp->cdp_flags & CDP_ACTIVE) ||
+		    (de->de_cdp->cdp_flags & CDP_HID)) {
 			return (ENOENT);
+		}
 		dev = &de->de_cdp->cdp_c;
 	} else {
 		dev = NULL;
@@ -171,9 +214,20 @@
 	} else {
 		vp->v_type = VBAD;
 	}
+	devfs_de_lock();
+	if (de->de_vnode != NULL) {
+		/* someone has allocated before us ! */
+		VI_LOCK(vp);
+		vholdl(vp);
+		VI_UNLOCK(vp);
+		vgone(vp);
+		vdrop(vp);
+		vp = de->de_vnode;
+	}
 	vp->v_data = de;
 	de->de_vnode = vp;
-	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+	devfs_de_unlock();
+	vn_lock(vp, lkflag | LK_RETRY, td);
 #ifdef MAC
 	mac_associate_vnode_devfs(mp, de, vp);
 #endif
@@ -498,7 +552,7 @@
 		de = TAILQ_FIRST(&dd->de_dlist);	/* "." */
 		de = TAILQ_NEXT(de, de_list);		/* ".." */
 		de = de->de_dir;
-		error = devfs_allocv(de, dvp->v_mount, vpp, td);
+		error = devfs_allocv(de, dvp->v_mount, vpp, td, cnp->cn_lkflags);
 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
 		return (error);
 	}
@@ -506,7 +560,8 @@
 	devfs_populate(dmp);
 	dd = dvp->v_data;
 	de = devfs_find(dd, cnp->cn_nameptr, cnp->cn_namelen);
-	while (de == NULL) {	/* While(...) so we can use break */
+	if ((flags & (ISLASTCN|ISOPEN)) == (ISLASTCN|ISOPEN))
+	    while (de == NULL) {	/* While(...) so we can use break */
 
 		if (nameiop == DELETE)
 			return (ENOENT);
@@ -555,7 +610,7 @@
 			return (0);
 		}
 	}
-	error = devfs_allocv(de, dvp->v_mount, vpp, td);
+	error = devfs_allocv(de, dvp->v_mount, vpp, td, cnp->cn_lkflags);
 	return (error);
 }
 
@@ -611,7 +666,7 @@
 	if (de == NULL)
 		goto notfound;
 	de->de_flags &= ~DE_WHITEOUT;
-	error = devfs_allocv(de, dvp->v_mount, vpp, td);
+	error = devfs_allocv(de, dvp->v_mount, vpp, td, cnp->cn_lkflags);
 notfound:
 	sx_xunlock(&dmp->dm_lock);
 	return (error);
@@ -830,6 +885,10 @@
 			de = dd->de_dir;
 		else
 			de = dd;
+		if (de->de_cdp && (de->de_cdp->cdp_flags & CDP_HID)) {
+			/* printf("devfs_readdir(): skip hid dev\n"); */
+			continue;
+		}
 		dp = dd->de_dirent;
 		if (dp->d_reclen > uio->uio_resid)
 			break;
@@ -870,17 +929,21 @@
 	struct devfs_dirent *de;
 	struct cdev *dev;
 
+	devfs_de_lock();
 	de = vp->v_data;
-	if (de != NULL)
+	if (de != NULL) {
 		de->de_vnode = NULL;
-	vp->v_data = NULL;
+		vp->v_data = NULL;
+	}
+	devfs_de_unlock();
 	vnode_destroy_vobject(vp);
 
 	dev = vp->v_rdev;
 	vp->v_rdev = NULL;
 
-	if (dev == NULL)
+	if (dev == NULL) {
 		return (0);
+	}
 
 	dev_lock();
 	dev->si_usecount -= vp->v_usecount;
@@ -934,25 +997,32 @@
 	dev = vp->v_rdev;
 	cdp = dev->si_priv;
 	for (;;) {
+		devfs_de_lock();
 		dev_lock();
 		vp2 = NULL;
 		for (i = 0; i <= cdp->cdp_maxdirent; i++) {
 			de = cdp->cdp_dirents[i];
 			if (de == NULL)
 				continue;
+
 			vp2 = de->de_vnode;
-			de->de_vnode = NULL;
-			if (vp2 != NULL)
+			if (vp2 != NULL) {
+				de->de_vnode = NULL;
+				dev_unlock();
+				VI_LOCK(vp2);
+				devfs_de_unlock();
+				vholdl(vp2);
+				VI_UNLOCK(vp2);
+				vgone(vp2);
+				vdrop(vp2);
 				break;
+			}
 		}
-		dev_unlock();
 		if (vp2 != NULL) {
-			/* XXX */
-			vhold(vp2);
-			vgone(vp2);
-			vdrop(vp2);
 			continue;
 		}
+		dev_unlock();
+		devfs_de_unlock();
 		break;
 	}
 	return (0);
@@ -1120,7 +1190,7 @@
 	mac_create_devfs_symlink(ap->a_cnp->cn_cred, dmp->dm_mount, dd, de);
 #endif
 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
-	devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, td);
+	devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, td, ap->a_cnp->cn_lkflags);
 	sx_xunlock(&dmp->dm_lock);
 	return (0);
 }
--- ./fs/devfs/devfs_int.h-ORIG	Tue Sep 20 04:56:48 2005
+++ ./fs/devfs/devfs_int.h	Wed Jul 19 15:52:01 2006
@@ -22,7 +22,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/fs/devfs/devfs_int.h,v 1.2 2005/09/19 19:56:48 phk Exp $
+ + $FreeBSD: src/sys/fs/devfs/devfs_int.h,v 1.2 2005/09/19 19:56:48 phk Exp $
  */
 
 /*
@@ -47,6 +47,7 @@
 
 	u_int			cdp_flags;
 #define CDP_ACTIVE		(1 << 0)
+#define CDP_HID			(1 << 1)
 
 	u_int			cdp_inuse;
 	u_int			cdp_maxdirent;
@@ -58,6 +59,10 @@
 void devfs_free(struct cdev *);
 void devfs_create(struct cdev *dev);
 void devfs_destroy(struct cdev *dev);
+void devfs_hide(struct cdev *dev);
+
+void devfs_de_lock(void);
+void devfs_de_unlock(void);
 
 extern struct unrhdr *devfs_inos;
 extern struct mtx devmtx;
--- ./fs/devfs/devfs_vfsops.c-ORIG	Fri Jul  7 16:28:01 2006
+++ ./fs/devfs/devfs_vfsops.c	Wed Jul 19 15:52:06 2006
@@ -29,9 +29,9 @@
  * SUCH DAMAGE.
  *
  *	@(#)kernfs_vfsops.c	8.10 (Berkeley) 5/14/95
- * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
+ + From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
  *
- * $FreeBSD: src/sys/fs/devfs/devfs_vfsops.c,v 1.49 2006/07/06 13:24:22 rwatson Exp $
+ + $FreeBSD: src/sys/fs/devfs/devfs_vfsops.c,v 1.49 2006/07/06 13:24:22 rwatson Exp $
  */
 
 #include "opt_devfs.h"
@@ -139,10 +139,14 @@
 	struct devfs_mount *dmp;
 
 	dmp = VFSTODEVFS(mp);
-	error = devfs_allocv(dmp->dm_rootdir, mp, &vp, td);
+	sx_xlock(&dmp->dm_lock);
+	error = devfs_allocv(dmp->dm_rootdir, mp, &vp, td, LK_EXCLUSIVE);
+	sx_xunlock(&dmp->dm_lock);
 	if (error)
 		return (error);
 	vp->v_vflag |= VV_ROOT;
+	VREF(vp);
+	vhold(vp);
 	*vpp = vp;
 	return (0);
 }
--- ./fs/devfs/devfs.h-ORIG	Wed Apr 12 21:17:29 2006
+++ ./fs/devfs/devfs.h	Wed Jul 19 15:52:14 2006
@@ -31,9 +31,9 @@
  * SUCH DAMAGE.
  *
  *	@(#)kernfs.h	8.6 (Berkeley) 3/29/95
- * From: FreeBSD: src/sys/miscfs/kernfs/kernfs.h 1.14
+ + From: FreeBSD: src/sys/miscfs/kernfs/kernfs.h 1.14
  *
- * $FreeBSD: src/sys/fs/devfs/devfs.h,v 1.29 2006/04/12 12:17:29 pjd Exp $
+ + $FreeBSD: src/sys/fs/devfs/devfs.h,v 1.29 2006/04/12 12:17:29 pjd Exp $
  */
 
 #ifndef _FS_DEVFS_DEVFS_H_
@@ -163,7 +163,7 @@
 void devfs_rules_apply(struct devfs_mount *dm, struct devfs_dirent *de);
 void devfs_rules_cleanup (struct devfs_mount *dm);
 int devfs_rules_ioctl(struct devfs_mount *dm, u_long cmd, caddr_t data, struct thread *td);
-int devfs_allocv (struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td);
+int devfs_allocv (struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td, int lkflag);
 void devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de);
 void devfs_populate (struct devfs_mount *dm);
 void devfs_cleanup (struct devfs_mount *dm);
--- ./kern/kern_conf.c-ORIG	Wed May 17 15:37:14 2006
+++ ./kern/kern_conf.c	Wed Jul 19 15:52:33 2006
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/kern/kern_conf.c,v 1.198 2006/05/17 06:37:14 phk Exp $");
+__FBSDID("$FreeBSD: src/sys/kern/kern_conf.c,v 1.198-bis 2006/05/17 06:37:14 phk Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -72,6 +72,10 @@
 dev_ref(struct cdev *dev)
 {
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("dev_ref(): dev:%p", dev));
+	KASSERT(dev->si_refcount != 0xdeadc0de,
+		 ("dev_ref(): si_refcount @%p", dev));
 	mtx_assert(&devmtx, MA_NOTOWNED);
 	mtx_lock(&devmtx);
 	dev->si_refcount++;
@@ -82,6 +86,10 @@
 dev_refl(struct cdev *dev)
 {
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("dev_refl(): dev:%p", dev));
+	KASSERT(dev->si_refcount != 0xdeadc0de,
+		 ("dev_refl(): si_refcount @%p", dev));
 	mtx_assert(&devmtx, MA_OWNED);
 	dev->si_refcount++;
 }
@@ -91,6 +99,10 @@
 {
 	int flag = 0;
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("dev_rel(): dev:%p", dev));
+	KASSERT(dev->si_refcount != 0xdeadc0de,
+		 ("dev_rel(): si_refcount @%p", dev));
 	mtx_assert(&devmtx, MA_NOTOWNED);
 	dev_lock();
 	dev->si_refcount--;
@@ -116,6 +128,10 @@
 {
 	struct cdevsw *csw;
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("dev_refthread(): dev:%p", dev));
+	KASSERT(dev->si_threadcount != 0xdeadc0de,
+		 ("dev_refthread(): si_threadcount @%p", dev));
 	mtx_assert(&devmtx, MA_NOTOWNED);
 	dev_lock();
 	csw = dev->si_devsw;
@@ -129,6 +145,10 @@
 dev_relthread(struct cdev *dev)
 {
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("dev_relthread(): dev:%p", dev));
+	KASSERT(dev->si_threadcount != 0xdeadc0de,
+	 	("dev_relthread(): si_threadcount@%p", dev));
 	mtx_assert(&devmtx, MA_NOTOWNED);
 	dev_lock();
 	dev->si_threadcount--;
@@ -400,6 +420,7 @@
 	LIST_FOREACH(si2, &csw->d_devs, si_list) {
 		if (si2->si_drv0 == udev) {
 			devfs_free(si);
+			printf("newdev(): reuse %s:%d\n", csw->d_name, udev);
 			return (si2);
 		}
 	}
@@ -515,6 +536,11 @@
 	struct cdev *dev;
 	int i;
 
+	if (devsw == NULL) printf("make_dev_credv(): NULL cdevsw\n");
+	KASSERT(((devsw != (void*)0xdeadc0de) &&
+		(*((u_int*)devsw) != 0xdeadc0de)),
+	    ("Invalid devsw (%p) in make_dev", devsw));
+
 	KASSERT((minornr & ~MAXMINOR) == 0,
 	    ("Invalid minor (0x%x) in make_dev", minornr));
 
@@ -633,12 +659,20 @@
 {
 	struct cdevsw *csw;
 
+	KASSERT(dev != (void*)0xdeadc0de,
+		 ("destroy_dev(): dev:%p", dev));
+	KASSERT(dev->si_refcount != 0xdeadc0de,
+		 ("destroy_dev(): si_refcount @%p", dev));
+
 	mtx_assert(&devmtx, MA_OWNED);
 	KASSERT(dev->si_flags & SI_NAMED,
 	    ("WARNING: Driver mistake: destroy_dev on %d\n", minor(dev)));
 
 	devfs_destroy(dev);
 
+	if (dev->si_refcount == 0)
+		printf("destroy_devl(): si_refcount == 0 @%p\n", dev);
+
 	/* Remove name marking */
 	dev->si_flags &= ~SI_NAMED;
 
@@ -677,7 +711,7 @@
 		LIST_REMOVE(dev, si_list);
 
 		/* If cdevsw has no more struct cdev *'s, clean it */
-		if (LIST_EMPTY(&csw->d_devs))
+		if (LIST_EMPTY(&csw->d_devs) && !(csw->d_flags & D_NO_FINI))
 			fini_cdevsw(csw);
 	}
 	dev->si_flags &= ~SI_ALIAS;
@@ -685,16 +719,29 @@
 	if (dev->si_refcount > 0) {
 		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
 	} else {
+		printf("destroy_devl(%p)\n", dev);
 		devfs_free(dev);
 	}
 }
 
+static int destroy_dev_cnt = 0;
+
 void
 destroy_dev(struct cdev *dev)
 {
 
 	dev_lock();
 	destroy_devl(dev);
+	dev_unlock();
+	destroy_dev_cnt++;
+}
+
+void
+hide_dev(struct cdev *dev)
+{
+
+	dev_lock();
+	devfs_hide(dev);
 	dev_unlock();
 }
 
--- ./kern/tty_pts.c-ORIG	Sat Apr 29 06:39:57 2006
+++ ./kern/tty_pts.c	Wed Jul 19 15:52:47 2006
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/kern/tty_pts.c,v 1.8 2006/04/28 21:39:57 rwatson Exp $");
+__FBSDID("$FreeBSD: src/sys/kern/tty_pts.c,v 1.8-bis 2006/04/28 21:39:57 rwatson Exp $");
 
 /*
  * Pseudo-teletype Driver
@@ -96,7 +96,7 @@
 	.d_ioctl =	ptsioctl,
 	.d_poll =	ttypoll,
 	.d_name =	"pts",
-	.d_flags =	D_TTY | D_NEEDGIANT,
+	.d_flags =	D_TTY | D_NEEDGIANT | D_NO_FINI,
 	.d_kqfilter =	ttykqfilter,
 };
 
@@ -109,7 +109,7 @@
 	.d_ioctl =	ptcioctl,
 	.d_poll =	ptcpoll,
 	.d_name =	"ptc",
-	.d_flags =	D_TTY | D_NEEDGIANT,
+	.d_flags =	D_TTY | D_NEEDGIANT | D_NO_FINI,
 	.d_kqfilter =	ttykqfilter,
 };
 
@@ -135,7 +135,7 @@
  */
 struct	pt_desc {
 	int			 pt_num;	/* (c) pty number */
-	LIST_ENTRY(pt_desc)	 pt_list;	/* (p) global pty list */
+	TAILQ_ENTRY(pt_desc)	 pt_list;	/* (p) global pty list */
 
 	int			 pt_flags;
 	struct selinfo		 pt_selr, pt_selw;
@@ -148,8 +148,8 @@
 };
 
 static struct mtx		pt_mtx;
-static LIST_HEAD(,pt_desc)	pt_list;
-static LIST_HEAD(,pt_desc)	pt_free_list;
+static TAILQ_HEAD(,pt_desc)	pt_active_list;
+static TAILQ_HEAD(,pt_desc)	pt_free_list;
 
 #define	PF_PKT		0x008		/* packet mode */
 #define	PF_STOPPED	0x010		/* user told stopped */
@@ -162,6 +162,10 @@
 
 static unsigned int max_pts = 1000;
 
+static int free_pts = 0;
+
+static int active_pts = 0;
+
 static unsigned int nb_allocated;
 
 TUNABLE_INT("kern.pts.enable", &use_pts);
@@ -173,6 +177,12 @@
 
 SYSCTL_INT(_kern_pts, OID_AUTO, max, CTLFLAG_RW, &max_pts, 0, "max pts");
 
+SYSCTL_INT(_kern_pts, OID_AUTO, active, CTLFLAG_RD, &active_pts, 0,
+    "# of active pts");
+
+SYSCTL_INT(_kern_pts, OID_AUTO, free, CTLFLAG_RD, &free_pts, 0,
+    "# of free pts");
+
 /*
  * If there's a free pty descriptor in the pty descriptor list, retrieve it.
  * Otherwise, allocate a new one, initialize it, and hook it up.  If there's
@@ -182,29 +192,47 @@
 pty_new(void)
 {
 	struct pt_desc *pt;
-	int nb;
 
 	mtx_lock(&pt_mtx);
 	if (nb_allocated >= max_pts || nb_allocated == 0xffffff) {
 		mtx_unlock(&pt_mtx);
+		printf("pty_new(): too many pty allocation(%d)\n",
+			nb_allocated);
 		return (NULL);
 	}
-	pt = LIST_FIRST(&pt_free_list);
-	if (pt) {
-		LIST_REMOVE(pt, pt_list);
-		LIST_INSERT_HEAD(&pt_list, pt, pt_list);
-		mtx_unlock(&pt_mtx);
-	} else {
-		nb = next_avail_nb++;
+	nb_allocated++;
+	/* first free unused ptc/pty pairs */
+	TAILQ_FOREACH(pt, &pt_free_list, pt_list) {
+		dev_lock();
+		if (pt->pt_devc && pt->pt_devc->si_usecount == 0 &&
+		    pt->pt_devs && pt->pt_devs->si_usecount == 0) {
+			dev_unlock();
+			destroy_dev(pt->pt_devc);
+			destroy_dev(pt->pt_devs);
+			pt->pt_devc = pt->pt_devs = NULL;
+		} else
+			dev_unlock();
+	}
+	TAILQ_FOREACH(pt, &pt_free_list, pt_list) {
+		if (pt->pt_devc == NULL && pt->pt_devs == NULL) {
+			TAILQ_REMOVE(&pt_free_list, pt, pt_list);
+			free_pts--;
+			TAILQ_INSERT_TAIL(&pt_active_list, pt, pt_list);
+			active_pts++;
+			mtx_unlock(&pt_mtx);
+			break;
+		}
+	}
+	if (!pt) {
 		mtx_unlock(&pt_mtx);
 		pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO);
 		mtx_lock(&pt_mtx);
-		pt->pt_num = nb;
-		LIST_INSERT_HEAD(&pt_list, pt, pt_list);
+		pt->pt_num = next_avail_nb++;
+		TAILQ_INSERT_TAIL(&pt_active_list, pt, pt_list);
+		active_pts++;
 		mtx_unlock(&pt_mtx);
 		pt->pt_tty = ttyalloc();
 	}
-	nb_allocated++;
 	return (pt);
 }
 
@@ -218,12 +246,16 @@
 
 	KASSERT(pt->pt_ptc_open == 0 && pt->pt_pts_open == 0,
 	    ("pty_release: pts/%d freed while open\n", pt->pt_num));
+#if 0
 	KASSERT(pt->pt_devs == NULL && pt->pt_devc == NULL,
-	    ("pty_release: pts/%d freed whith non-null struct cdev\n", pt->pt_num));
+	    ("pty_release: pts/%d freed while non-null struct cdev\n", pt->pt_num));
+#endif
 	mtx_assert(&pt_mtx, MA_OWNED);
 	nb_allocated--;
-	LIST_REMOVE(pt, pt_list);
-	LIST_INSERT_HEAD(&pt_free_list, pt, pt_list);
+	TAILQ_REMOVE(&pt_active_list, pt, pt_list);
+	active_pts--;
+	TAILQ_INSERT_TAIL(&pt_free_list, pt, pt_list);
+	free_pts++;
 }
 
 /*
@@ -238,6 +270,15 @@
 	if (pt->pt_ptc_open || pt->pt_pts_open)
 		return;
 
+#if 1
+	pt->pt_tty->t_dev = NULL;
+
+	hide_dev(pt->pt_devs);
+	hide_dev(pt->pt_devc);
+	mtx_lock(&pt_mtx);
+	pty_release(pt);
+	mtx_unlock(&pt_mtx);
+#else
 	if (bootverbose)
 		printf("destroying pty %d\n", pt->pt_num);
 
@@ -249,6 +290,8 @@
 	mtx_lock(&pt_mtx);
 	pty_release(pt);
 	mtx_unlock(&pt_mtx);
+#endif
+	return;
 }
 
 /*ARGSUSED*/
@@ -857,9 +900,11 @@
 	    NUM_TO_MINOR(pt->pt_num), cred, UID_ROOT, GID_WHEEL, 0666,
 	    "pty/%d", pt->pt_num);
 
-	dev_ref(devc);
+	dev_lock();
+	dev_refl(devc);
 	devc->si_drv1 = pt;
 	devc->si_tty = pt->pt_tty;
+	dev_unlock();
 	*dev = devc;
 
 	if (bootverbose)
@@ -869,14 +914,28 @@
 	return;
 }
 
+#if 0
+static struct cdev *dmyc;
+static struct cdev *dmys;
+#endif
+
 static void
 pty_drvinit(void *unused)
 {
 
 	mtx_init(&pt_mtx, "pt_mtx", NULL, MTX_DEF);
-	LIST_INIT(&pt_list);
-	LIST_INIT(&pt_free_list);
+	TAILQ_INIT(&pt_active_list);
+	TAILQ_INIT(&pt_free_list);
 	EVENTHANDLER_REGISTER(dev_clone, pty_clone, 0, 1000);
+#if 0
+	dmyc = make_dev(&ptc_cdevsw,
+            MAXMINOR, UID_ROOT, GID_WHEEL, 0666, "pty/ptmx");
+	make_dev_alias(dmyc, "ptmx");
+	hide_dev(dmyc);
+	dmys = make_dev(&pts_cdevsw,
+            MAXMINOR, UID_ROOT, GID_WHEEL, 0666, "pts/d");
+	hide_dev(dmys);
+#endif
 }
 
 SYSINIT(ptydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,pty_drvinit,NULL)
--- ./sys/conf.h-ORIG	Sat May 13 04:40:54 2006
+++ ./sys/conf.h	Wed Jul 19 15:52:54 2006
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  *	@(#)conf.h	8.5 (Berkeley) 1/9/95
- * $FreeBSD: src/sys/sys/conf.h,v 1.229 2006/05/12 19:40:54 jmg Exp $
+ + $FreeBSD: src/sys/sys/conf.h,v 1.229 2006/05/12 19:40:54 jmg Exp $
  */
 
 #ifndef _SYS_CONF_H_
@@ -169,6 +169,7 @@
 #define D_MMAP_ANON	0x00100000	/* special treatment in vm_mmap.c */
 #define D_PSEUDO	0x00200000	/* make_dev() can return NULL */
 #define D_NEEDGIANT	0x00400000	/* driver want Giant */
+#define D_NO_FINI	0x00800000	/* do not call fini_cdevsw() */
 
 /*
  * Version numbers.
@@ -243,6 +244,7 @@
 
 int	count_dev(struct cdev *_dev);
 void	destroy_dev(struct cdev *_dev);
+void	hide_dev(struct cdev *_dev);
 struct cdevsw *dev_refthread(struct cdev *_dev);
 void	dev_relthread(struct cdev *_dev);
 void	dev_depends(struct cdev *_pdev, struct cdev *_cdev);


More information about the freebsd-bugs mailing list