Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Mar 2018 07:30:58 +0000 (UTC)
From:      Eitan Adler <eadler@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r330913 - stable/11/sys/dev/syscons
Message-ID:  <201803140730.w2E7Uw8L062312@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: eadler
Date: Wed Mar 14 07:30:58 2018
New Revision: 330913
URL: https://svnweb.freebsd.org/changeset/base/330913

Log:
  MFC r314641,r314646,r314997,r315390:
  
  Colorize syscons kernel console output according to a table indexed
  by the CPU number.
  
  This was originally for debugging near-deadlock conditions where
  multiple CPUs either deadlock or scramble each other's output trying
  to report the problem, but I found it interesting and sometimes
  useful for ordinary kernel messages.  Ordinary kernel messages
  shouldn't be interleaved, but if they are then the colorization
  makes them readable even if the interleaving is for every character
  (provided the CPU printing each message doesn't change).
  
  The default colors are 8-15 starting at 15 (bright white on black)
  for CPU 0 and repeating every 8 CPUs.  This works best with 8 CPUs.
  Non-bright colors and nonzero background colors need special
  configuration to avoid unreadable and ugly combinations so are not
  configured by default.  The next bright color after 15 is 8 (bright
  black = dark gray) is not very readable but is the only other color
  used with 2 CPUs.  After that the next bright color is 9 (bright
  blue) which is not much brighter than bright black, but is used with
  3+ CPUs.  Other bright colors are brighter.
  
  Colorization is configured by default so that it gets tested.  It can
  only be turned off by configuring SC_KERNEL_CONS_ATTR to anything other
  than FG_WHITE.  After booting, all colors can be changed using the
  syscons.kattr sysctl.  This is a SYSCTL_OPAQUE, and no utility is
  provided to change it (sysctl only displays it).
  
  The default colors work in all VGA modes that I could test.  In 2-color
  graphics modes, all 8 bright colors are displayed as bright white, so
  the colorization has no effect, but anything with a nonzero background
  gives white on white unless the foreground is zero.  I don't have an
  mono or VGA grayscale hardware to test on.  Support for mono mode seems
  to have never worked right in syscons (I think bright white gives white
  underline with either bold or bright), but VGA grayscale should work
  better than 2-color graphics.
  
  Implement ec_putc() (emergency kernel [syscons] console putc()) and use
  it in emergency in sc_cnputc().
  
  Locking fixes in sc_cnputc() previously turned off normal output in
  near-deadlock conditions and added deferred output which might never
  be completed.  Emergency output goes to the frame buffer using
  sufficiently atomic non-blocking writes if the console is in text
  mode (in graphics mode, nothing is done, modulo races setting the
  graphics mode bit).  Screen updates overwrite the emergency output
  if the emergency condition clears enough to reach them.
  
  ec_putc() also works for "early" console output in normal x86 text
  mode as soon as this mode is initialized (if ever).  This uses a
  hard-coded x86 frame buffer address before cninit() and a hopefully
  MI address after cninit().  But non-x86 is more likely to not support
  text mode, when ec_putc() will be null.  ec_putc() has no dependencies
  of syscons before cninit(), and only has them later to track syscons'
  mode changes.  This commit doesn't attach ec_putc() for early use.
  
  To test emergency use, put a breakpoint in central syscons output code
  like sc_puts() and do some user output.  The system used to race or
  deadlock in ddb output soon after entry to ddb.  The locking fixes
  deferred the output until after leaving ddb, so ddb was unusable and
  you had to try typing c[ontinue] blindly until it exited, or better use
  a serial console in parallel.  Now the output goes to a window in the
  middle 2/3 of the screen.  Scrolling is circular and there is no cursor,
  but otherwise ec_putc() provides full dumb terminal functionality and
  very fast output that hides artificates from dumb overwrites.

Modified:
  stable/11/sys/dev/syscons/scterm-teken.c
  stable/11/sys/dev/syscons/syscons.c
  stable/11/sys/dev/syscons/syscons.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/syscons/scterm-teken.c
==============================================================================
--- stable/11/sys/dev/syscons/scterm-teken.c	Wed Mar 14 07:16:29 2018	(r330912)
+++ stable/11/sys/dev/syscons/scterm-teken.c	Wed Mar 14 07:30:58 2018	(r330913)
@@ -178,7 +178,7 @@ scteken_puts(scr_stat *scp, u_char *buf, int len, int 
 	if (kernel) {
 		/* Use special colors for kernel messages. */
 		backup = *teken_get_curattr(&ts->ts_teken);
-		scteken_revattr(SC_KERNEL_CONS_ATTR, &kattr);
+		scteken_revattr(sc_kattr(), &kattr);
 		teken_set_curattr(&ts->ts_teken, &kattr);
 		teken_input(&ts->ts_teken, buf, len);
 		teken_set_curattr(&ts->ts_teken, &backup);

Modified: stable/11/sys/dev/syscons/syscons.c
==============================================================================
--- stable/11/sys/dev/syscons/syscons.c	Wed Mar 14 07:16:29 2018	(r330912)
+++ stable/11/sys/dev/syscons/syscons.c	Wed Mar 14 07:30:58 2018	(r330913)
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
+#include <sys/pcpu.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/random.h>
@@ -102,6 +103,8 @@ static default_attr user_default = {
     SC_NORM_REV_ATTR,
 };
 
+static	u_char		sc_kattrtab[MAXCPU];
+
 static	int		sc_console_unit = -1;
 static	int		sc_saver_keyb_only = 1;
 static  scr_stat    	*sc_console;
@@ -143,6 +146,8 @@ static	int		sc_no_suspend_vtswitch = 0;
 static	int		sc_susp_scr;
 
 static SYSCTL_NODE(_hw, OID_AUTO, syscons, CTLFLAG_RD, 0, "syscons");
+SYSCTL_OPAQUE(_hw_syscons, OID_AUTO, kattr, CTLFLAG_RW,
+    &sc_kattrtab, sizeof(sc_kattrtab), "CU", "kernel console attributes");
 static SYSCTL_NODE(_hw_syscons, OID_AUTO, saver, CTLFLAG_RD, 0, "saver");
 SYSCTL_INT(_hw_syscons_saver, OID_AUTO, keybonly, CTLFLAG_RW,
     &sc_saver_keyb_only, 0, "screen saver interrupted by input only");
@@ -263,6 +268,62 @@ static struct cdevsw consolectl_devsw = {
 	.d_name		= "consolectl",
 };
 
+/* ec -- emergency console. */
+
+static	u_int	ec_scroffset;
+
+static void
+ec_putc(int c)
+{
+	uintptr_t fb;
+	u_short *scrptr;
+	u_int ind;
+	int attr, column, mysize, width, xsize, yborder, ysize;
+
+	if (c < 0 || c > 0xff || c == '\a')
+		return;
+	if (sc_console == NULL) {
+#if !defined(__amd64__) && !defined(__i386__)
+		return;
+#endif
+		/*
+		 * This is enough for ec_putc() to work very early on x86
+		 * if the kernel starts in normal color text mode.
+		 */
+		fb = 0xb8000;
+		xsize = 80;
+		ysize = 25;
+	} else {
+		if (main_console.status & GRAPHICS_MODE)
+			return;
+		fb = main_console.sc->adp->va_window;
+		xsize = main_console.xsize;
+		ysize = main_console.ysize;
+	}
+	yborder = ysize / 5;
+	scrptr = (u_short *)(void *)fb + xsize * yborder;
+	mysize = xsize * (ysize - 2 * yborder);
+	do {
+		ind = ec_scroffset;
+		column = ind % xsize;
+		width = (c == '\b' ? -1 : c == '\t' ? (column + 8) & ~7 :
+		    c == '\r' ? -column : c == '\n' ? xsize - column : 1);
+		if (width == 0 || (width < 0 && ind < -width))
+			return;
+	} while (atomic_cmpset_rel_int(&ec_scroffset, ind, ind + width) == 0);
+	if (c == '\b' || c == '\r')
+		return;
+	if (c == '\n')
+		ind += xsize;	/* XXX clearing from new pos is not atomic */
+
+	attr = sc_kattr();
+	if (c == '\t' || c == '\n')
+		c = ' ';
+	do
+		scrptr[ind++ % mysize] = (attr << 8) | c;
+	while (--width != 0);
+}
+
 int
 sc_probe_unit(int unit, int flags)
 {
@@ -1859,10 +1920,13 @@ sc_cnputc(struct consdev *cd, int c)
     sc_cnputc_log[head % sizeof(sc_cnputc_log)] = c;
 
     /*
-     * If we couldn't open, return to defer output.
+     * If we couldn't open, do special reentrant output and return to defer
+     * normal output.
      */
-    if (!st.scr_opened)
+    if (!st.scr_opened) {
+	ec_putc(c);
 	return;
+    }
 
 #ifndef SC_NO_HISTORY
     if (scp == scp->sc->cur_scp && scp->status & SLKED) {
@@ -3009,8 +3073,16 @@ scinit(int unit, int flags)
     int i;
 
     /* one time initialization */
-    if (init_done == COLD)
+    if (init_done == COLD) {
 	sc_get_bios_values(&bios_value);
+	for (i = 0; i < nitems(sc_kattrtab); i++) {
+#if SC_KERNEL_CONS_ATTR == FG_WHITE
+	    sc_kattrtab[i] = 8 + (i + FG_WHITE) % 8U;
+#else
+	    sc_kattrtab[i] = SC_KERNEL_CONS_ATTR;
+#endif
+	}
+    }
     init_done = WARM;
 
     /*
@@ -4024,6 +4096,12 @@ sc_bell(scr_stat *scp, int pitch, int duration)
 	    pitch *= 2;
 	sysbeep(1193182 / pitch, duration);
     }
+}
+
+int
+sc_kattr(void)
+{
+    return (sc_kattrtab[PCPU_GET(cpuid) % nitems(sc_kattrtab)]);
 }
 
 static void

Modified: stable/11/sys/dev/syscons/syscons.h
==============================================================================
--- stable/11/sys/dev/syscons/syscons.h	Wed Mar 14 07:16:29 2018	(r330912)
+++ stable/11/sys/dev/syscons/syscons.h	Wed Mar 14 07:30:58 2018	(r330913)
@@ -591,6 +591,7 @@ void		sc_paste(scr_stat *scp, const u_char *p, int cou
 void		sc_respond(scr_stat *scp, const u_char *p,
 			   int count, int wakeup);
 void		sc_bell(scr_stat *scp, int pitch, int duration);
+int		sc_kattr(void);
 
 /* schistory.c */
 #ifndef SC_NO_HISTORY



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201803140730.w2E7Uw8L062312>