Date: Wed, 15 Oct 2008 16:58:36 +0000 (UTC) From: Ed Schouten <ed@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r183922 - in head: share/man/man4 sys/kern sys/sys Message-ID: <200810151658.m9FGwaYZ038854@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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. */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200810151658.m9FGwaYZ038854>