Add page cache manager as the pager for vmo
This commit is contained in:
parent
4629b8a15e
commit
4a3b0576b2
|
@ -13,6 +13,17 @@ dependencies = [
|
|||
"rsdp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.32"
|
||||
|
@ -91,6 +102,15 @@ version = "0.2.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -172,6 +192,7 @@ dependencies = [
|
|||
"jinux-util",
|
||||
"jinux-virtio",
|
||||
"lazy_static",
|
||||
"lru",
|
||||
"pod",
|
||||
"pod-derive",
|
||||
"ringbuffer",
|
||||
|
@ -263,6 +284,21 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
|
||||
[[package]]
|
||||
name = "pod"
|
||||
version = "0.1.0"
|
||||
|
@ -429,6 +465,12 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-input-decoder"
|
||||
version = "0.1.4"
|
||||
|
|
|
@ -27,6 +27,7 @@ ringbuffer = "0.10.0"
|
|||
|
||||
spin = "0.9.4"
|
||||
vte = "0.10"
|
||||
lru = "0.9.0"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
|
|
|
@ -6,11 +6,11 @@ use super::*;
|
|||
|
||||
impl InodeHandle<Rights> {
|
||||
pub fn new(
|
||||
inode: Arc<dyn Inode>,
|
||||
inode: VfsInode,
|
||||
access_mode: AccessMode,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<Self> {
|
||||
let inode_info = inode.metadata();
|
||||
let inode_info = inode.raw_inode().metadata();
|
||||
if access_mode.is_readable() && !inode_info.mode.is_readable() {
|
||||
return_errno_with_message!(Errno::EACCES, "File is not readable");
|
||||
}
|
||||
|
|
|
@ -4,16 +4,18 @@ mod dyn_cap;
|
|||
mod static_cap;
|
||||
|
||||
use super::utils::{
|
||||
AccessMode, DirentWriter, DirentWriterContext, Inode, InodeType, SeekFrom, StatusFlags,
|
||||
AccessMode, DirentWriter, DirentWriterContext, InodeType, SeekFrom, StatusFlags,
|
||||
};
|
||||
use super::vfs_inode::VfsInode;
|
||||
use crate::prelude::*;
|
||||
use crate::rights::Rights;
|
||||
use alloc::sync::Arc;
|
||||
use jinux_frame::vm::VmIo;
|
||||
|
||||
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
|
||||
|
||||
struct InodeHandle_ {
|
||||
inode: Arc<dyn Inode>,
|
||||
inode: VfsInode,
|
||||
offset: Mutex<usize>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: Mutex<StatusFlags>,
|
||||
|
@ -22,15 +24,18 @@ struct InodeHandle_ {
|
|||
impl InodeHandle_ {
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let file_size = self.inode.metadata().size;
|
||||
let file_size = self.inode.raw_inode().metadata().size;
|
||||
let start = file_size.min(*offset);
|
||||
let end = file_size.min(*offset + buf.len());
|
||||
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
||||
self.inode.read_at(start, &mut buf[0..start - end])?
|
||||
self.inode
|
||||
.raw_inode()
|
||||
.read_at(start, &mut buf[0..end - start])?
|
||||
} else {
|
||||
self.inode.read_at(start, &mut buf[0..start - end])?
|
||||
// TODO: use page cache
|
||||
// self.inode.pages().read_at(start, buf[0..start - end])?
|
||||
self.inode
|
||||
.pages()
|
||||
.read_bytes(start, &mut buf[0..end - start])?;
|
||||
end - start
|
||||
};
|
||||
|
||||
*offset += len;
|
||||
|
@ -39,20 +44,23 @@ impl InodeHandle_ {
|
|||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let file_size = self.inode.metadata().size;
|
||||
let file_size = self.inode.raw_inode().metadata().size;
|
||||
if self.status_flags.lock().contains(StatusFlags::O_APPEND) {
|
||||
*offset = file_size;
|
||||
}
|
||||
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
||||
self.inode.write_at(*offset, buf)?
|
||||
self.inode.raw_inode().write_at(*offset, buf)?
|
||||
} else {
|
||||
self.inode.write_at(*offset, buf)?
|
||||
// TODO: use page cache
|
||||
// let len = self.inode.pages().write_at(*offset, buf)?;
|
||||
// if offset + len > file_size {
|
||||
// self.inode.resize(offset + len)?;
|
||||
// }
|
||||
// len
|
||||
let pages = self.inode.pages();
|
||||
let should_expand_size = *offset + buf.len() > file_size;
|
||||
if should_expand_size {
|
||||
pages.resize(*offset + buf.len())?;
|
||||
}
|
||||
pages.write_bytes(*offset, buf)?;
|
||||
if should_expand_size {
|
||||
self.inode.raw_inode().resize(*offset + buf.len())?;
|
||||
}
|
||||
buf.len()
|
||||
};
|
||||
|
||||
*offset += len;
|
||||
|
@ -69,7 +77,7 @@ impl InodeHandle_ {
|
|||
off as i64
|
||||
}
|
||||
SeekFrom::End(off /* as i64 */) => {
|
||||
let file_size = self.inode.metadata().size as i64;
|
||||
let file_size = self.inode.raw_inode().metadata().size as i64;
|
||||
assert!(file_size >= 0);
|
||||
file_size
|
||||
.checked_add(off)
|
||||
|
@ -116,7 +124,7 @@ impl InodeHandle_ {
|
|||
pub fn readdir(&self, writer: &mut dyn DirentWriter) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let mut dir_writer_ctx = DirentWriterContext::new(*offset, writer);
|
||||
let written_size = self.inode.readdir(&mut dir_writer_ctx)?;
|
||||
let written_size = self.inode.raw_inode().readdir(&mut dir_writer_ctx)?;
|
||||
*offset = dir_writer_ctx.pos();
|
||||
Ok(written_size)
|
||||
}
|
||||
|
|
|
@ -9,3 +9,4 @@ pub mod poll;
|
|||
pub mod stat;
|
||||
pub mod stdio;
|
||||
pub mod utils;
|
||||
pub mod vfs_inode;
|
||||
|
|
|
@ -2,6 +2,7 @@ use alloc::string::String;
|
|||
use alloc::sync::Arc;
|
||||
use bitflags::bitflags;
|
||||
use core::any::Any;
|
||||
use jinux_frame::vm::VmFrame;
|
||||
|
||||
use super::{DirentWriterContext, FileSystem};
|
||||
use crate::fs::ioctl::IoctlCmd;
|
||||
|
@ -101,6 +102,10 @@ pub trait Inode: Any + Sync + Send {
|
|||
|
||||
fn metadata(&self) -> Metadata;
|
||||
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
|
||||
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
|
||||
|
|
|
@ -4,12 +4,14 @@ pub use access_mode::AccessMode;
|
|||
pub use dirent_writer::{DirentWriter, DirentWriterContext};
|
||||
pub use fs::{FileSystem, SuperBlock};
|
||||
pub use inode::{Inode, InodeMode, InodeType, Metadata, Timespec};
|
||||
pub use page_cache::PageCacheManager;
|
||||
pub use status_flags::StatusFlags;
|
||||
|
||||
mod access_mode;
|
||||
mod dirent_writer;
|
||||
mod fs;
|
||||
mod inode;
|
||||
mod page_cache;
|
||||
mod status_flags;
|
||||
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
use super::Inode;
|
||||
use crate::prelude::*;
|
||||
use crate::vm::vmo::Pager;
|
||||
use jinux_frame::vm::{VmAllocOptions, VmFrame, VmFrameVec};
|
||||
use lru::LruCache;
|
||||
|
||||
pub struct PageCacheManager {
|
||||
pages: Mutex<LruCache<usize, Page>>,
|
||||
backed_inode: Weak<dyn Inode>,
|
||||
}
|
||||
|
||||
impl PageCacheManager {
|
||||
pub fn new(inode: &Weak<dyn Inode>) -> Self {
|
||||
Self {
|
||||
pages: Mutex::new(LruCache::unbounded()),
|
||||
backed_inode: inode.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pager for PageCacheManager {
|
||||
fn commit_page(&self, offset: usize) -> Result<VmFrame> {
|
||||
let page_idx = offset / PAGE_SIZE;
|
||||
let mut pages = self.pages.lock();
|
||||
let frame = if let Some(page) = pages.get(&page_idx) {
|
||||
page.frame()
|
||||
} else {
|
||||
let page = if offset < self.backed_inode.upgrade().unwrap().metadata().size {
|
||||
let mut page = Page::alloc()?;
|
||||
self.backed_inode
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.read_page(page_idx, &page.frame())?;
|
||||
page.set_state(PageState::UpToDate);
|
||||
page
|
||||
} else {
|
||||
Page::alloc_zero()?
|
||||
};
|
||||
let frame = page.frame();
|
||||
pages.put(page_idx, page);
|
||||
frame
|
||||
};
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
fn update_page(&self, offset: usize) -> Result<()> {
|
||||
let page_idx = offset / PAGE_SIZE;
|
||||
let mut pages = self.pages.lock();
|
||||
if let Some(page) = pages.get_mut(&page_idx) {
|
||||
page.set_state(PageState::Dirty);
|
||||
} else {
|
||||
error!("page {} is not in page cache", page_idx);
|
||||
panic!();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decommit_page(&self, offset: usize) -> Result<()> {
|
||||
let page_idx = offset / PAGE_SIZE;
|
||||
let mut pages = self.pages.lock();
|
||||
if let Some(page) = pages.pop(&page_idx) {
|
||||
match page.state() {
|
||||
PageState::Dirty => self
|
||||
.backed_inode
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write_page(page_idx, &page.frame())?,
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
warn!("page {} is not in page cache, do nothing", page_idx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Page {
|
||||
frame: VmFrame,
|
||||
state: PageState,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn alloc() -> Result<Self> {
|
||||
let frame = {
|
||||
let vm_alloc_option = VmAllocOptions::new(1);
|
||||
let mut frames = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||
frames.pop().unwrap()
|
||||
};
|
||||
Ok(Self {
|
||||
frame,
|
||||
state: PageState::Uninit,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn alloc_zero() -> Result<Self> {
|
||||
let frame = {
|
||||
let vm_alloc_option = VmAllocOptions::new(1);
|
||||
let mut frames = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||
frames.zero();
|
||||
frames.pop().unwrap()
|
||||
};
|
||||
Ok(Self {
|
||||
frame,
|
||||
state: PageState::Dirty,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn frame(&self) -> VmFrame {
|
||||
self.frame.clone()
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &PageState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, new_state: PageState) {
|
||||
self.state = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
enum PageState {
|
||||
/// `Uninit` indicates a new allocated page which content has not been initialized.
|
||||
/// The page is available to write, not available to read.
|
||||
Uninit,
|
||||
/// `UpToDate` indicates a page which content is consistent with corresponding disk content.
|
||||
/// The page is available to read and write.
|
||||
UpToDate,
|
||||
/// `Dirty` indicates a page which content has been updated and not written back to underlying disk.
|
||||
/// The page is available to read and write.
|
||||
Dirty,
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use super::utils::{Inode, PageCacheManager};
|
||||
use crate::rights::Rights;
|
||||
use crate::vm::vmo::{Vmo, VmoFlags, VmoOptions};
|
||||
|
||||
pub struct VfsInode {
|
||||
raw_inode: Arc<dyn Inode>,
|
||||
pages: Vmo,
|
||||
}
|
||||
|
||||
impl VfsInode {
|
||||
pub fn new(raw_inode: Arc<dyn Inode>) -> Result<Self> {
|
||||
let page_cache_manager = Arc::new(PageCacheManager::new(&Arc::downgrade(&raw_inode)));
|
||||
let pages = VmoOptions::<Rights>::new(raw_inode.metadata().size)
|
||||
.flags(VmoFlags::RESIZABLE)
|
||||
.pager(page_cache_manager)
|
||||
.alloc()?;
|
||||
Ok(Self { raw_inode, pages })
|
||||
}
|
||||
|
||||
pub fn pages(&self) -> &Vmo {
|
||||
&self.pages
|
||||
}
|
||||
|
||||
pub fn raw_inode(&self) -> &Arc<dyn Inode> {
|
||||
&self.raw_inode
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ use crate::{
|
|||
};
|
||||
|
||||
extern crate alloc;
|
||||
extern crate lru;
|
||||
|
||||
pub mod driver;
|
||||
pub mod error;
|
||||
|
|
|
@ -265,14 +265,14 @@ impl Vmo_ {
|
|||
return_errno_with_message!(Errno::EINVAL, "read range exceeds vmo size");
|
||||
}
|
||||
let read_range = offset..(offset + read_len);
|
||||
let frames = self.ensure_all_pages_exist(read_range, false)?;
|
||||
let frames = self.ensure_all_pages_exist(&read_range, false)?;
|
||||
let read_offset = offset % PAGE_SIZE;
|
||||
Ok(frames.read_bytes(read_offset, buf)?)
|
||||
}
|
||||
|
||||
/// Ensure all pages inside range are backed up vm frames, returns the frames.
|
||||
fn ensure_all_pages_exist(&self, range: Range<usize>, write_page: bool) -> Result<VmFrameVec> {
|
||||
let page_idx_range = get_page_idx_range(&range);
|
||||
fn ensure_all_pages_exist(&self, range: &Range<usize>, write_page: bool) -> Result<VmFrameVec> {
|
||||
let page_idx_range = get_page_idx_range(range);
|
||||
let mut frames = VmFrameVec::empty();
|
||||
for page_idx in page_idx_range {
|
||||
let mut page_frame = self.get_backup_frame(page_idx, write_page, true)?;
|
||||
|
@ -382,9 +382,15 @@ impl Vmo_ {
|
|||
}
|
||||
|
||||
let write_range = offset..(offset + write_len);
|
||||
let frames = self.ensure_all_pages_exist(write_range, true)?;
|
||||
let frames = self.ensure_all_pages_exist(&write_range, true)?;
|
||||
let write_offset = offset % PAGE_SIZE;
|
||||
frames.write_bytes(write_offset, buf)?;
|
||||
if let Some(pager) = &self.inner.lock().pager {
|
||||
let page_idx_range = get_page_idx_range(&write_range);
|
||||
for page_idx in page_idx_range {
|
||||
pager.update_page(page_idx * PAGE_SIZE)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue