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
This commit is contained in:
Ed Schouten 2008-10-15 16:58:35 +00:00
parent b739742ca9
commit f6dd5c15fd
3 changed files with 174 additions and 43 deletions

View File

@ -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

View File

@ -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 <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,

View File

@ -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. */