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

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


The following reply was made to PR kern/99758; it has been noted by GNATS.

From: Atsuo Ohki <ohki at gssm.otsuka.tsukuba.ac.jp>
To: "Wojciech A. Koszek" <wkoszek at FreeBSD.org>,
        freebsd-gnats-submit at FreeBSD.org, freebsd-bugs at FreeBSD.org
Cc:  
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Wed, 19 Jul 2006 16:12:27 +0900

 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