svn commit: r183922 - in head: share/man/man4 sys/kern sys/sys

Ed Schouten ed at FreeBSD.org
Wed Oct 15 16:58:36 UTC 2008


Author: ed
Date: Wed Oct 15 16:58:35 2008
New Revision: 183922
URL: http://svn.freebsd.org/changeset/base/183922

Log:
  Import some improvements to the TTY code from the MPSAFE TTY branch.
  
  - Change the ddb(4) commands to be more useful (by thompsa@):
    - `show ttys' is now called `show all ttys'. This command will now
      also display the address where the TTY data structure resides.
    - Add `show tty <addr>', which dumps the TTY in a readable form.
  
  - Place an upper bound on the TTY buffer sizes. Some drivers do not want
    to care about baud rates. Protect these drivers by preventing the TTY
    buffers from getting enormous. Right now we'll just clamp it to 64K,
    which is pretty high, taking into account that these buffers are only
    used by the built-in discipline.
  
  - Only call ttydev_leave() when needed. Back in April/May the TTY
    reference counting mechanism was a little different, which required us
    to call ttydev_leave() each time we finished a cdev operation.
    Nowadays we only need to call ttydev_leave() when we really mark it as
    being closed.
  
  - Improve return codes of read() and write() on TTY device nodes.
  
  - Make sure we really wake up all blocked threads when the driver calls
    tty_rel_gone(). There were some possible code paths where we didn't
    properly wake up any readers/writers.
  
  - Add extra assertions to prevent sleeping on a TTY that has been
    abandoned by the driver.
  
  - Use ttydev_cdevsw as a more reliable method to figure out whether a
    device node is a real TTY device node.
  
  Obtained from:	//depot/projects/mpsafetty/...
  Reviewed by:	thompsa

Modified:
  head/share/man/man4/ddb.4
  head/sys/kern/tty.c
  head/sys/sys/tty.h

Modified: head/share/man/man4/ddb.4
==============================================================================
--- head/share/man/man4/ddb.4	Wed Oct 15 15:54:33 2008	(r183921)
+++ head/share/man/man4/ddb.4	Wed Oct 15 16:58:35 2008	(r183922)
@@ -540,6 +540,13 @@ modifier will alter the display to show 
 addresses for the process and not show other information.
 .\"
 .Pp
+.It Ic show Cm all ttys
+Show all TTY's within the system.
+Output is similar to
+.Xr pstat 8 ,
+but also includes the address of the TTY structure.
+.\"
+.Pp
 .It Ic show Cm allchains
 Show the same information like "show lockchain" does, but
 for every thread in the system.
@@ -963,10 +970,8 @@ Backtrace.
 .El
 .\"
 .Pp
-.It Ic show Cm ttys
-Show all TTY's within the system.
-Output is similar to
-.Xr pstat 8 .
+.It Ic show Cm tty Ar addr
+Display the contents of a TTY structure in a readable form.
 .\"
 .Pp
 .It Ic show Cm turnstile Ar addr

Modified: head/sys/kern/tty.c
==============================================================================
--- head/sys/kern/tty.c	Wed Oct 15 15:54:33 2008	(r183921)
+++ head/sys/kern/tty.c	Wed Oct 15 16:58:35 2008	(r183922)
@@ -92,21 +92,23 @@ static unsigned int tty_list_count = 0;
  * Set TTY buffer sizes.
  */
 
+#define	TTYBUF_MAX	65536
+
 static void
 tty_watermarks(struct tty *tp)
 {
-	speed_t sp;
+	size_t bs;
 
 	/* Provide an input buffer for 0.2 seconds of data. */
-	sp = MAX(tp->t_termios.c_ispeed, 0);
-	ttyinq_setsize(&tp->t_inq, tp, sp / 5);
+	bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX);
+	ttyinq_setsize(&tp->t_inq, tp, bs);
 
 	/* Set low watermark at 10% (when 90% is available). */
 	tp->t_inlow = (ttyinq_getsize(&tp->t_inq) * 9) / 10;
 
 	/* Provide an ouput buffer for 0.2 seconds of data. */
-	sp = MAX(tp->t_termios.c_ospeed, 0);
-	ttyoutq_setsize(&tp->t_outq, tp, sp / 5);
+	bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX);
+	ttyoutq_setsize(&tp->t_outq, tp, bs);
 
 	/* Set low watermark at 10% (when 90% is available). */
 	tp->t_outlow = (ttyoutq_getsize(&tp->t_outq) * 9) / 10;
@@ -133,11 +135,12 @@ tty_drain(struct tty *tp)
 }
 
 /*
- * Because the revoke() call already calls d_close() without making sure
- * all threads are purged from the TTY, we can only destroy the buffers
- * and such when the last thread leaves the TTY. ttydev_enter() and
- * ttydev_leave() are called from within the cdev functions, to make
- * sure we can garbage collect the TTY.
+ * Though ttydev_enter() and ttydev_leave() seem to be related, they
+ * don't have to be used together. ttydev_enter() is used by the cdev
+ * operations to prevent an actual operation from being processed when
+ * the TTY has been abandoned. ttydev_leave() is used by ttydev_open()
+ * and ttydev_close() to determine whether per-TTY data should be
+ * deallocated.
  */
 
 static __inline int
@@ -287,6 +290,7 @@ ttydev_open(struct cdev *dev, int oflags
 
 done:	tp->t_flags &= ~TF_OPENCLOSE;
 	ttydev_leave(tp);
+
 	return (error);
 }
 
@@ -378,22 +382,23 @@ ttydev_read(struct cdev *dev, struct uio
 
 	error = ttydev_enter(tp);
 	if (error)
-		return (0);
+		goto done;
 
 	error = tty_wait_background(tp, curthread, SIGTTIN);
-	if (error)
+	if (error) {
+		tty_unlock(tp);
 		goto done;
+	}
 
 	error = ttydisc_read(tp, uio, ioflag);
-done:	ttydev_leave(tp);
+	tty_unlock(tp);
 
 	/*
-	 * The read() and write() calls should not throw an error when
-	 * the device is ripped offline.
+	 * The read() call should not throw an error when the device is
+	 * being destroyed. Silently convert it to an EOF.
 	 */
-	if (error == ENXIO)
-		return (0);
-
+done:	if (error == ENXIO)
+		error = 0;
 	return (error);
 }
 
@@ -405,23 +410,18 @@ ttydev_write(struct cdev *dev, struct ui
 
 	error = ttydev_enter(tp);
 	if (error)
-		return (0);
+		return (error);
 
 	if (tp->t_termios.c_lflag & TOSTOP) {
 		error = tty_wait_background(tp, curthread, SIGTTOU);
-		if (error)
-			goto done;
+		if (error) {
+			tty_unlock(tp);
+			return (error);
+		}
 	}
 
 	error = ttydisc_write(tp, uio, ioflag);
-done:	ttydev_leave(tp);
-
-	/*
-	 * The read() and write() calls should not throw an error when
-	 * the device is ripped offline.
-	 */
-	if (error == ENXIO)
-		return (0);
+	tty_unlock(tp);
 
 	return (error);
 }
@@ -479,7 +479,7 @@ ttydev_ioctl(struct cdev *dev, u_long cm
 	}
 
 	error = tty_ioctl(tp, cmd, data, td);
-done:	ttydev_leave(tp);
+done:	tty_unlock(tp);
 
 	return (error);
 }
@@ -518,7 +518,7 @@ ttydev_poll(struct cdev *dev, int events
 			selrecord(td, &tp->t_outpoll);
 	}
 
-	ttydev_leave(tp);
+	tty_unlock(tp);
 
 	return (revents);
 }
@@ -535,7 +535,7 @@ ttydev_mmap(struct cdev *dev, vm_offset_
 	if (error)
 		return (-1);
 	error = ttydevsw_mmap(tp, offset, paddr, nprot);
-	ttydev_leave(tp);
+	tty_unlock(tp);
 
 	return (error);
 }
@@ -623,7 +623,7 @@ ttydev_kqfilter(struct cdev *dev, struct
 		break;
 	}
 
-	ttydev_leave(tp);
+	tty_unlock(tp);
 	return (error);
 }
 
@@ -973,7 +973,8 @@ tty_rel_gone(struct tty *tp)
 	/* Simulate carrier removal. */
 	ttydisc_modem(tp, 0);
 
-	/* Wake up misc. blocked threads. */
+	/* Wake up all blocked threads. */
+	tty_wakeup(tp, FREAD|FWRITE);
 	cv_broadcast(&tp->t_bgwait);
 	cv_broadcast(&tp->t_dcdwait);
 
@@ -1189,6 +1190,7 @@ tty_wait(struct tty *tp, struct cv *cv)
 	tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
 #endif
 	tty_lock_assert(tp, MA_OWNED);
+	MPASS(!tty_gone(tp));
 
 	error = cv_wait_sig(cv, tp->t_mtx);
 
@@ -1214,6 +1216,7 @@ tty_timedwait(struct tty *tp, struct cv 
 	tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED);
 #endif
 	tty_lock_assert(tp, MA_OWNED);
+	MPASS(!tty_gone(tp));
 
 	error = cv_timedwait_sig(cv, tp->t_mtx, hz);
 
@@ -1690,7 +1693,7 @@ ttyhook_register(struct tty **rtp, struc
 	cdp = dev_refthread(dev);
 	if (cdp == NULL)
 		goto done1;
-	if ((cdp->d_flags & D_TTY) == 0)
+	if (cdp != &ttydev_cdevsw)
 		goto done2;
 	tp = dev->si_drv1;
 
@@ -1741,6 +1744,7 @@ ttyhook_unregister(struct tty *tp)
 #include "opt_ddb.h"
 #ifdef DDB
 #include <ddb/ddb.h>
+#include <ddb/db_sym.h>
 
 static struct {
 	int flag;
@@ -1776,14 +1780,134 @@ static struct {
 	{ 0,	       '\0' },
 };
 
+#define	TTY_FLAG_BITS \
+	"\20\1NOPREFIX\2INITLOCK\3CALLOUT\4OPENED_IN\5OPENED_OUT\6GONE" \
+	"\7OPENCLOSE\10ASYNC\11LITERAL\12HIWAT_IN\13HIWAT_OUT\14STOPPED" \
+	"\15EXCLUDE\16BYPASS\17ZOMBIE\20HOOK"
+
+#define DB_PRINTSYM(name, addr) \
+	db_printf("%s  " #name ": ", sep); \
+	db_printsym((db_addr_t) addr, DB_STGY_ANY); \
+	db_printf("\n");
+
+static void
+_db_show_devsw(const char *sep, const struct ttydevsw *tsw)
+{
+	db_printf("%sdevsw: ", sep);
+	db_printsym((db_addr_t)tsw, DB_STGY_ANY);
+	db_printf(" (%p)\n", tsw);
+	DB_PRINTSYM(open, tsw->tsw_open);
+	DB_PRINTSYM(close, tsw->tsw_close);
+	DB_PRINTSYM(outwakeup, tsw->tsw_outwakeup);
+	DB_PRINTSYM(inwakeup, tsw->tsw_inwakeup);
+	DB_PRINTSYM(ioctl, tsw->tsw_ioctl);
+	DB_PRINTSYM(param, tsw->tsw_param);
+	DB_PRINTSYM(modem, tsw->tsw_modem);
+	DB_PRINTSYM(mmap, tsw->tsw_mmap);
+	DB_PRINTSYM(pktnotify, tsw->tsw_pktnotify);
+	DB_PRINTSYM(free, tsw->tsw_free);
+}
+static void
+_db_show_hooks(const char *sep, const struct ttyhook *th)
+{
+	db_printf("%shook: ", sep);
+	db_printsym((db_addr_t)th, DB_STGY_ANY);
+	db_printf(" (%p)\n", th);
+	if (th == NULL)
+		return;
+	DB_PRINTSYM(rint, th->th_rint);
+	DB_PRINTSYM(rint_bypass, th->th_rint_bypass);
+	DB_PRINTSYM(rint_done, th->th_rint_done);
+	DB_PRINTSYM(rint_poll, th->th_rint_poll);
+	DB_PRINTSYM(getc_inject, th->th_getc_inject);
+	DB_PRINTSYM(getc_capture, th->th_getc_capture);
+	DB_PRINTSYM(getc_poll, th->th_getc_poll);
+	DB_PRINTSYM(close, th->th_close);
+}
+
+static void
+_db_show_termios(const char *name, const struct termios *t)
+{
+
+	db_printf("%s: iflag 0x%x oflag 0x%x cflag 0x%x "
+	    "lflag 0x%x ispeed %u ospeed %u\n", name,
+	    t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag,
+	    t->c_ispeed, t->c_ospeed);
+}
+
 /* DDB command to show TTY statistics. */
-DB_SHOW_COMMAND(ttys, db_show_ttys)
+DB_SHOW_COMMAND(tty, db_show_tty)
+{
+	struct tty *tp;
+
+	if (!have_addr) {
+		db_printf("usage: show tty <addr>\n");
+		return;
+	}
+	tp = (struct tty *)addr;
+
+	db_printf("0x%p: %s\n", tp, tty_devname(tp));
+	db_printf("\tmtx: %p\n", tp->t_mtx);
+	db_printf("\tflags: %b\n", tp->t_flags, TTY_FLAG_BITS);
+	db_printf("\trevokecnt: %u\n", tp->t_revokecnt);
+
+	/* Buffering mechanisms. */
+	db_printf("\tinq: %p begin %u linestart %u reprint %u end %u "
+	    "nblocks %u quota %u\n", &tp->t_inq, tp->t_inq.ti_begin,
+	    tp->t_inq.ti_linestart, tp->t_inq.ti_reprint, tp->t_inq.ti_end,
+	    tp->t_inq.ti_nblocks, tp->t_inq.ti_quota);
+	db_printf("\toutq: %p begin %u end %u nblocks %u quota %u\n",
+	    &tp->t_outq, tp->t_outq.to_begin, tp->t_outq.to_end,
+	    tp->t_outq.to_nblocks, tp->t_outq.to_quota);
+	db_printf("\tinlow: %zu\n", tp->t_inlow);
+	db_printf("\toutlow: %zu\n", tp->t_outlow);
+	_db_show_termios("\ttermios", &tp->t_termios);
+	db_printf("\twinsize: row %u col %u xpixel %u ypixel %u\n",
+	    tp->t_winsize.ws_row, tp->t_winsize.ws_col,
+	    tp->t_winsize.ws_xpixel, tp->t_winsize.ws_ypixel);
+	db_printf("\tcolumn: %u\n", tp->t_column);
+	db_printf("\twritepos: %u\n", tp->t_writepos);
+	db_printf("\tcompatflags: 0x%x\n", tp->t_compatflags);
+
+	/* Init/lock-state devices. */
+	_db_show_termios("\ttermios_init_in", &tp->t_termios_init_in);
+	_db_show_termios("\ttermios_init_out", &tp->t_termios_init_out);
+	_db_show_termios("\ttermios_lock_in", &tp->t_termios_lock_in);
+	_db_show_termios("\ttermios_lock_out", &tp->t_termios_lock_out);
+
+	/* Hooks */
+	_db_show_devsw("\t", tp->t_devsw);
+	_db_show_hooks("\t", tp->t_hook);
+
+	/* Process info. */
+	db_printf("\tpgrp: %p gid %d jobc %d\n", tp->t_pgrp,
+	    tp->t_pgrp ? tp->t_pgrp->pg_id : 0,
+	    tp->t_pgrp ? tp->t_pgrp->pg_jobc : 0);
+	db_printf("\tsession: %p", tp->t_session);
+	if (tp->t_session != NULL)
+	    db_printf(" count %u leader %p tty %p sid %d login %s",
+		tp->t_session->s_count, tp->t_session->s_leader,
+		tp->t_session->s_ttyp, tp->t_session->s_sid,
+		tp->t_session->s_login);
+	db_printf("\n");
+	db_printf("\tsessioncnt: %u\n", tp->t_sessioncnt);
+	db_printf("\tdevswsoftc: %p\n", tp->t_devswsoftc);
+	db_printf("\thooksoftc: %p\n", tp->t_hooksoftc);
+	db_printf("\tdev: %p\n", tp->t_dev);
+}
+
+/* DDB command to list TTYs. */
+DB_SHOW_ALL_COMMAND(ttys, db_show_all_ttys)
 {
 	struct tty *tp;
 	size_t isiz, osiz;
 	int i, j;
 
 	/* Make the output look like `pstat -t'. */
+	db_printf("PTR        ");
+#if defined(__LP64__)
+	db_printf("        ");
+#endif
 	db_printf("      LINE   INQ  CAN  LIN  LOW  OUTQ  USE  LOW   "
 	    "COL  SESS  PGID STATE\n");
 
@@ -1791,7 +1915,8 @@ DB_SHOW_COMMAND(ttys, db_show_ttys)
 		isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE;
 		osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE;
 
-		db_printf("%10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d %5d ",
+		db_printf("%p %10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d %5d ",
+		    tp,
 		    tty_devname(tp),
 		    isiz,
 		    tp->t_inq.ti_linestart - tp->t_inq.ti_begin,

Modified: head/sys/sys/tty.h
==============================================================================
--- head/sys/sys/tty.h	Wed Oct 15 15:54:33 2008	(r183921)
+++ head/sys/sys/tty.h	Wed Oct 15 16:58:35 2008	(r183922)
@@ -63,6 +63,7 @@ struct tty {
 	struct mtx	t_mtxobj;	/* Per-TTY lock (when not borrowing). */
 	TAILQ_ENTRY(tty) t_list;	/* (l) TTY list entry. */
 	unsigned int	t_flags;	/* (t) Terminal option flags. */
+/* Keep flags in sync with db_show_tty and pstat(8). */
 #define	TF_NOPREFIX	0x0001	/* Don't prepend "tty" to device name. */
 #define	TF_INITLOCK	0x0002	/* Create init/lock state devices. */
 #define	TF_CALLOUT	0x0004	/* Create "cua" devices. */


More information about the svn-src-head mailing list