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 : 2: only EMERG and ALERT (panics) render on the console. CRIT and
23 : ERR must not, the kernel prints ratelimited CRIT bursts from inside
24 : tile syscalls (e.g. AF_XDP skb-mode TX over a bond with a packet
25 : tap). All messages still land in the kernel ring buffer
26 : (dmesg/journal) regardless of the console loglevel.
27 :
28 : fini restores the kernel defaults (blink on, loglevel 7), which may
29 : over-restore if the operator had their own settings before init;
30 : the previous values are not persisted anywhere. */
31 :
32 : #include "configure.h"
33 :
34 : #include <errno.h>
35 : #include <fcntl.h>
36 : #include <stdlib.h>
37 : #include <unistd.h>
38 :
39 0 : #define NAME "console"
40 :
41 0 : #define CURSOR_BLINK_PATH "/sys/class/graphics/fbcon/cursor_blink"
42 0 : #define PRINTK_PATH "/proc/sys/kernel/printk"
43 :
44 : #define CONSOLE_LOGLEVEL_QUIET (2UL) /* KERN_ALERT and above render */
45 : #define CONSOLE_LOGLEVEL_DEFAULT (7UL)
46 :
47 : static ulong
48 : read_ulong_field( char const * path,
49 0 : ulong idx ) {
50 0 : int fd = open( path, O_RDONLY );
51 0 : if( FD_UNLIKELY( fd<0 ) ) {
52 0 : if( FD_LIKELY( errno==ENOENT ) ) return ULONG_MAX;
53 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
54 0 : }
55 :
56 0 : char buf[ 256 ];
57 0 : long len = read( fd, buf, sizeof(buf)-1UL );
58 0 : if( FD_UNLIKELY( len<=0L ) ) FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
59 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
60 0 : buf[ len ] = '\0';
61 :
62 0 : char * p = buf;
63 0 : for( ulong i=0UL; i<idx; i++ ) {
64 0 : while( *p && *p!=' ' && *p!='\t' ) p++;
65 0 : while( *p==' ' || *p=='\t' ) p++;
66 0 : }
67 0 : char * end;
68 0 : ulong val = strtoul( p, &end, 10 );
69 0 : if( FD_UNLIKELY( end==p ) ) FD_LOG_ERR(( "failed to parse `%s`", path ));
70 0 : return val;
71 0 : }
72 :
73 : static void
74 : write_cstr( char const * path,
75 0 : char const * val ) {
76 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"%s\" > %s`%s", fd_log_style_dim(), val, path , fd_log_style_normal() ));
77 :
78 0 : int fd = open( path, O_WRONLY );
79 0 : if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
80 0 : long len = (long)strlen( val );
81 0 : if( FD_UNLIKELY( write( fd, val, (ulong)len )!=len ) )
82 0 : FD_LOG_ERR(( "write(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
83 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
84 0 : }
85 :
86 : static int
87 0 : has_fbcon( void ) {
88 0 : return 0==access( CURSOR_BLINK_PATH, F_OK );
89 0 : }
90 :
91 : static int
92 0 : enabled( config_t const * config ) {
93 0 : (void)config;
94 : /* The printk knob always exists; fbcon only when a framebuffer
95 : console is bound. Loglevel clamping is worth doing even without
96 : fbcon (serial consoles render synchronously in the printk
97 : caller's context). */
98 0 : return 0==access( PRINTK_PATH, F_OK );
99 0 : }
100 :
101 : static void
102 : init_perm( fd_cap_chk_t * chk,
103 0 : config_t const * config FD_PARAM_UNUSED ) {
104 0 : fd_cap_chk_root( chk, NAME, "modify `" PRINTK_PATH "` and `" CURSOR_BLINK_PATH "`" );
105 0 : }
106 :
107 : static void
108 : fini_perm( fd_cap_chk_t * chk,
109 0 : config_t const * config FD_PARAM_UNUSED ) {
110 0 : fd_cap_chk_root( chk, NAME, "modify `" PRINTK_PATH "` and `" CURSOR_BLINK_PATH "`" );
111 0 : }
112 :
113 : static void
114 0 : init( config_t const * config ) {
115 0 : (void)config;
116 :
117 0 : if( FD_LIKELY( has_fbcon() ) ) write_cstr( CURSOR_BLINK_PATH, "0" );
118 :
119 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
120 0 : if( FD_LIKELY( level>CONSOLE_LOGLEVEL_QUIET ) ) write_cstr( PRINTK_PATH, "2" );
121 0 : }
122 :
123 : static int
124 : fini( config_t const * config,
125 0 : int pre_init ) {
126 0 : (void)config; (void)pre_init;
127 :
128 0 : if( FD_LIKELY( has_fbcon() ) ) write_cstr( CURSOR_BLINK_PATH, "1" );
129 :
130 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
131 0 : if( FD_LIKELY( level<CONSOLE_LOGLEVEL_DEFAULT ) ) write_cstr( PRINTK_PATH, "7" );
132 0 : return 1;
133 0 : }
134 :
135 : static configure_result_t
136 : check( config_t const * config,
137 0 : int check_type ) {
138 0 : (void)config; (void)check_type;
139 :
140 0 : if( FD_LIKELY( has_fbcon() ) ) {
141 0 : ulong blink = read_ulong_field( CURSOR_BLINK_PATH, 0UL );
142 0 : if( FD_UNLIKELY( blink!=0UL && blink!=ULONG_MAX ) )
143 0 : NOT_CONFIGURED( "framebuffer console cursor blink is enabled" );
144 0 : }
145 :
146 0 : ulong level = read_ulong_field( PRINTK_PATH, 0UL );
147 0 : if( FD_UNLIKELY( level!=ULONG_MAX && level>CONSOLE_LOGLEVEL_QUIET ) )
148 0 : NOT_CONFIGURED( "console loglevel is %lu; kernel messages render console work on the printing CPU", level );
149 :
150 0 : CONFIGURE_OK();
151 0 : }
152 :
153 : configure_stage_t fd_cfg_stage_console = {
154 : .name = NAME,
155 : .always_recreate = 0,
156 : .enabled = enabled,
157 : .init_perm = init_perm,
158 : .fini_perm = fini_perm,
159 : .init = init,
160 : .fini = fini,
161 : .check = check,
162 : };
163 :
164 : #undef NAME
165 : #undef CURSOR_BLINK_PATH
166 : #undef PRINTK_PATH
|