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:
parent
b739742ca9
commit
f6dd5c15fd
@ -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
|
||||
|
203
sys/kern/tty.c
203
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 <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,
|
||||
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user