[TEST/REVIEW] tty reference counting patch

From: Poul-Henning Kamp <phk_at_phk.freebsd.dk>
Date: Tue, 08 Jun 2004 15:42:22 +0200
This patch adds reference counting to struct tty and adds the ability
to free a struct tty back to the system again.

The PTY driver has been modified to use this.

I have left in two annoying printfs which track the reference counts
per tty.  Just remove them when you tire of them.  If you provoke a
problem with this patch I'll be interested in the output from them
however.

Poul-Henning

Index: kern/kern_proc.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_proc.c,v
retrieving revision 1.203
diff -u -r1.203 kern_proc.c
--- kern/kern_proc.c	22 May 2004 23:11:44 -0000	1.203
+++ kern/kern_proc.c	8 Jun 2004 12:42:43 -0000
_at__at_ -469,6 +469,8 _at__at_
 	SESS_UNLOCK(savesess);
 	PGRP_UNLOCK(pgrp);
 	if (savesess->s_count == 0) {
+		if (savesess->s_ttyp != NULL)
+			ttyrel(savesess->s_ttyp);
 		mtx_destroy(&savesess->s_mtx);
 		FREE(pgrp->pg_session, M_SESSION);
 	}
Index: kern/tty.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/tty.c,v
retrieving revision 1.216
diff -u -r1.216 tty.c
--- kern/tty.c	7 Jun 2004 20:45:45 -0000	1.216
+++ kern/tty.c	8 Jun 2004 12:46:47 -0000
_at__at_ -212,7 +212,8 _at__at_
 /*
  * list of struct tty where pstat(8) can pick it up with sysctl
  */
-static SLIST_HEAD(, tty) tty_list;
+static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER(&tty_list);
+static struct mtx tty_list_mutex;
 
 static int  drainwait = 5*60;
 SYSCTL_INT(_kern, OID_AUTO, drainwait, CTLFLAG_RW, &drainwait,
_at__at_ -229,6 +230,7 _at__at_
 	s = spltty();
 	tp->t_dev = device;
 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
+		ttyref(tp);
 		SET(tp->t_state, TS_ISOPEN);
 		if (ISSET(tp->t_cflag, CLOCAL))
 			SET(tp->t_state, TS_CONNECTED);
_at__at_ -270,6 +272,7 _at__at_
 	tp->t_pgrp = NULL;
 	tp->t_session = NULL;
 	tp->t_state = 0;
+	ttyrel(tp);
 	splx(s);
 	return (0);
 }
_at__at_ -1065,6 +1068,7 _at__at_
 		tp->t_session = p->p_session;
 		tp->t_pgrp = p->p_pgrp;
 		SESS_LOCK(p->p_session);
+		ttyref(tp);
 		p->p_session->s_ttyp = tp;
 		SESS_UNLOCK(p->p_session);
 		PROC_LOCK(p);
_at__at_ -2631,22 +2635,67 _at__at_
 }
 
 /*
+ * Gain a reference to a TTY
+ */
+int
+ttyref(struct tty *tp)
+{
+	int i;
+	
+	mtx_lock(&tp->t_mtx);
+	KASSERT(tp->t_refcnt > 0,
+	    ("ttyref(): tty refcnt is %d (%s)",
+	    tp->t_refcnt, devtoname(tp->t_dev)));
+	i = ++tp->t_refcnt;
+	printf("ttyref(%s -> %d)\n", devtoname(tp->t_dev), tp->t_refcnt);
+	mtx_unlock(&tp->t_mtx);
+	return (i);
+}
+
+/*
+ * Drop a reference to a TTY
+ */
+int
+ttyrel(struct tty *tp)
+{
+	int i;
+	
+	mtx_lock(&tp->t_mtx);
+	KASSERT(tp->t_refcnt > 0,
+	    ("ttyrel(): tty refcnt is %d (%s)",
+	    tp->t_refcnt, devtoname(tp->t_dev)));
+	i = tp->t_refcnt--;
+	printf("ttyrel(%s -> %d)\n", devtoname(tp->t_dev), tp->t_refcnt);
+	mtx_unlock(&tp->t_mtx);
+	return (i);
+}
+
+/*
  * Allocate a tty struct.  Clists in the struct will be allocated by
  * ttyopen().
  */
 struct tty *
 ttymalloc(struct tty *tp)
 {
+	static int once;
+
+	if (!once) {
+		mtx_init(&tty_list_mutex, "ttylist", NULL, MTX_DEF);
+		once++;
+	}
 
 	if (tp)
 		return(tp);
 	tp = malloc(sizeof *tp, M_TTYS, M_WAITOK | M_ZERO);
 	tp->t_timeout = -1;
-	SLIST_INSERT_HEAD(&tty_list, tp, t_list);
+	mtx_init(&tp->t_mtx, "tty", NULL, MTX_DEF);
+	tp->t_refcnt = 1;
+	mtx_lock(&tty_list_mutex);
+	LIST_INSERT_HEAD(&tty_list, tp, t_list);
+	mtx_unlock(&tty_list_mutex);
 	return (tp);
 }
 
-#if 0 /* XXX not yet usable: session leader holds a ref (see kern_exit.c). */
 /*
  * Free a tty struct.  Clists in the struct should have been freed by
  * ttyclose().
_at__at_ -2654,9 +2703,15 _at__at_
 void
 ttyfree(struct tty *tp)
 {
+
+	if (ttyrel(tp) != 0)
+		return;
+	mtx_lock(&tty_list_mutex);
+	LIST_REMOVE(tp, t_list);
+	mtx_unlock(&tty_list_mutex);
+	mtx_destroy(&tp->t_mtx);
 	free(tp, M_TTYS);
 }
-#endif /* 0 */
 
 static int
 sysctl_kern_ttys(SYSCTL_HANDLER_ARGS)
_at__at_ -2665,7 +2720,7 _at__at_
 	struct xtty xt;
 	int error;
 
-	SLIST_FOREACH(tp, &tty_list, t_list) {
+	LIST_FOREACH(tp, &tty_list, t_list) {
 		bzero(&xt, sizeof xt);
 		xt.xt_size = sizeof xt;
 #define XT_COPY(field) xt.xt_##field = tp->t_##field
Index: kern/tty_pty.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/tty_pty.c,v
retrieving revision 1.119
diff -u -r1.119 tty_pty.c
--- kern/tty_pty.c	4 Jun 2004 16:02:56 -0000	1.119
+++ kern/tty_pty.c	8 Jun 2004 13:35:55 -0000
_at__at_ -61,7 +61,6 _at__at_
 static void ptsstart(struct tty *tp);
 static void ptsstop(struct tty *tp, int rw);
 static void ptcwakeup(struct tty *tp, int flag);
-static dev_t ptyinit(dev_t cdev);
 
 static	d_open_t	ptsopen;
 static	d_close_t	ptsclose;
_at__at_ -98,9 +97,11 _at__at_
 	.d_poll =	ptcpoll,
 	.d_name =	"ptc",
 	.d_maj =	CDEV_MAJOR_C,
-	.d_flags =	D_TTY | D_NEEDGIANT,
+	.d_flags =	D_NEEDGIANT,
 };
 
+static char *names = "pqrsPQRS";
+
 #define BUFSIZ 100		/* Chunk size iomoved to/from user */
 
 struct	pt_ioctl {
_at__at_ -119,42 +120,6 _at__at_
 #define	PF_NOSTOP	0x40
 #define PF_UCNTL	0x80		/* user control mode */
 
-static char *names = "pqrsPQRS";
-/*
- * This function creates and initializes a pts/ptc pair
- *
- * pts == /dev/tty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv]
- * ptc == /dev/pty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv]
- *
- * XXX: define and add mapping of upper minor bits to allow more
- *      than 256 ptys.
- */
-static dev_t
-ptyinit(dev_t devc)
-{
-	dev_t devs;
-	struct pt_ioctl *pt;
-	int n;
-
-	n = minor(devc);
-	/* For now we only map the lower 8 bits of the minor */
-	if (n & ~0xff)
-		return (NODEV);
-
-	devc->si_flags &= ~SI_CHEAPCLONE;
-
-	pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO);
-	pt->devs = devs = make_dev(&pts_cdevsw, n,
-	    UID_ROOT, GID_WHEEL, 0666, "tty%c%r", names[n / 32], n % 32);
-	pt->devc = devc;
-
-	pt->pt_tty = ttymalloc(pt->pt_tty);
-	devs->si_drv1 = devc->si_drv1 = pt;
-	devs->si_tty = devc->si_tty = pt->pt_tty;
-	pt->pt_tty->t_dev = devs;
-	return (devc);
-}
-
 /*ARGSUSED*/
 static	int
 ptsopen(dev, flag, devtype, td)
_at__at_ -328,6 +293,7 _at__at_
 	}
 }
 
+
 static	int
 ptcopen(dev, flag, devtype, td)
 	dev_t dev;
_at__at_ -335,25 +301,42 _at__at_
 	struct thread *td;
 {
 	struct tty *tp;
-	struct pt_ioctl *pti;
+	struct pt_ioctl *pt;
+	u_int n;
+
+	if (dev->si_drv1 != NULL)
+		return (EIO);	/* XXX: EBUSY ?? */
+
+	n = minor(dev);
+
+	/* For now we only map the lower 8 bits of the minor */
+	if (n & ~0xff)
+		return (ENXIO);
+
+	pt = malloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO);
+	pt->devs = make_dev(&pts_cdevsw, n,
+	    UID_ROOT, GID_WHEEL, 0666, "tty%c%r",
+	    names[n / 32], n % 32);
+	pt->devc = dev;
+	pt->devs->si_drv1 = pt;
+	pt->devc->si_drv1 = pt;
+
+	tp = ttymalloc(NULL);
+	pt->pt_tty = tp;
+	pt->devs->si_tty = pt->pt_tty;
+	pt->devc->si_tty = pt->pt_tty;
+	pt->pt_tty->t_dev = pt->devs;
 
-	if (!dev->si_drv1)
-		ptyinit(dev);
-	if (!dev->si_drv1)
-		return(ENXIO);
-	tp = dev->si_tty;
-	if (tp->t_oproc)
-		return (EIO);
 	tp->t_timeout = -1;
 	tp->t_oproc = ptsstart;
 	tp->t_stop = ptsstop;
 	(void)ttyld_modem(tp, 1);
 	tp->t_lflag &= ~EXTPROC;
-	pti = dev->si_drv1;
-	pti->pt_prison = td->td_ucred->cr_prison;
-	pti->pt_flags = 0;
-	pti->pt_send = 0;
-	pti->pt_ucntl = 0;
+
+	pt->pt_prison = td->td_ucred->cr_prison;
+	pt->pt_flags = 0;
+	pt->pt_send = 0;
+	pt->pt_ucntl = 0;
 	return (0);
 }
 
_at__at_ -365,8 +348,10 _at__at_
 	struct thread *td;
 {
 	struct tty *tp;
+	struct pt_ioctl *pt;
 
-	tp = dev->si_tty;
+	pt = dev->si_drv1;
+	tp = pt->pt_tty;
 	(void)ttyld_modem(tp, 0);
 
 	/*
_at__at_ -384,6 +369,10 _at__at_
 	}
 
 	tp->t_oproc = 0;		/* mark closed */
+	ttyfree(tp);
+	destroy_dev(pt->devs);
+	free(pt, M_PTY);
+	dev->si_drv1 = NULL;
 	return (0);
 }
 
Index: sys/tty.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/tty.h,v
retrieving revision 1.77
diff -u -r1.77 tty.h
--- sys/tty.h	4 Jun 2004 21:55:55 -0000	1.77
+++ sys/tty.h	8 Jun 2004 12:26:43 -0000
_at__at_ -49,6 +49,8 _at__at_
 #include <sys/termios.h>
 #include <sys/queue.h>
 #include <sys/selinfo.h>
+#include <sys/_lock.h>
+#include <sys/_mutex.h>
 
 /*
  * Clists are character lists, which is a variable length linked list
_at__at_ -110,7 +112,10 _at__at_
 	int	t_olowat;		/* Low water mark for output. */
 	speed_t	t_ospeedwat;		/* t_ospeed override for watermarks. */
 	int	t_gen;			/* Generation number. */
-	SLIST_ENTRY(tty) t_list;	/* Global chain of ttys for pstat(8) */
+	LIST_ENTRY(tty) t_list;		/* Global chain of ttys for pstat(8) */
+
+	struct mtx t_mtx;
+	int	t_refcnt;
 };
 
 #define	t_cc		t_termios.c_cc
_at__at_ -307,6 +312,8 _at__at_
 struct tty *ttymalloc(struct tty *tp);
 int	 ttymodem(struct tty *tp, int flag);
 int	 ttyopen(dev_t device, struct tty *tp);
+int	 ttyref(struct tty *tp);
+int	 ttyrel(struct tty *tp);
 int	 ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo);
 int	 ttywait(struct tty *tp);
 int	 unputc(struct clist *q);
-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
phk_at_FreeBSD.ORG         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe
Never attribute to malice what can adequately be explained by incompetence.
Received on Tue Jun 08 2004 - 11:42:24 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:56 UTC