From d29bf12ff8af22103581957d676732a09eec7e19 Mon Sep 17 00:00:00 2001 From: Ian Dowse Date: Sun, 22 Jun 2003 02:54:33 +0000 Subject: [PATCH] Use a new message buffer `consmsgbuf' to forward messages to a TIOCCONS console (e.g. xconsole) via a timeout routine instead of calling into the tty code directly from printf(). This fixes a number of cases where calling printf() at the wrong time (such as with locks held) would cause a panic if xconsole is running. The TIOCCONS message buffer is 8k in size by default, but this can be changed with the kern.consmsgbuf_size sysctl. By default, messages are checked for 5 times per second. The timer runs and the buffer memory remains allocated only at times when a TIOCCONS console is active. Discussed on: freebsd-arch --- sys/kern/subr_prf.c | 26 ++++++++--------- sys/kern/tty.c | 6 ++-- sys/kern/tty_cons.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ sys/sys/tty.h | 3 ++ 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c index d564cf6e5bf..e802e004731 100644 --- a/sys/kern/subr_prf.c +++ b/sys/kern/subr_prf.c @@ -89,9 +89,6 @@ struct snprintf_arg { extern int log_open; -struct tty *constty; /* pointer to console "window" tty */ - -static void (*v_putc)(int) = cnputc; /* routine to putc on virtual console */ static void msglogchar(int c, int pri); static void putchar(int ch, void *arg); static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len); @@ -335,21 +332,24 @@ static void putchar(int c, void *arg) { struct putchar_arg *ap = (struct putchar_arg*) arg; - int flags = ap->flags; struct tty *tp = ap->tty; + int consdirect, flags = ap->flags; + + consdirect = ((flags & TOCONS) && constty == NULL); + /* Don't use the tty code after a panic. */ if (panicstr) - constty = NULL; - if ((flags & TOCONS) && tp == NULL && constty) { - tp = constty; - flags |= TOTTY; + consdirect = 1; + if (consdirect) { + if (c != '\0') + cnputc(c); + } else { + if ((flags & TOTTY) && tp != NULL) + tputchar(c, tp); + if ((flags & TOCONS) && constty != NULL) + msgbuf_addchar(&consmsgbuf, c); } - if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 && - (flags & TOCONS) && tp == constty) - constty = NULL; if ((flags & TOLOG)) msglogchar(c, ap->pri); - if ((flags & TOCONS) && constty == NULL && c != '\0') - (*v_putc)(c); } /* diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 1c8aa6bcce5..4bebd65cf96 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -261,7 +261,7 @@ ttyclose(struct tty *tp) funsetown(&tp->t_sigio); s = spltty(); if (constty == tp) - constty = NULL; + constty_clear(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); @@ -871,9 +871,9 @@ ttioctl(struct tty *tp, u_long cmd, void *data, int flag) if (error) return (error); - constty = tp; + constty_set(tp); } else if (tp == constty) - constty = NULL; + constty_clear(); break; case TIOCDRAIN: /* wait till output drained */ error = ttywait(tp); diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c index fe723e691bf..53a5d3d3248 100644 --- a/sys/kern/tty_cons.c +++ b/sys/kern/tty_cons.c @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -117,11 +118,16 @@ int cons_unavail = 0; /* XXX: static int cn_mute; static int openflag; /* how /dev/console was opened */ static int cn_is_open; +static char *consbuf; /* buffer used by `consmsgbuf' */ +static struct callout conscallout; /* callout for outputting to constty */ +struct msgbuf consmsgbuf; /* message buffer for console tty */ static u_char console_pausing; /* pause after each line during probe */ static char *console_pausestr= ""; +struct tty *constty; /* pointer to console "window" tty */ void cndebug(char *); +static void constty_timeout(void *arg); CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL); SET_DECLARE(cons_set, struct consdev); @@ -589,6 +595,70 @@ cndbctl(int on) refcount++; } +static int consmsgbuf_size = 8192; +SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, + ""); + +/* + * Redirect console output to a tty. + */ +void +constty_set(struct tty *tp) +{ + int size; + + KASSERT(tp != NULL, ("constty_set: NULL tp")); + if (consbuf == NULL) { + size = consmsgbuf_size; + consbuf = malloc(size, M_TTYS, M_WAITOK); + msgbuf_init(&consmsgbuf, consbuf, size); + callout_init(&conscallout, 0); + } + constty = tp; + constty_timeout(NULL); +} + +/* + * Disable console redirection to a tty. + */ +void +constty_clear(void) +{ + int c; + + constty = NULL; + if (consbuf == NULL) + return; + callout_stop(&conscallout); + while ((c = msgbuf_getchar(&consmsgbuf)) != -1) + cnputc(c); + free(consbuf, M_TTYS); + consbuf = NULL; +} + +/* Times per second to check for pending console tty messages. */ +static int constty_wakeups_per_second = 5; +SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, + &constty_wakeups_per_second, 0, ""); + +static void +constty_timeout(void *arg) +{ + int c; + + while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { + if (tputchar(c, constty) < 0) + constty = NULL; + } + if (constty != NULL) { + callout_reset(&conscallout, hz / constty_wakeups_per_second, + constty_timeout, NULL); + } else { + /* Deallocate the constty buffer memory. */ + constty_clear(); + } +} + static void cn_drvinit(void *unused) { diff --git a/sys/sys/tty.h b/sys/sys/tty.h index b298e8ae2c2..2241d95eaae 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -265,6 +265,7 @@ struct speedtab { #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_TTYS); #endif +extern struct msgbuf consmsgbuf; /* Message buffer for constty. */ extern struct tty *constty; /* Temporary virtual console. */ extern long tk_cancc; extern long tk_nin; @@ -275,6 +276,8 @@ int b_to_q(char *cp, int cc, struct clist *q); void catq(struct clist *from, struct clist *to); void clist_alloc_cblocks(struct clist *q, int ccmax, int ccres); void clist_free_cblocks(struct clist *q); +void constty_set(struct tty *tp); +void constty_clear(void); int getc(struct clist *q); void ndflush(struct clist *q, int cc); char *nextc(struct clist *q, char *cp, int *c);