diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 index 07505cde48d5..a08dc49e38d5 100644 --- a/share/man/man4/ddb.4 +++ b/share/man/man4/ddb.4 @@ -540,6 +540,13 @@ modifier will alter the display to show VM map 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 diff --git a/sys/kern/tty.c b/sys/kern/tty.c index e31d0702b8bb..ef9818bff46b 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -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, int devtype, struct thread *td) done: tp->t_flags &= ~TF_OPENCLOSE; ttydev_leave(tp); + return (error); } @@ -377,23 +381,24 @@ ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) int error; error = ttydev_enter(tp); - if (error) - return (0); - - error = tty_wait_background(tp, curthread, SIGTTIN); if (error) goto done; + error = tty_wait_background(tp, curthread, SIGTTIN); + 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 uio *uio, int ioflag) 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 cmd, caddr_t data, int fflag, } 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, struct thread *td) selrecord(td, &tp->t_outpoll); } - ttydev_leave(tp); + tty_unlock(tp); return (revents); } @@ -535,7 +535,7 @@ ttydev_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 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 knote *kn) 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 *cv, int hz) 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, struct thread *td, int fd, 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 +#include 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 \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, diff --git a/sys/sys/tty.h b/sys/sys/tty.h index f01ab70b8382..9c4e956ae129 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -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. */