Skip to content

Commit

Permalink
实现内核通知链 notifier chain (#316)
Browse files Browse the repository at this point in the history
* 实现通知链块结构

* 实现通知链的基本功能

* 实现 atomic notifier chain

* 实现 blocking notifier chain

* 使用 rust 范式完成功能

* 支持回调次数 nr_to_call

* 移动至 libs 目录

* 完善通知链相关方法

* 修正相关格式

* 文档编写

* 更改文档路径
  • Loading branch information
ccrysisa authored Aug 6, 2023
1 parent be63f3b commit 729a96e
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/kernel/core_api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
atomic
data_structures
casting
notifier_chain
softirq
33 changes: 33 additions & 0 deletions docs/kernel/core_api/notifier_chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Notifier Chian 通知链

## 1.原理概要

  通知链是内核中各个子系统之间或子系统内部各个模块之间的事件通知机制。通知链本质上是一个事件处理函数的列表,每个通知链都与某个类型的事件相关(例如 reboot 事件)。当特定的事件发生时,就会调用相应事件的通知链中的回调函数,使子系统/模块对事件响应,并进行相对应的处理。

  通知链和订阅功能有些类似,可以理解为:有个“通知者”维护了一个列表,“订阅者”将自己的回调函数注册进这个列表(“订阅者”当然也可以注销自己的回调函数)。当某个事件发生需要通知时,“通知者”就遍历这个列表中所有的回调函数并进行调用,这样所有注册的“订阅者”就能针对这个事件做出自己的响应和处理。

## 2.核心功能

### 2.1 注册回调函数

  将回调函数封装成特定的结构体,并将该结构体注册到指定的通知链当中。相关方法为 `register`,由“订阅者”使用。

### 2.2 注销回调函数

  将回调函数从指定的通知链当中进行注销,即从通知链中删去该回调函数。相关方法为 `unregister`,由“订阅者”使用。

### 2.3 事件通知

  当某个事件发生时,该事件相关的通知链通过该方法来进行事件通知。`call_chain` 这个方法会遍历通知链中的所有元素,并依次调用注册的回调函数。该方法由“通知者”使用。

## 3.通知链类型

  每种通知链都有相对应的 `register``unregister` 以及 `call_chain` 的接口,其功能同上面所述的核心功能。

- `AtomicNotifierChain`:原子的通知链,不可睡眠,建议用于中断上下文。
- `BlockingNotifierChain`:可阻塞的通知链,可以睡眠,建议用在进程上下文。
- `RawNotifierChain`:原始的通知链,由调用者自行考虑线程安全。

## 4. 其它问题

  `BlockingNotifierChain` 暂时没实现可睡眠的功能。
1 change: 1 addition & 0 deletions kernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![feature(core_intrinsics)]
#![feature(c_void_variant)]
#![feature(drain_filter)]
#![feature(is_some_and)]
#![feature(panic_info_message)]
#![feature(ptr_internals)]
#![feature(trait_upcasting)]
Expand Down
1 change: 1 addition & 0 deletions kernel/src/libs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ pub mod spinlock;
pub mod vec_cursor;
#[macro_use]
pub mod volatile;
pub mod notifier;
pub mod wait_queue;
196 changes: 196 additions & 0 deletions kernel/src/libs/notifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
use crate::{
kwarn,
libs::{rwlock::RwLock, spinlock::SpinLock},
syscall::SystemError,
};
use alloc::{sync::Arc, vec::Vec};

/// @brief 通知链节点
pub trait NotifierBlock<T> {
/// @brief 通知链中注册的回调函数类型
fn notifier_call(&self, action: u64, data: Option<&T>) -> i32;
/// @brief 通知链节点的优先级
fn priority(&self) -> i32;
}

/// @brief 通知链
// TODO: 考虑使用红黑树封装
struct NotifierChain<T>(Vec<Arc<dyn NotifierBlock<T>>>);

impl<T> NotifierChain<T> {
pub fn new() -> Self {
Self(vec![])
}

/// @brief 将节点注册到通知链
/// @param unique_priority 检查通知链中优先级的唯一性
pub fn register(
&mut self,
block: Arc<dyn NotifierBlock<T>>,
unique_priority: bool,
) -> Result<(), SystemError> {
let mut index: usize = 0;

// 在 notifier chain中寻找第一个优先级比要插入块低的块
for b in self.0.iter() {
// 判断之前是否已经注册过该节点
if Arc::as_ptr(&block) == Arc::as_ptr(b) {
kwarn!(
"notifier callback {:?} already registered",
Arc::as_ptr(&block)
);
return Err(SystemError::EEXIST);
}

if block.priority() > b.priority() {
break;
}

// 优先级唯一性检测
if block.priority() == b.priority() && unique_priority {
return Err(SystemError::EBUSY);
}

index += 1;
}

// 插入 notifier chain
self.0.insert(index, block);
return Ok(());
}

/// @brief 在通知链中取消注册节点
pub fn unregister(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
let remove = self
.0
.drain_filter(|b| Arc::as_ptr(&block) == Arc::as_ptr(b));
match remove.count() {
0 => return Err(SystemError::ENOENT),
_ => return Ok(()),
}
}

/// @brief 通知链进行事件通知
/// @param nr_to_call 回调函数次数
/// @return (最后一次回调函数的返回值,回调次数)
// TODO: 增加 NOTIFIER_STOP_MASK 相关功能
pub fn call_chain(
&self,
action: u64,
data: Option<&T>,
nr_to_call: Option<usize>,
) -> (i32, usize) {
let mut ret: i32 = 0;
let mut nr_calls: usize = 0;

for b in self.0.iter() {
if nr_to_call.is_some_and(|x| nr_calls >= x) {
break;
}
ret = b.notifier_call(action, data);
nr_calls += 1;
}
return (ret, nr_calls);
}
}

/// @brief 原子的通知链,使用 SpinLock 进行同步
pub struct AtomicNotifierChain<T>(SpinLock<NotifierChain<T>>);

impl<T> AtomicNotifierChain<T> {
pub fn new() -> Self {
Self(SpinLock::new(NotifierChain::<T>::new()))
}

pub fn register(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.lock();
return notifier_chain_guard.register(block, false);
}

pub fn register_unique_prio(
&mut self,
block: Arc<dyn NotifierBlock<T>>,
) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.lock();
return notifier_chain_guard.register(block, true);
}

pub fn unregister(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.lock();
return notifier_chain_guard.unregister(block);
}

pub fn call_chain(
&self,
action: u64,
data: Option<&T>,
nr_to_call: Option<usize>,
) -> (i32, usize) {
let notifier_chain_guard = self.0.lock();
return notifier_chain_guard.call_chain(action, data, nr_to_call);
}
}

/// @brief 可阻塞的通知链,使用 RwLock 进行同步
// TODO: 使用 semaphore 封装
pub struct BlockingNotifierChain<T>(RwLock<NotifierChain<T>>);

impl<T> BlockingNotifierChain<T> {
pub fn new() -> Self {
Self(RwLock::new(NotifierChain::<T>::new()))
}

pub fn register(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.write();
return notifier_chain_guard.register(block, false);
}

pub fn register_unique_prio(
&mut self,
block: Arc<dyn NotifierBlock<T>>,
) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.write();
return notifier_chain_guard.register(block, true);
}

pub fn unregister(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
let mut notifier_chain_guard = self.0.write();
return notifier_chain_guard.unregister(block);
}

pub fn call_chain(
&self,
action: u64,
data: Option<&T>,
nr_to_call: Option<usize>,
) -> (i32, usize) {
let notifier_chain_guard = self.0.read();
return notifier_chain_guard.call_chain(action, data, nr_to_call);
}
}

/// @brief 原始的通知链,由调用者自行考虑同步
pub struct RawNotifierChain<T>(NotifierChain<T>);

impl<T> RawNotifierChain<T> {
pub fn new() -> Self {
Self(NotifierChain::<T>::new())
}

pub fn register(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
return self.0.register(block, false);
}

pub fn unregister(&mut self, block: Arc<dyn NotifierBlock<T>>) -> Result<(), SystemError> {
return self.0.unregister(block);
}

pub fn call_chain(
&self,
action: u64,
data: Option<&T>,
nr_to_call: Option<usize>,
) -> (i32, usize) {
return self.0.call_chain(action, data, nr_to_call);
}
}

0 comments on commit 729a96e

Please sign in to comment.