From nobody@FreeBSD.org  Tue Jul  4 02:42:07 2006
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 0BFDD16A4E0
	for <freebsd-gnats-submit@FreeBSD.org>; Tue,  4 Jul 2006 02:42:07 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id A9F4C43D45
	for <freebsd-gnats-submit@FreeBSD.org>; Tue,  4 Jul 2006 02:42:06 +0000 (GMT)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.13.1/8.13.1) with ESMTP id k642g6gE099463
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 4 Jul 2006 02:42:06 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.13.1/8.13.1/Submit) id k642g6BG099462;
	Tue, 4 Jul 2006 02:42:06 GMT
	(envelope-from nobody)
Message-Id: <200607040242.k642g6BG099462@www.freebsd.org>
Date: Tue, 4 Jul 2006 02:42:06 GMT
From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: freebsd-gnats-submit@FreeBSD.org
Subject: chown/chmod pty slave side in kernel
X-Send-Pr-Version: www-2.3

>Number:         99758
>Category:       kern
>Synopsis:       [pty] [patch] chown/chmod pty slave side in kernel
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jul 04 02:50:14 GMT 2006
>Closed-Date:    Sun May 02 21:59:36 UTC 2010
>Last-Modified:  Sun May 02 21:59:36 UTC 2010
>Originator:     Atsuo Ohki
>Release:        FreeBSD 6.1
>Organization:
GSSM, University of Tsukuba, Tokyo
>Environment:
FreeBSD smr01 6.1-RELEASE-p2 FreeBSD 6.1-RELEASE-p2 #3: Mon Jul  3 15:55:22 JST 2006     ohki@smr01:/usr/src/sys/i386/compile/SMP  i386
>Description:
I know that the ownership and permission of the pty slave side can be
controlled by `grantpt()' (with a set-uided helper program /usr/libexec/pt_chown),
but all programs which manipulate control/slave pair of pty do not
use `grantpt()'. More over, `grantpt()' leave the onwership of pty slave as
those of user who used the slave last time.

I though it is better to control the ownership and permission of the pty slave
as follow:
 1) when the control side is opened, set the ownership of the corresponding
    slave to those who opend the control side, and the permission as 0620.
 2) when the control side is closed, restore the ownership/permission of the
    corresponding slave to root/wheel, 0666.

Included patch modifies kern/tty_pty.c, fs/devfs/devfs_devs.c,
fs/devfs/devfs_vfsops.c

>How-To-Repeat:

>Fix:
--- kern/tty_pty.c-ORIG Fri Mar 31 01:46:56 2006
+++ kern/tty_pty.c      Sun Jul  2 22:49:29 2006
@@ -265,6 +265,8 @@
        }
 }
 
+extern void devfs_update(struct cdev *dev, uid_t uid, gid_t gid, int mode);
+
 static int
 ptcopen(struct cdev *dev, int flag, int devtype, struct thread *td)
 {
@@ -288,6 +290,9 @@
        pt->pt_flags = 0;
        pt->pt_send = 0;
        pt->pt_ucntl = 0;
+
+       devfs_update(pt->devs, td->td_ucred->cr_ruid, 4, 0620);
+
        return (0);
 }
 
@@ -311,6 +316,11 @@
                tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
                tp->t_state |= TS_ZOMBIE;
                ttyflush(tp, FREAD | FWRITE);
+       }
+
+       {
+               struct ptsc *pt = dev->si_drv1;
+               devfs_update(pt->devs, UID_ROOT, GID_WHEEL, 0666);
        }
 
        tp->t_oproc = 0;                /* mark closed */
--- fs/devfs/devfs_devs.c-ORIG  Mon Mar 13 12:05:06 2006
+++ fs/devfs/devfs_devs.c       Mon Jul  3 10:45:18 2006
@@ -490,6 +490,43 @@
        devfs_generation++;
 }
 
+extern int devfs_sx_xlock(u_int idx);
+extern void devfs_sx_xunlock(u_int idx);
+void devfs_update(struct cdev *dev, uid_t uid, gid_t gid, int mode);
+
+void
+devfs_update(struct cdev *dev, uid_t uid, gid_t gid, int mode)
+{
+       struct cdev_priv *cdp;
+       struct devfs_dirent *de;
+       u_int i;
+
+       cdp = dev->si_priv;
+       dev_lock();
+       if (dev != &cdp->cdp_c || cdp->cdp_dirents == NULL)
+               goto out;
+
+       dev->si_uid = uid;
+       dev->si_gid = gid;
+       dev->si_mode = mode;
+
+       for (i=0; i <= cdp->cdp_maxdirent; i++) {
+               if ((de = cdp->cdp_dirents[i]) != NULL &&
+                   de->de_cdp == cdp && de->de_dirent->d_type == DT_CHR) {
+                       dev_unlock();
+                       if (devfs_sx_xlock(i)) {
+                               de->de_uid = uid;
+                               de->de_gid = gid;
+                               de->de_mode =mode;
+                               devfs_sx_xunlock(i);
+                       }
+                       dev_lock();
+               }
+       }
+out:
+       dev_unlock();
+}
+
 static void
 devfs_devs_init(void *junk __unused)
 {
--- fs/devfs/devfs_vfsops.c-ORIG        Mon Sep 26 23:36:52 2005
+++ fs/devfs/devfs_vfsops.c     Mon Jul  3 15:53:37 2006
@@ -61,6 +61,68 @@
 static vfs_statfs_t    devfs_statfs;
 
 /*
+ * stuff for devfs_update()
+ */
+#define DEVFS_IDX2MOUNTP_SIZE  16
+static struct devfs_mount *devfs_mountps[DEVFS_IDX2MOUNTP_SIZE];
+
+static inline void
+put_fmp(struct devfs_mount *dm)
+{
+       int i;
+
+       for (i=0; i<DEVFS_IDX2MOUNTP_SIZE; i++) {
+               if (devfs_mountps[i] == NULL) {
+                       devfs_mountps[i] = dm;
+                       return;
+               }
+       }
+}
+
+static inline void
+clear_fmp(struct devfs_mount *dm)
+{
+       int i;
+
+       for (i=0; i<DEVFS_IDX2MOUNTP_SIZE; i++) {
+               if (devfs_mountps[i] == dm) {
+                       devfs_mountps[i] = NULL;
+                       return;
+               }
+       }
+}
+
+int devfs_sx_xlock(u_int idx);
+void devfs_sx_xunlock(u_int idx);
+
+int
+devfs_sx_xlock(u_int idx) {
+       int i;
+
+       for (i=0; i<DEVFS_IDX2MOUNTP_SIZE; i++) {
+               if (devfs_mountps[i] != NULL &&
+                   devfs_mountps[i]->dm_idx == idx) {
+                       sx_xlock(&(devfs_mountps[i]->dm_lock));
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+void
+devfs_sx_xunlock(u_int idx) {
+       int i;
+
+       for (i=0; i<DEVFS_IDX2MOUNTP_SIZE; i++) {
+               if (devfs_mountps[i] != NULL &&
+                   devfs_mountps[i]->dm_idx == idx) {
+                       sx_xunlock(&(devfs_mountps[i]->dm_lock));
+                       return;
+               }
+       }
+}
+
+/*
  * Mount the filesystem
  */
 static int
@@ -105,6 +167,8 @@
 
        vfs_mountedfrom(mp, "devfs");
 
+       put_fmp(fmp);   /* XXX */
+
        return (0);
 }
 
@@ -121,6 +185,7 @@
        if (error)
                return (error);
        sx_xlock(&fmp->dm_lock);
+       clear_fmp(fmp); /* XXX */
        devfs_cleanup(fmp);
        devfs_rules_cleanup(fmp);
        sx_xunlock(&fmp->dm_lock);

>Release-Note:
>Audit-Trail:

From: Robert Watson <rwatson@FreeBSD.org>
To: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
Cc: freebsd-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Wed, 5 Jul 2006 12:08:54 +0100 (BST)

 On Tue, 4 Jul 2006, Atsuo Ohki wrote:
 
 > I know that the ownership and permission of the pty slave side can be 
 > controlled by `grantpt()' (with a set-uided helper program 
 > /usr/libexec/pt_chown), but all programs which manipulate control/slave pair 
 > of pty do not use `grantpt()'. More over, `grantpt()' leave the onwership of 
 > pty slave as those of user who used the slave last time.
 >
 > I though it is better to control the ownership and permission of the pty slave
 > as follow:
 > 1) when the control side is opened, set the ownership of the corresponding
 >    slave to those who opend the control side, and the permission as 0620.
 > 2) when the control side is closed, restore the ownership/permission of the
 >    corresponding slave to root/wheel, 0666.
 >
 > Included patch modifies kern/tty_pty.c, fs/devfs/devfs_devs.c, 
 > fs/devfs/devfs_vfsops.c
 
 The tty_pts implementation in 7-CURRENT does the same, except more so, by 
 forcing revocation of the pty on last close.  Unfortunately, this triggers 
 bugs in devfs.  Your help in getting the 7.x pts implementation up and running 
 would be much appreciated.  Take a look at src/sys/kern/tty_pts.c.
 
 Robert N M Watson
 Computer Laboratory
 University of Cambridge

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: Robert Watson <rwatson@FreeBSD.org>
Cc: freebsd-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Thu, 06 Jul 2006 17:42:20 +0900

 Robert Watson writes:
 > ...
 > The tty_pts implementation in 7-CURRENT does the same, except more so, by 
 > forcing revocation of the pty on last close.  Unfortunately, this triggers 
 > bugs in devfs.  Your help in getting the 7.x pts implementation up and runnin
 > g 
 > would be much appreciated.  Take a look at src/sys/kern/tty_pts.c.
 
  Thank you for interested in my patch.
 
  I don't have a machine to run 7-current now,
  so just read `src/sys/kern/tty_pts.c'.
 
  Is make_dev()/destroy_dev() problem specific to pts implementaition?
  What happens when destroy_devl() of src/sys/kern/kern_conf.c is modified as
 
 --- kern_conf.c	Thu Jul  6 17:11:27 2006
 +++ kern_conf.c	Thu Jul  6 17:38:51 2006
 @@ -682,6 +682,10 @@
  	}
  	dev->si_flags &= ~SI_ALIAS;
  
 +	if (dev->si_refcount == 0 && dev->si_priv->cdp_inuse > 0) {
 +		/* devfs_populate_loop() will free this cdev structure */
 +		dev->si_refcount++;
 +	}
  	if (dev->si_refcount > 0) {
  		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
  	} else {
 
 
  I suspect the situation that the cdev structure is freed
  while it is referred to by devfs.

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: Robert Watson <rwatson@FreeBSD.org>
Cc: freebsd-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Fri, 07 Jul 2006 20:39:29 +0900

 I writes:
 > Robert Watson writes:
 >> ...
 >> The tty_pts implementation in 7-CURRENT does the same, except more so, by 
 >> forcing revocation of the pty on last close.  Unfortunately, this triggers 
 >> bugs in devfs.  Your help in getting the 7.x pts implementation up and
 >> running would be much appreciated.  Take a look at src/sys/kern/tty_pts.c.
 > 
 >  Thank you for interested in my patch.
 > 
 >  I don't have a machine to run 7-current now,
 
  Now I have a machine running 7-current (cvs tag HEAD).
 
  Could you give me a pointer to how to trigger bugs in devfs
  releated with pts implementation?

From: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>
To: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
Cc: Robert Watson <rwatson@FreeBSD.org>, freebsd-gnats-submit@FreeBSD.org,
        freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Fri, 7 Jul 2006 16:56:43 +0000

 On Fri, Jul 07, 2006 at 08:39:29PM +0900, Atsuo Ohki wrote:
 > I writes:
 > > Robert Watson writes:
 > >> ...
 > >> The tty_pts implementation in 7-CURRENT does the same, except more so, by 
 > >> forcing revocation of the pty on last close.  Unfortunately, this triggers 
 > >> bugs in devfs.  Your help in getting the 7.x pts implementation up and
 > >> running would be much appreciated.  Take a look at src/sys/kern/tty_pts.c.
 > > 
 > >  Thank you for interested in my patch.
 > > 
 > >  I don't have a machine to run 7-current now,
 > 
 >  Now I have a machine running 7-current (cvs tag HEAD).
 > 
 >  Could you give me a pointer to how to trigger bugs in devfs
 >  releated with pts implementation?
 
 Hello,
 
 Sure. I'm willing to hear more about your changes and patches! To reproduce
 problems I've seen, try to download Peter Wemm's stress suite, compile it,
 and run PTY code. As I recall, after unpacking stress2.tgz you'll have
 run.sh script and pty<some_extension>. You run it by typing: ./run
 ./pty<some_extension_maybe_conf>. Try to switch to other virtual terminal
 and login.
 
 For debugging stuff, where I liked to put printf()s and KASSERTs here and
 there, I used window(1), because it doesn't bomp you with tons of function
 calls, and creates (AFAICT) 2 vTTYs.
 
 Let me know if you find something,
 -- 
 Wojciech A. Koszek
 wkoszek@FreeBSD.org
 http://FreeBSD.czest.pl/dunstan/

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>
Cc: Robert Watson <rwatson@FreeBSD.org>, freebsd-gnats-submit@FreeBSD.org,
        freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Sun, 09 Jul 2006 22:19:42 +0900

 "Wojciech A. Koszek" writes:
 > Sure. I'm willing to hear more about your changes and patches! To reproduce
 > problems I've seen, try to download Peter Wemm's stress suite, compile it,
 > and run PTY code. As I recall, after unpacking stress2.tgz you'll have
 > run.sh script and pty<some_extension>. You run it by typing: ./run
 > ./pty<some_extension_maybe_conf>. Try to switch to other virtual terminal
 > and login.
 
  I got stress2.tgz and done `./run.sh pty.cfg' and got the message like
 
 	Memory modified after free ...
 	Most recently used by DEVFS1
 
  The reason for this panic is devfs_close() in fs/devfs/devfs_vnops.c.
  As you see, devfs_close() eventually calls ptcclose()/ptsclose()
  which calls pty_maybecleanup() destroying devs for ptc&pts, but
  devfs_close() then calls dev_relthread() which may access just freeed dev.
 
  I'm afraid that devfs is not designed to handle destroing dev during
  close operation.
 
  I'm working on this problem with the idea:
   i) destory_dev() should not free dev, but just mark inactive.
   ii) devfs_populate() should actually free an inactive dev.
   iii) modify devfs_find() and other routines to take care of an inactive dev.
  But no success yet ;-<

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>
Cc: Robert Watson <rwatson@FreeBSD.org>, freebsd-gnats-submit@FreeBSD.org,
        freebsd-bugs@FreeBSD.org
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Thu, 13 Jul 2006 18:15:02 +0900

 I wrote:
 >  I got stress2.tgz and done `./run.sh pty.cfg' and got the message like
 > 
 > 	Memory modified after free ...
 > 	Most recently used by DEVFS1
 > 
 >  The reason for this panic is devfs_close() in fs/devfs/devfs_vnops.c.
 >  As you see, devfs_close() eventually calls ptcclose()/ptsclose()
 >  which calls pty_maybecleanup() destroying devs for ptc&pts, but
 >  devfs_close() then calls dev_relthread() which may access just freeed dev.
 > 
 >  I'm afraid that devfs is not designed to handle destroing dev during
 >  close operation.
 > 
 >  I'm working on this problem with the idea:
 >   i) destory_dev() should not free dev, but just mark inactive.
 >   ii) devfs_populate() should actually free an inactive dev.
 >   iii) modify devfs_find() and other routines to take care of an inactive dev
 > .
 >  But no success yet ;-<
 
  I achieved a little success.
  Now (really now!),  pty test of stress2 is running.
 
  I modified as follow:
  
  i) destroy_dev() in cdevs' close routine is not appropriate.
     I introduced hide_dev() (in kern/kern_conf.c) and
     devfs_hide() (in fs/devfs/devfs_devs.c) to make dev invisible 
     from userland. (via devfs_find(), devfs_readdir() and so on.)
 
  ii) pty_maybecleanup() no longer calls destroy_dev(), but
      calls hide_dev() to make pts/ptc invisible.
      when both of pts/ptc are closed, link them to pt_free_list
      as usual (structure dev for ptc/pts are not destroyied!).
 
      pty_new() now takes care of destorying dev for ptc/pts.
      when a new ptc/pts is requested, search pt_free_list to
      find a devs' which are free!(i.e. si_usecount == 0).
      if found, destroy existing devs.
      if not found, things goes by as before.
 
      I use the name ptsXX, ptcXX instead of pts/XX, ptc/XX.
      (original naming causes system hungup related to vnode operation.
       I must solve this problem.)

From: Ian Dowse <iedowse@iedowse.com>
To: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
Cc: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>,
    freebsd-gnats-submit@FreeBSD.org, Robert Watson <rwatson@FreeBSD.org>
Subject: Re: kern/99758: chown/chmod pty slave side in kernel 
Date: Fri, 14 Jul 2006 10:45:44 +0100

 In message <200607130915.k6D9F2eg054212@smr00.gssm.otsuka.tsukuba.ac.jp>, Atsuo
  Ohki writes:
 > I achieved a little success.
 > Now (really now!),  pty test of stress2 is running.
 >
 > I modified as follow:
 
 I'm not sure if it is related, but the following old patch had
 solved some issue I was seeing with getty running on USB serial
 ports. Maybe it is a similar issue?
 
 Ian
 
 Index: kern_conf.c
 ===================================================================
 RCS file: /dump/FreeBSD-CVS/src/sys/kern/kern_conf.c,v
 retrieving revision 1.197
 diff -u -r1.197 kern_conf.c
 --- kern_conf.c	12 Jan 2006 19:15:14 -0000	1.197
 +++ kern_conf.c	3 Feb 2006 00:43:20 -0000
 @@ -658,6 +658,7 @@
  		dev->si_flags &= ~SI_CLONELIST;
  	}
  
 +	dev_refl(dev);	/* another thread might dev_rel() while we sleep. */
  	csw = dev->si_devsw;
  	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
  	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
 @@ -683,10 +684,15 @@
  	}
  	dev->si_flags &= ~SI_ALIAS;
  
 -	if (dev->si_refcount > 0) {
 +	if (dev->si_refcount > 1) {
 +		dev->si_refcount--;
  		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
  	} else {
 +		KASSERT(dev->si_refcount == 1,
 +		    ("destroy_devl unexpected refcount %d", dev->si_refcount));
 +		dev_unlock();
  		devfs_free(dev);
 +		dev_lock();
  	}
  }
  

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>,
        freebsd-gnats-submit@FreeBSD.org, freebsd-bugs@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);

From: Atsuo Ohki <ohki@gssm.otsuka.tsukuba.ac.jp>
To: "Wojciech A. Koszek" <wkoszek@FreeBSD.org>,
        freebsd-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: kern/99758: chown/chmod pty slave side in kernel
Date: Sun, 23 Jul 2006 22:25:33 +0900

 I wrote:
 >  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.
 
  It passed 3-day long pty stress test successfully :-)
State-Changed-From-To: open->closed 
State-Changed-By: jilles 
State-Changed-When: Sun May 2 21:59:35 UTC 2010 
State-Changed-Why:  
The new TTY layer in 8.0 does this, but will not be backported to 7.x. 
It seems unlikely that such a complicated change will be re-done 
differently on 7.x. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=99758 
>Unformatted:
