Skip to content

Commit b0a7948

Browse files
authored
feat(klog): Implement kernel log level management and procfs interface (#1415)
- Introduced a new module for managing kernel log levels, mimicking Linux behavior. - Added support for dynamic log level configuration via command line and procfs interface. - Created a new `/proc/sys/kernel/printk` file for reading and writing log level settings. - Updated existing logging mechanisms to utilize the new log level management system. - Enhanced the QEMU startup script to allow setting log levels through environment variables. This implementation improves logging flexibility and aligns with expected Linux functionality. Signed-off-by: longjin <[email protected]>
1 parent 82786fa commit b0a7948

File tree

12 files changed

+406
-90
lines changed

12 files changed

+406
-90
lines changed

kernel/src/debug/klog/loglevel.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//! 内核日志级别管理模块
2+
//!
3+
//! 提供类似Linux内核的loglevel功能,支持通过cmdline参数和procfs接口
4+
//! 动态控制内核日志输出级别。
5+
6+
use core::sync::atomic::{AtomicU8, Ordering};
7+
8+
use system_error::SystemError;
9+
10+
use crate::init::cmdline::{KernelCmdlineKV, KernelCmdlineParameter, KCMDLINE_PARAM_KV};
11+
12+
/// 全局内核日志级别配置
13+
///
14+
/// 遵循Linux内核语义:
15+
/// - console_loglevel: 控制台输出级别,只有级别小于等于此值的消息才会输出
16+
/// - default_message_loglevel: 默认消息级别
17+
/// - minimum_console_loglevel: 最小控制台级别
18+
/// - default_console_loglevel: 默认控制台级别
19+
pub static KERNEL_LOG_LEVEL: KernelLogLevel = KernelLogLevel::new();
20+
21+
/// 日志级别
22+
#[derive(Default, Clone, PartialEq, Debug)]
23+
pub enum LogLevel {
24+
EMERG = 0,
25+
ALERT = 1,
26+
CRIT = 2,
27+
ERR = 3,
28+
WARN = 4,
29+
NOTICE = 5,
30+
INFO = 6,
31+
DEBUG = 7,
32+
#[default]
33+
DEFAULT = 8,
34+
}
35+
36+
impl From<usize> for LogLevel {
37+
fn from(value: usize) -> Self {
38+
match value {
39+
0 => LogLevel::EMERG,
40+
1 => LogLevel::ALERT,
41+
2 => LogLevel::CRIT,
42+
3 => LogLevel::ERR,
43+
4 => LogLevel::WARN,
44+
5 => LogLevel::NOTICE,
45+
6 => LogLevel::INFO,
46+
7 => LogLevel::DEBUG,
47+
_ => LogLevel::DEFAULT,
48+
}
49+
}
50+
}
51+
52+
impl From<::log::Level> for LogLevel {
53+
fn from(value: ::log::Level) -> Self {
54+
match value {
55+
log::Level::Error => LogLevel::ERR, // 3
56+
log::Level::Warn => LogLevel::WARN, // 4
57+
log::Level::Info => LogLevel::INFO, // 6
58+
log::Level::Debug => LogLevel::DEBUG, // 7
59+
log::Level::Trace => LogLevel::DEBUG, // 7 (maps to DEBUG)
60+
}
61+
}
62+
}
63+
64+
/// 内核日志级别管理结构
65+
#[derive(Debug)]
66+
pub struct KernelLogLevel {
67+
/// 控制台输出级别 (console_loglevel)
68+
/// 只有级别小于等于此值的消息才会输出到控制台
69+
pub console_level: AtomicU8,
70+
/// 默认消息级别 (default_message_loglevel)
71+
pub default_message_level: AtomicU8,
72+
/// 最小控制台级别 (minimum_console_loglevel)
73+
pub minimum_level: AtomicU8,
74+
/// 默认控制台级别 (default_console_loglevel)
75+
pub default_console_level: AtomicU8,
76+
}
77+
78+
impl KernelLogLevel {
79+
/// 创建新的内核日志级别配置
80+
///
81+
/// 默认值遵循Linux内核标准:
82+
/// - console_loglevel: 7 (DEBUG)
83+
/// - default_message_loglevel: 4 (WARNING)
84+
/// - minimum_console_loglevel: 1 (ALERT)
85+
/// - default_console_loglevel: 7 (DEBUG)
86+
pub const fn new() -> Self {
87+
Self {
88+
console_level: AtomicU8::new(7), // DEBUG级别
89+
default_message_level: AtomicU8::new(4), // WARNING级别
90+
minimum_level: AtomicU8::new(1), // ALERT级别
91+
default_console_level: AtomicU8::new(7), // DEBUG级别
92+
}
93+
}
94+
95+
/// 检查消息是否应该被输出到控制台
96+
///
97+
/// # 参数
98+
/// - `message_level`: 消息级别 (0-7)
99+
///
100+
/// # 返回值
101+
/// - `true`: 消息级别 <= 控制台级别,应该输出
102+
/// - `false`: 消息级别 > 控制台级别,应该过滤
103+
///
104+
/// # Linux语义
105+
/// 遵循Linux内核的过滤规则:message_level <= console_loglevel
106+
pub fn should_print(&self, message_level: LogLevel) -> bool {
107+
let console_level = self.console_level.load(Ordering::Acquire);
108+
// Linux语义:message_level <= console_loglevel 时输出
109+
message_level as u8 <= console_level
110+
}
111+
112+
/// 设置控制台日志级别
113+
///
114+
/// # 参数
115+
/// - `level`: 新的控制台级别 (0-7)
116+
///
117+
/// # 返回值
118+
/// - `Ok(())`: 设置成功
119+
/// - `Err(SystemError::EINVAL)`: 级别值无效
120+
pub fn set_console_level(&self, level: u8) -> Result<(), SystemError> {
121+
if level <= 7 {
122+
self.console_level.store(level, Ordering::Release);
123+
Ok(())
124+
} else {
125+
Err(SystemError::EINVAL)
126+
}
127+
}
128+
129+
/// 获取当前控制台日志级别
130+
pub fn get_console_level(&self) -> u8 {
131+
self.console_level.load(Ordering::Acquire)
132+
}
133+
134+
/// 获取默认消息级别
135+
pub fn get_default_message_level(&self) -> u8 {
136+
self.default_message_level.load(Ordering::Acquire)
137+
}
138+
139+
/// 获取最小控制台级别
140+
pub fn get_minimum_level(&self) -> u8 {
141+
self.minimum_level.load(Ordering::Acquire)
142+
}
143+
144+
/// 获取默认控制台级别
145+
pub fn get_default_console_level(&self) -> u8 {
146+
self.default_console_level.load(Ordering::Acquire)
147+
}
148+
149+
/// 获取所有日志级别配置
150+
///
151+
/// # 返回值
152+
/// 返回包含四个级别值的数组:[console, default_message, minimum, default_console]
153+
pub fn get_all_levels(&self) -> [u8; 4] {
154+
[
155+
self.get_console_level(),
156+
self.get_default_message_level(),
157+
self.get_minimum_level(),
158+
self.get_default_console_level(),
159+
]
160+
}
161+
}
162+
163+
/// loglevel命令行参数处理
164+
///
165+
/// 支持格式:loglevel=N (N=0-7)
166+
/// 示例:loglevel=4 只输出WARNING及以上级别的日志
167+
#[linkme::distributed_slice(KCMDLINE_PARAM_KV)]
168+
static LOGLEVEL_PARAM: KernelCmdlineParameter = KernelCmdlineParameter::KV(KernelCmdlineKV {
169+
name: "loglevel",
170+
value: None,
171+
initialized: false,
172+
default: "7", // 默认DEBUG级别
173+
});
174+
175+
/// 处理loglevel参数
176+
///
177+
/// 在cmdline参数解析完成后调用,设置全局日志级别
178+
pub fn handle_loglevel_param() {
179+
if let Some(param) = KCMDLINE_PARAM_KV.iter().find(|x| x.name() == "loglevel") {
180+
if let Some(value_str) = param.value_str() {
181+
if let Ok(level) = value_str.parse::<u8>() {
182+
if level <= 7 {
183+
let _ = KERNEL_LOG_LEVEL.set_console_level(level);
184+
log::info!("loglevel: set console log level to {} via cmdline", level);
185+
} else {
186+
log::warn!("loglevel: invalid level {}, must be 0-7", level);
187+
}
188+
} else {
189+
log::warn!("loglevel: invalid value '{}', must be a number", value_str);
190+
}
191+
}
192+
}
193+
}

kernel/src/debug/klog/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod loglevel;
12
pub mod mm;

kernel/src/filesystem/procfs/log.rs renamed to kernel/src/filesystem/procfs/klog.rs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,7 @@ use core::fmt::{Display, Formatter, Result};
22

33
use alloc::string::String;
44

5-
use crate::time::PosixTimeSpec;
6-
7-
// /// 日志类型
8-
// #[derive(Default, Clone, Debug)]
9-
// pub enum LogType {
10-
// /// 启动信息
11-
// Startup,
12-
// /// 驱动信息
13-
// Driver,
14-
// /// 系统信息
15-
// System,
16-
// /// 硬件信息
17-
// Hardware,
18-
// /// 内核模块信息
19-
// KernelModule,
20-
// /// 内核调试信息
21-
// KernelDebug,
22-
// #[default]
23-
// Default,
24-
// }
25-
26-
/// 日志级别
27-
#[derive(Default, Clone, PartialEq, Debug)]
28-
pub enum LogLevel {
29-
EMERG = 0,
30-
ALERT = 1,
31-
CRIT = 2,
32-
ERR = 3,
33-
WARN = 4,
34-
NOTICE = 5,
35-
INFO = 6,
36-
DEBUG = 7,
37-
#[default]
38-
DEFAULT = 8,
39-
}
40-
41-
impl From<usize> for LogLevel {
42-
fn from(value: usize) -> Self {
43-
match value {
44-
0 => LogLevel::EMERG,
45-
1 => LogLevel::ALERT,
46-
2 => LogLevel::CRIT,
47-
3 => LogLevel::ERR,
48-
4 => LogLevel::WARN,
49-
5 => LogLevel::NOTICE,
50-
6 => LogLevel::INFO,
51-
7 => LogLevel::DEBUG,
52-
_ => LogLevel::DEFAULT,
53-
}
54-
}
55-
}
5+
use crate::{debug::klog::loglevel::LogLevel, time::PosixTimeSpec};
566

577
/// 日志消息
588
#[derive(Default, Clone, Debug)]

kernel/src/filesystem/procfs/kmsg.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use core::sync::atomic::{compiler_fence, Ordering};
22

3-
use super::log::{LogLevel, LogMessage};
3+
use super::klog::LogMessage;
44

5-
use crate::libs::spinlock::SpinLock;
5+
use crate::{debug::klog::loglevel::LogLevel, libs::spinlock::SpinLock};
66

77
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
88

@@ -52,7 +52,7 @@ impl Kmsg {
5252

5353
/// 添加日志消息
5454
pub fn push(&mut self, msg: LogMessage) {
55-
self.buffer.push(msg);
55+
self.buffer.push(msg.clone());
5656
self.is_changed = true;
5757
}
5858

kernel/src/filesystem/procfs/mod.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ use crate::{
1515
arch::mm::LockedFrameAllocator,
1616
driver::base::device::device_number::DeviceNumber,
1717
filesystem::{
18-
procfs::proc_thread_self_ns::{
19-
current_thread_self_ns_ino, open_thread_self_ns_file, read_thread_self_ns_link,
20-
ThreadSelfNsFileType,
18+
procfs::{
19+
proc_thread_self_ns::{
20+
current_thread_self_ns_ino, open_thread_self_ns_file, read_thread_self_ns_link,
21+
ThreadSelfNsFileType,
22+
},
23+
sys::sysctl::PrintkSysctl,
2124
},
2225
vfs::{
2326
mount::{MountFlags, MountPath},
@@ -43,13 +46,14 @@ use super::vfs::{
4346
FileSystem, FsInfo, IndexNode, InodeId, Magic, Metadata, SuperBlock,
4447
};
4548

49+
pub mod klog;
4650
pub mod kmsg;
47-
pub mod log;
4851
mod proc_cpuinfo;
4952
mod proc_mounts;
5053
mod proc_thread_self_ns;
5154
mod proc_version;
5255
mod procfs_setup;
56+
mod sys;
5357
mod syscall;
5458

5559
/// @brief 进程文件类型
@@ -77,6 +81,8 @@ pub enum ProcFileType {
7781
ProcThreadSelfNsRoot,
7882
/// /proc/thread-self/ns/* namespace files
7983
ProcThreadSelfNsChild(ThreadSelfNsFileType),
84+
/// /proc/sys/kernel/printk
85+
ProcSysKernelPrintk,
8086
//todo: 其他文件类型
8187
///默认文件类型
8288
Default,
@@ -799,7 +805,8 @@ impl IndexNode for LockedProcFSInode {
799805
ProcFileType::ProcKmsg
800806
| ProcFileType::ProcFdDir
801807
| ProcFileType::ProcFdFile
802-
| ProcFileType::ProcThreadSelfNsRoot => 0,
808+
| ProcFileType::ProcThreadSelfNsRoot
809+
| ProcFileType::ProcSysKernelPrintk => 0,
803810
};
804811

805812
// 为不同类型的 procfs 节点设置文件私有数据
@@ -883,6 +890,12 @@ impl IndexNode for LockedProcFSInode {
883890
return read_thread_self_ns_link(ns_type, buf, offset)
884891
}
885892

893+
ProcFileType::ProcSysKernelPrintk => {
894+
// 使用 PrintkSysctl 处理 /proc/sys/kernel/printk 文件读取
895+
let buflen = buf.len().min(len);
896+
return PrintkSysctl.read_to_buffer(&mut buf[..buflen]);
897+
}
898+
886899
_ => (),
887900
};
888901

@@ -903,12 +916,32 @@ impl IndexNode for LockedProcFSInode {
903916

904917
fn write_at(
905918
&self,
906-
_offset: usize,
907-
_len: usize,
908-
_buf: &[u8],
919+
offset: usize,
920+
len: usize,
921+
buf: &[u8],
909922
_data: SpinLockGuard<FilePrivateData>,
910923
) -> Result<usize, SystemError> {
911-
return Err(SystemError::ENOSYS);
924+
if offset > 0 {
925+
// 不支持随机写入
926+
return Err(SystemError::EINVAL);
927+
}
928+
929+
let inode = self.0.lock();
930+
931+
// 检查当前inode是否为一个文件夹,如果是的话,就返回错误
932+
if inode.metadata.file_type == FileType::Dir {
933+
return Err(SystemError::EISDIR);
934+
}
935+
936+
// 根据文件类型处理写入
937+
match inode.fdata.ftype {
938+
ProcFileType::ProcSysKernelPrintk => {
939+
// 使用 PrintkSysctl 处理 /proc/sys/kernel/printk 文件写入
940+
let buflen = buf.len().min(len);
941+
return PrintkSysctl.write_from_buffer(&buf[..buflen]);
942+
}
943+
_ => Err(SystemError::ENOSYS),
944+
}
912945
}
913946

914947
fn fs(&self) -> Arc<dyn FileSystem> {

kernel/src/filesystem/procfs/procfs_setup.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ impl ProcFS {
1313
self.create_version_file();
1414
self.create_cpuinfo_file();
1515
self.create_self_file();
16+
self.create_sysctl_files();
1617
}
1718

1819
/// @brief 创建 /proc/meminfo 文件

0 commit comments

Comments
 (0)