-
Notifications
You must be signed in to change notification settings - Fork 79
Open
Description
考虑我们有这样的结构,它主要实现了这样的功能:
- 发送读请求,将
waker
保存,让出 CPU。 - 中断到来,根据 token 取出
waker
,唤醒 Future。 - 完成读取。
这套方案存在两个问题:
- 在非理想情况下,Future 可能被其他事件取消(信号/进程终止等),此时后续的
complete_read_blocks
不会被执行,因此需要驱动支持直接取消一个 token,而不需要完成数据复制工作。(不能创建一个新的 buffer 放到全局区,把 complete 操作移到handle_irq
处,因为这会导致额外的复制开销。) peek_used
方法只会取出一个待处理的 token(而不会消费),而在这个 token 被处理前无法得知其他 token 是否完成,从而将对应任务唤醒。这会导致其他的 Future 因为后续无法收到中断信号而永远被挂起(假设中断发生时所有任务都已经就绪)。
这样的问题在目前的异步接口下很难解决。
struct ReqGuard<'a>(&'a VirtIOBlkDevice, u16);
impl Drop for ReqGuard<'_> {
fn drop(&mut self) {
let mut inner = self.0.inner.lock();
inner.tokens.remove(&self.1);
inner.block.// How: cancel_request(self.1);
}
}
async fn read_block(&self, block_id: usize, buf: &mut [u8]) -> SyscallResult {
let mut req = BlkReq::default();
let mut resp = BlkResp::default();
let token = loop {
let token = unsafe {
self.inner.lock().block.read_blocks_nb(block_id, &mut req, buf, &mut resp)
};
// Error handling...
};
let guard = ReqGuard(self, token);
poll_fn(|cx| {
self.inner.lock().tokens.insert(token, cx.waker().clone());
Poll::<()>::Pending
}).await;
core::mem::forget(guard);
let res = unsafe {
self.inner.lock().block.complete_read_blocks(token, &mut req, buf, &mut resp)
};
// Error handling...
}
fn handle_irq(&self) {
let mut inner = self.inner.lock();
if let Some(token) = inner.block.peek_used() {
if let Some(waker) = inner.tokens.remove(&token) {
waker.wake();
}
}
}
Metadata
Metadata
Assignees
Labels
No labels