Line data Source code
1 : /* The console stage quiets periodic kernel console rendering work
2 : that would otherwise run on isolated tile CPUs.
3 :
4 : Two sources, neither stoppable by cpuset partitions, IRQ steering,
5 : or the unbound-workqueue cpumask, because both use bound (per-CPU)
6 : work that runs on the CPU that queued it:
7 :
8 : - The framebuffer console cursor blink (fb_flashcursor) is driven
9 : by a self-re-arming timer. Ordinary timers fire on the CPU
10 : that armed them, so once the blink timer lands on a tile CPU
11 : (e.g. because a kernel message was printed from that CPU and
12 : redrew the console) it re-arms there forever, preempting the
13 : tile at ~5Hz.
14 :
15 : - Any printk at or below the console loglevel renders to the
16 : framebuffer console via work queued on the printing CPU
17 : (drm_fb_helper_damage_work). A kernel warning emitted inside a
18 : tile's own syscall therefore schedules display work onto that
19 : tile's core.
20 :
21 : init disables the cursor blink and clamps the console loglevel to
22 : ERR (4): oopses, panics, and hardware errors still render on the
23 : console; WARNING-class no longer reaches it. All messages still
24 : land in the kernel ring buffer (dmesg/journal) regardless of the
25 : console loglevel.
26 :
27 : fini restores the kernel defaults (blink on, loglevel 7), which may
28 : over-restore if the operator had their own settings before init;
29 : the previous values are not persisted anywhere. */
30 :
31 : #include "configure.h"
32 :
33 : #include <errno.h>
34 : #include <fcntl.h>
35 : #include <stdlib.h>
36 : #include <unistd.h>
37 :
38 0 : #define NAME "console"
39 :
40 0 : #define CURSOR_BLINK_PATH "/sys/class/graphics/fbcon/cursor_blink"
41 0 : #define PRINTK_PATH "/proc/sys/kernel/printk"
42 :
43 : #define CONSOLE_LOGLEVEL_QUIET (4UL) /* KERN_ERR and above render */
44 : #define CONSOLE_LOGLEVEL_DEFAULT (7UL)
45 :
46 : static ulong
47 : read_ulong_field( char const * path,
48 0 : ulong idx ) {
49 0 : int fd = open( path, O_RDONLY );
50 0 : if( FD_UNLIKELY( fd<0 ) ) {
51 0 : if( FD_LIKELY( errno==ENOENT ) ) return ULONG_MAX;
52 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
53 0 : }
54 :
55 0 : char buf[ 256 ];
56 0 : long len = read( fd, buf, sizeof(buf)-1UL );
57 0 : if( FD_UNLIKELY( len<=0L ) ) FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
58 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
59 0 : buf[ len ] = '\0';
60 :
61 0 : char * p = buf;
62 0 : for( ulong i=0UL; i<idx; i++ ) {
63 0 : while( *p && *p!=' ' && *p!='\t' ) p++;
64 0 : while( *p==' ' || *p=='\t' ) p++;
65 0 : }
66 0 : char * end;
67 0 : ulong val = strtoul( p, &end, 10 );
68 0 : if( FD_UNLIKELY( end==p ) ) FD_LOG_ERR(( "failed to parse `%s`", path ));
69 0 : return val;
70 0 : }
71 :
72 : static void
73 : write_cstr( char const * path,
74 0 : char const * val ) {
75 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"%s\" > %s`%s", fd_log_style_dim(), val, path , fd_log_style_normal() ));
76 :
77 0 : int fd = open( path, O_WRONLY );
78 0 : if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
79 0 : long len = (long)strlen( val );
80 0 : if( FD_UNLIKELY( write( fd, val, (ulong)len )!=len ) )
81 0 : FD_LOG_ERR(( "write(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
82 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
83 0 : }
84 :
85 : static int
86 0 : has_fbcon( void ) {
87 0 : return 0==access( CURSOR_BLINK_PATH, F_OK );
88 0 : }
89 :
90 : static int
91 0 : enabled( config_t const * config ) {
92 0 : (void)config;
93 : /* The printk knob always exists; fbcon only when a framebuffer
94 : console is bound. Loglevel clamping is worth doing even without
95 : fbcon (serial consoles render synchronously in the printk
96 : caller's context). */
97 0 : return 0==access( PRINTK_PATH, F_OK );
98 0 : }
99 :
100 : static void
101 : init_perm( fd_cap_chk_t * chk,
102 0 : config_t const * config FD_PARAM_UNUSED ) {
103 0 : fd_cap_chk_root( chk, NAME, "modify `" PRINTK_PATH "` and `" CURSOR_BLINK_PATH "`" );
104 0 : }
105 :
106 : static void
107 : fini_perm( fd_cap_chk_t * chk,
108 0 : config_t const * config FD_PARAM_UNUSED ) {
109 0 : fd_cap_chk_root( chk, NAME, "modify `" PRINTK_PATH "` and `" CURSOR_BLINK_PATH "`" );
110 0 : }
111 :
112 : static void
113 0 : init( config_t const * config ) {
114 0 : (void)config;
115 :
116 0 : if( FD_LIKELY( has_fbcon() ) ) write_cstr( CURSOR_BLINK_PATH, "0" );
117 :
118 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
119 0 : if( FD_LIKELY( level>CONSOLE_LOGLEVEL_QUIET ) ) write_cstr( PRINTK_PATH, "4" );
120 0 : }
121 :
122 : static int
123 : fini( config_t const * config,
124 0 : int pre_init ) {
125 0 : (void)config; (void)pre_init;
126 :
127 0 : if( FD_LIKELY( has_fbcon() ) ) write_cstr( CURSOR_BLINK_PATH, "1" );
128 :
129 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
130 0 : if( FD_LIKELY( level<CONSOLE_LOGLEVEL_DEFAULT ) ) write_cstr( PRINTK_PATH, "7" );
131 0 : return 1;
132 0 : }
133 :
134 : static configure_result_t
135 : check( config_t const * config,
136 0 : int check_type ) {
137 0 : (void)config; (void)check_type;
138 :
139 0 : if( FD_LIKELY( has_fbcon() ) ) {
140 0 : ulong blink = read_ulong_field( CURSOR_BLINK_PATH, 0UL );
141 0 : if( FD_UNLIKELY( blink!=0UL && blink!=ULONG_MAX ) )
142 0 : NOT_CONFIGURED( "framebuffer console cursor blink is enabled" );
143 0 : }
144 :
145 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
146 0 : if( FD_UNLIKELY( level!=ULONG_MAX && level>CONSOLE_LOGLEVEL_QUIET ) )
147 0 : NOT_CONFIGURED( "console loglevel is %lu; kernel warnings render console work on the printing CPU", level );
148 :
149 0 : CONFIGURE_OK();
150 0 : }
151 :
152 : configure_stage_t fd_cfg_stage_console = {
153 : .name = NAME,
154 : .always_recreate = 0,
155 : .enabled = enabled,
156 : .init_perm = init_perm,
157 : .fini_perm = fini_perm,
158 : .init = init,
159 : .fini = fini,
160 : .check = check,
161 : };
162 :
163 : #undef NAME
164 : #undef CURSOR_BLINK_PATH
165 : #undef PRINTK_PATH
|