Modify some related usages of PageCache and VMO

This commit is contained in:
Chen Chengjun 2026-02-11 09:25:01 +00:00
parent 610504e2e9
commit 22053aad99
13 changed files with 183 additions and 248 deletions

View File

@ -27,8 +27,8 @@ use crate::{
exfat::{constants::*, inode::Ino}, exfat::{constants::*, inode::Ino},
registry::{FsProperties, FsType}, registry::{FsProperties, FsType},
utils::{ utils::{
CachePage, FileSystem, FsEventSubscriberStats, FsFlags, Inode, PageCache, FileSystem, FsEventSubscriberStats, FsFlags, Inode, LockedCachePage, PageCache,
PageCacheBackend, SuperBlock, PageCacheBackend, PageCacheOps, SuperBlock,
}, },
}, },
prelude::*, prelude::*,
@ -83,7 +83,7 @@ impl ExfatFs {
fat_cache: RwLock::new(LruCache::<ClusterID, ClusterID>::new( fat_cache: RwLock::new(LruCache::<ClusterID, ClusterID>::new(
NonZeroUsize::new(FAT_LRU_CACHE_SIZE).unwrap(), NonZeroUsize::new(FAT_LRU_CACHE_SIZE).unwrap(),
)), )),
meta_cache: PageCache::with_capacity(fs_size, weak_self.clone() as _).unwrap(), meta_cache: PageCacheOps::with_capacity(fs_size, weak_self.clone() as _).unwrap(),
mutex: Mutex::new(()), mutex: Mutex::new(()),
fs_event_subscriber_stats: FsEventSubscriberStats::new(), fs_event_subscriber_stats: FsEventSubscriberStats::new(),
}); });
@ -153,17 +153,17 @@ impl ExfatFs {
} }
pub(super) fn sync_meta_at(&self, range: core::ops::Range<usize>) -> Result<()> { pub(super) fn sync_meta_at(&self, range: core::ops::Range<usize>) -> Result<()> {
self.meta_cache.pages().decommit(range)?; self.meta_cache.flush_range(range)?;
Ok(()) Ok(())
} }
pub(super) fn write_meta_at(&self, offset: usize, buf: &[u8]) -> Result<()> { pub(super) fn write_meta_at(&self, offset: usize, buf: &[u8]) -> Result<()> {
self.meta_cache.pages().write_bytes(offset, buf)?; self.meta_cache.write_bytes(offset, buf)?;
Ok(()) Ok(())
} }
pub(super) fn read_meta_at(&self, offset: usize, buf: &mut [u8]) -> Result<()> { pub(super) fn read_meta_at(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
self.meta_cache.pages().read_bytes(offset, buf)?; self.meta_cache.read_bytes(offset, buf)?;
Ok(()) Ok(())
} }
@ -422,7 +422,7 @@ impl FileSystem for ExfatFs {
for inode in self.inodes.read().values() { for inode in self.inodes.read().values() {
inode.sync_all()?; inode.sync_all()?;
} }
self.meta_cache.evict_range(0..self.fs_size())?; self.meta_cache.flush_range(0..self.fs_size())?;
Ok(()) Ok(())
} }

View File

@ -29,8 +29,9 @@ use crate::{
exfat::{dentry::ExfatDentryIterator, fat::ExfatChain, fs::ExfatFs}, exfat::{dentry::ExfatDentryIterator, fat::ExfatChain, fs::ExfatFs},
path::{is_dot, is_dot_or_dotdot, is_dotdot}, path::{is_dot, is_dot_or_dotdot, is_dotdot},
utils::{ utils::{
CachePage, DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType, Metadata, DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType, LockedCachePage,
MknodType, PageCache, PageCacheBackend, StatusFlags, SymbolicLink, mkmod, Metadata, MknodType, PageCache, PageCacheBackend, PageCacheOps, StatusFlags,
SymbolicLink, mkmod,
}, },
}, },
prelude::*, prelude::*,
@ -254,7 +255,7 @@ impl ExfatInodeInner {
return Ok((0, 0)); return Ok((0, 0));
} }
let iterator = ExfatDentryIterator::new(self.page_cache.pages(), 0, Some(self.size))?; let iterator = ExfatDentryIterator::new(&self.page_cache, 0, Some(self.size))?;
let mut sub_inodes = 0; let mut sub_inodes = 0;
let mut sub_dirs = 0; let mut sub_dirs = 0;
for dentry_result in iterator { for dentry_result in iterator {
@ -357,7 +358,7 @@ impl ExfatInodeInner {
page_cache.write_bytes(start_off, &bytes)?; page_cache.write_bytes(start_off, &bytes)?;
if sync { if sync {
page_cache.decommit(start_off..start_off + bytes.len())?; page_cache.flush_range(start_off..start_off + bytes.len())?;
} }
Ok(()) Ok(())
@ -383,7 +384,7 @@ impl ExfatInodeInner {
let fs = self.fs(); let fs = self.fs();
let cluster_size = fs.cluster_size(); let cluster_size = fs.cluster_size();
let mut iter = ExfatDentryIterator::new(self.page_cache.pages(), offset, None)?; let mut iter = ExfatDentryIterator::new(&self.page_cache, offset, None)?;
let mut dir_read = 0; let mut dir_read = 0;
let mut current_off = offset; let mut current_off = offset;
@ -590,7 +591,7 @@ impl ExfatInodeInner {
} }
fn sync_data(&self, fs_guard: &MutexGuard<()>) -> Result<()> { fn sync_data(&self, fs_guard: &MutexGuard<()>) -> Result<()> {
self.page_cache.evict_range(0..self.size)?; self.page_cache.flush_range(0..self.size)?;
Ok(()) Ok(())
} }
@ -634,7 +635,7 @@ impl ExfatInode {
let end = file_size.min(offset + writer.avail()); let end = file_size.min(offset + writer.avail());
(start, end - start) (start, end - start)
}; };
inner.page_cache.pages().read(read_off, writer)?; inner.page_cache.read(read_off, writer)?;
inner.upgrade().update_atime()?; inner.upgrade().update_atime()?;
Ok(read_len) Ok(read_len)
@ -663,7 +664,7 @@ impl ExfatInode {
inner inner
.page_cache .page_cache
.discard_range(read_off..read_off + read_len); .discard_range(read_off..read_off + read_len)?;
let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice); let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice);
@ -709,14 +710,14 @@ impl ExfatInode {
if new_size > file_allocated_size { if new_size > file_allocated_size {
inner.resize(new_size, &fs_guard)?; inner.resize(new_size, &fs_guard)?;
} }
inner.page_cache.resize(new_size)?; inner.page_cache.resize(new_size, file_size)?;
} }
new_size.max(file_size) new_size.max(file_size)
}; };
// Locks released here, so that file write can be parallelized. // Locks released here, so that file write can be parallelized.
let inner = self.inner.upread(); let inner = self.inner.upread();
inner.page_cache.pages().write(offset, reader)?; inner.page_cache.write(offset, reader)?;
// Update timestamps and size. // Update timestamps and size.
{ {
@ -754,7 +755,7 @@ impl ExfatInode {
let start = offset.min(file_size); let start = offset.min(file_size);
let end = end_offset.min(file_size); let end = end_offset.min(file_size);
inner.page_cache.discard_range(start..end); inner.page_cache.discard_range(start..end)?;
let new_size = { let new_size = {
let mut inner = inner.upgrade(); let mut inner = inner.upgrade();
@ -764,7 +765,7 @@ impl ExfatInode {
if end_offset > file_allocated_size { if end_offset > file_allocated_size {
inner.resize(end_offset, &fs_guard)?; inner.resize(end_offset, &fs_guard)?;
} }
inner.page_cache.resize(end_offset)?; inner.page_cache.resize(end_offset, file_size)?;
} }
file_size.max(end_offset) file_size.max(end_offset)
}; };
@ -813,8 +814,9 @@ impl ExfatInode {
let inner = self.inner.write(); let inner = self.inner.write();
let fs = inner.fs(); let fs = inner.fs();
let fs_guard = fs.lock(); let fs_guard = fs.lock();
self.inner.write().resize(0, &fs_guard)?; let mut inner = self.inner.write();
self.inner.read().page_cache.resize(0)?; inner.resize(0, &fs_guard)?;
inner.page_cache.resize(0, inner.size)?;
Ok(()) Ok(())
} }
@ -845,7 +847,6 @@ impl ExfatInode {
let size = root_chain.num_clusters() as usize * sb.cluster_size as usize; let size = root_chain.num_clusters() as usize * sb.cluster_size as usize;
let name = ExfatName::new(); let name = ExfatName::new();
let inode = Arc::new_cyclic(|weak_self| ExfatInode { let inode = Arc::new_cyclic(|weak_self| ExfatInode {
inner: RwMutex::new(ExfatInodeInner { inner: RwMutex::new(ExfatInodeInner {
ino: EXFAT_ROOT_INO, ino: EXFAT_ROOT_INO,
@ -866,7 +867,7 @@ impl ExfatInode {
is_deleted: false, is_deleted: false,
parent_hash: 0, parent_hash: 0,
fs: fs_weak, fs: fs_weak,
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(), page_cache: PageCacheOps::with_capacity(size, weak_self.clone() as _).unwrap(),
}), }),
extension: Extension::new(), extension: Extension::new(),
}); });
@ -976,7 +977,7 @@ impl ExfatInode {
is_deleted: false, is_deleted: false,
parent_hash, parent_hash,
fs: fs_weak, fs: fs_weak,
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(), page_cache: PageCacheOps::with_capacity(size, weak_self.clone() as _).unwrap(),
}), }),
extension: Extension::new(), extension: Extension::new(),
}); });
@ -1019,8 +1020,7 @@ impl ExfatInode {
fn find_empty_dentries(&self, num_dentries: usize, fs_guard: &MutexGuard<()>) -> Result<usize> { fn find_empty_dentries(&self, num_dentries: usize, fs_guard: &MutexGuard<()>) -> Result<usize> {
let inner = self.inner.upread(); let inner = self.inner.upread();
let dentry_iterator = let dentry_iterator = ExfatDentryIterator::new(&inner.page_cache, 0, Some(inner.size))?;
ExfatDentryIterator::new(inner.page_cache.pages(), 0, Some(inner.size))?;
let mut contiguous_unused = 0; let mut contiguous_unused = 0;
let mut entry_id = 0; let mut entry_id = 0;
@ -1063,15 +1063,16 @@ impl ExfatInode {
inner.size_allocated = new_size_allocated; inner.size_allocated = new_size_allocated;
inner.size = new_size_allocated; inner.size = new_size_allocated;
inner.page_cache.resize(new_size_allocated)?; inner
.page_cache
.resize(new_size_allocated, old_size_allocated)?;
} }
let inner = self.inner.read(); let inner = self.inner.read();
// We need to write unused dentries (i.e. 0) to page cache. // We need to write unused dentries (i.e. 0) to page cache.
inner inner
.page_cache .page_cache
.pages() .fill_zeros(old_size_allocated..new_size_allocated)?;
.clear(old_size_allocated..new_size_allocated)?;
Ok(entry_id) Ok(entry_id)
} }
@ -1106,7 +1107,6 @@ impl ExfatInode {
let inner = self.inner.upread(); let inner = self.inner.upread();
inner inner
.page_cache .page_cache
.pages()
.write_bytes(start_off, &dentry_set.to_le_bytes())?; .write_bytes(start_off, &dentry_set.to_le_bytes())?;
let mut inner = inner.upgrade(); let mut inner = inner.upgrade();
@ -1144,11 +1144,7 @@ impl ExfatInode {
let fs = self.inner.read().fs(); let fs = self.inner.read().fs();
let mut buf = vec![0; len]; let mut buf = vec![0; len];
self.inner self.inner.read().page_cache.read_bytes(offset, &mut buf)?;
.read()
.page_cache
.pages()
.read_bytes(offset, &mut buf)?;
let num_dentry = len / DENTRY_SIZE; let num_dentry = len / DENTRY_SIZE;
@ -1166,11 +1162,7 @@ impl ExfatInode {
buf[buf_offset] &= 0x7F; buf[buf_offset] &= 0x7F;
} }
self.inner self.inner.read().page_cache.write_bytes(offset, &buf)?;
.read()
.page_cache
.pages()
.write_bytes(offset, &buf)?;
// FIXME: We must make sure that there are no spare tailing clusters in a directory. // FIXME: We must make sure that there are no spare tailing clusters in a directory.
Ok(()) Ok(())
@ -1242,8 +1234,9 @@ impl ExfatInode {
let is_dir = inode.inner.read().inode_type.is_directory(); let is_dir = inode.inner.read().inode_type.is_directory();
if delete_contents { if delete_contents {
if is_dir { if is_dir {
inode.inner.write().resize(0, fs_guard)?; let mut inner = inode.inner.write();
inode.inner.read().page_cache.resize(0)?; inner.resize(0, fs_guard)?;
inner.page_cache.resize(0, inner.size)?;
} }
// Set the delete flag. // Set the delete flag.
inode.inner.write().is_deleted = true; inode.inner.write().is_deleted = true;
@ -1344,7 +1337,7 @@ impl Inode for ExfatInode {
// We will delay updating the page_cache size when enlarging an inode until the real write. // We will delay updating the page_cache size when enlarging an inode until the real write.
if new_size < file_size { if new_size < file_size {
self.inner.read().page_cache.resize(new_size)?; self.inner.read().page_cache.resize(new_size, file_size)?;
} }
// Sync this inode since size has changed. // Sync this inode since size has changed.
@ -1449,7 +1442,7 @@ impl Inode for ExfatInode {
} }
fn page_cache(&self) -> Option<Arc<Vmo>> { fn page_cache(&self) -> Option<Arc<Vmo>> {
Some(self.inner.read().page_cache.pages().clone()) Some(self.inner.read().page_cache.clone())
} }
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> { fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {

View File

@ -9,7 +9,7 @@ use super::{
prelude::*, prelude::*,
super_block::SuperBlock, super_block::SuperBlock,
}; };
use crate::fs::utils::IdBitmap; use crate::fs::utils::{IdBitmap, LockedCachePage};
/// Blocks are clustered into block groups in order to reduce fragmentation and minimise /// Blocks are clustered into block groups in order to reduce fragmentation and minimise
/// the amount of head seeking when reading a large amount of consecutive data. /// the amount of head seeking when reading a large amount of consecutive data.
@ -91,7 +91,7 @@ impl BlockGroup {
}; };
let raw_inodes_cache = let raw_inodes_cache =
PageCache::with_capacity(raw_inodes_size, Arc::downgrade(&bg_impl) as _)?; PageCacheOps::with_capacity(raw_inodes_size, Arc::downgrade(&bg_impl) as _)?;
Ok(Self { Ok(Self {
idx, idx,
@ -134,10 +134,7 @@ impl BlockGroup {
let fs = self.fs(); let fs = self.fs();
let raw_inode = { let raw_inode = {
let offset = (inode_idx as usize) * fs.inode_size(); let offset = (inode_idx as usize) * fs.inode_size();
self.raw_inodes_cache self.raw_inodes_cache.read_val::<RawInode>(offset).unwrap()
.pages()
.read_val::<RawInode>(offset)
.unwrap()
}; };
let inode_desc = Dirty::new(InodeDesc::try_from(raw_inode)?); let inode_desc = Dirty::new(InodeDesc::try_from(raw_inode)?);
let ino = inode_idx + self.idx as u32 * fs.inodes_per_group() + 1; let ino = inode_idx + self.idx as u32 * fs.inodes_per_group() + 1;
@ -217,10 +214,7 @@ impl BlockGroup {
/// Writes back the raw inode metadata to the raw inode metadata cache. /// Writes back the raw inode metadata to the raw inode metadata cache.
pub fn sync_raw_inode(&self, inode_idx: u32, raw_inode: &RawInode) { pub fn sync_raw_inode(&self, inode_idx: u32, raw_inode: &RawInode) {
let offset = (inode_idx as usize) * self.fs().inode_size(); let offset = (inode_idx as usize) * self.fs().inode_size();
self.raw_inodes_cache self.raw_inodes_cache.write_val(offset, raw_inode).unwrap();
.pages()
.write_val(offset, raw_inode)
.unwrap();
} }
/// Writes back the metadata of this group. /// Writes back the metadata of this group.
@ -296,8 +290,7 @@ impl BlockGroup {
// Writes back the raw inode metadata. // Writes back the raw inode metadata.
self.raw_inodes_cache self.raw_inodes_cache
.pages() .flush_range(0..self.bg_impl.raw_inodes_size)?;
.decommit(0..self.bg_impl.raw_inodes_size)?;
Ok(()) Ok(())
} }

View File

@ -257,7 +257,7 @@ impl<'a> DirEntryReader<'a> {
let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len]; let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len];
let offset = entry_item.offset + DirEntry::HEADER_LEN; let offset = entry_item.offset + DirEntry::HEADER_LEN;
self.page_cache.pages().read_bytes(offset, name_buf)?; self.page_cache.read_bytes(offset, name_buf)?;
Ok(name_buf) Ok(name_buf)
} }
} }
@ -265,13 +265,12 @@ impl<'a> DirEntryReader<'a> {
impl DirEntryIter<'_> { impl DirEntryIter<'_> {
/// Reads a `DirEntryItem` at the current offset. /// Reads a `DirEntryItem` at the current offset.
fn read_next_dir_entry(&mut self) -> Option<DirEntryItem> { fn read_next_dir_entry(&mut self) -> Option<DirEntryItem> {
if self.offset >= self.page_cache.pages().size() { if self.offset >= self.page_cache.size() {
return None; return None;
}; };
let header = self let header = self
.page_cache .page_cache
.pages()
.read_val::<DirEntryHeader>(self.offset) .read_val::<DirEntryHeader>(self.offset)
.ok()?; .ok()?;
@ -391,9 +390,8 @@ impl<'a> DirEntryWriter<'a> {
/// Writes a `DirEntry` at the current offset. The name is written after the header. /// Writes a `DirEntry` at the current offset. The name is written after the header.
pub fn write_entry(&mut self, header: &DirEntryHeader, name: &str) -> Result<()> { pub fn write_entry(&mut self, header: &DirEntryHeader, name: &str) -> Result<()> {
self.page_cache.pages().write_val(self.offset, header)?; self.page_cache.write_val(self.offset, header)?;
self.page_cache self.page_cache
.pages()
.write_bytes(self.offset + DirEntry::HEADER_LEN, name.as_bytes())?; .write_bytes(self.offset + DirEntry::HEADER_LEN, name.as_bytes())?;
self.offset += header.record_len as usize; self.offset += header.record_len as usize;
@ -402,16 +400,17 @@ impl<'a> DirEntryWriter<'a> {
/// Writes the header of a `DirEntry` at the current offset. /// Writes the header of a `DirEntry` at the current offset.
pub fn write_header_only(&mut self, header: &DirEntryHeader) -> Result<()> { pub fn write_header_only(&mut self, header: &DirEntryHeader) -> Result<()> {
self.page_cache.pages().write_val(self.offset, header)?; self.page_cache.write_val(self.offset, header)?;
self.offset += header.record_len as usize; self.offset += header.record_len as usize;
Ok(()) Ok(())
} }
/// Initializes two special `DirEntry`s ("." and "..") with the given inode numbers. /// Initializes two special `DirEntry`s ("." and "..") with the given inode numbers.
pub fn init_dir(&mut self, self_ino: u32, parent_ino: u32) -> Result<()> { pub fn init_dir(&mut self, self_ino: u32, parent_ino: u32) -> Result<()> {
debug_assert!(self.page_cache.pages().size() == 0 && self.offset == 0); let old_size = self.page_cache.size();
debug_assert!(old_size == 0 && self.offset == 0);
self.page_cache.pages().resize(BLOCK_SIZE)?; self.page_cache.resize(BLOCK_SIZE, old_size)?;
let self_header = DirEntryHeader::new(self_ino, InodeType::Dir, 1); let self_header = DirEntryHeader::new(self_ino, InodeType::Dir, 1);
self.write_entry(&self_header, ".")?; self.write_entry(&self_header, ".")?;
@ -480,9 +479,9 @@ impl<'a> DirEntryWriter<'a> {
fn append_entry_in_the_end(&mut self, mut header: DirEntryHeader, name: &str) -> Result<()> { fn append_entry_in_the_end(&mut self, mut header: DirEntryHeader, name: &str) -> Result<()> {
// Resize and append it at the new block. // Resize and append it at the new block.
let old_size = self.page_cache.pages().size(); let old_size = self.page_cache.size();
let new_size = old_size + BLOCK_SIZE; let new_size = old_size + BLOCK_SIZE;
self.page_cache.resize(new_size)?; self.page_cache.resize(new_size, old_size)?;
header.record_len = BLOCK_SIZE as _; header.record_len = BLOCK_SIZE as _;
self.offset = old_size; self.offset = old_size;
@ -513,7 +512,7 @@ impl<'a> DirEntryWriter<'a> {
if is_last_entry { if is_last_entry {
// Shrink the size. // Shrink the size.
let new_size = pre_offset.align_up(BLOCK_SIZE); let new_size = pre_offset.align_up(BLOCK_SIZE);
self.page_cache.resize(new_size)?; self.page_cache.resize(new_size, self.page_cache.size())?;
pre_entry_item.set_record_len(new_size - pre_offset); pre_entry_item.set_record_len(new_size - pre_offset);
self.offset = pre_offset; self.offset = pre_offset;
self.write_header_only(&pre_entry_item.header)?; self.write_header_only(&pre_entry_item.header)?;
@ -566,7 +565,7 @@ impl<'a> DirEntryWriter<'a> {
let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len]; let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len];
let offset = item.offset + DirEntry::HEADER_LEN; let offset = item.offset + DirEntry::HEADER_LEN;
self.page_cache.pages().read_bytes(offset, name_buf)?; self.page_cache.read_bytes(offset, name_buf)?;
Ok(name_buf) Ok(name_buf)
} }

View File

@ -23,8 +23,8 @@ use crate::{
path::{is_dot, is_dot_or_dotdot, is_dotdot}, path::{is_dot, is_dot_or_dotdot, is_dotdot},
pipe::Pipe, pipe::Pipe,
utils::{ utils::{
Extension, FallocMode, Inode as _, InodeMode, Metadata, Permission, XattrName, Extension, FallocMode, Inode as _, InodeMode, LockedCachePage, Metadata, Permission,
XattrNamespace, XattrSetFlags, XattrName, XattrNamespace, XattrSetFlags,
}, },
}, },
process::{Gid, Uid, posix_thread::AsPosixThread}, process::{Gid, Uid, posix_thread::AsPosixThread},
@ -99,7 +99,7 @@ impl Inode {
} }
pub fn page_cache(&self) -> Arc<Vmo> { pub fn page_cache(&self) -> Arc<Vmo> {
self.inner.read().page_cache.pages().clone() self.inner.read().page_cache.clone()
} }
pub fn metadata(&self) -> Metadata { pub fn metadata(&self) -> Metadata {
@ -976,7 +976,7 @@ impl InodeInner {
let num_page_bytes = desc.num_page_bytes(); let num_page_bytes = desc.num_page_bytes();
let inode_impl = InodeImpl::new(desc, weak_self, fs); let inode_impl = InodeImpl::new(desc, weak_self, fs);
Self { Self {
page_cache: PageCache::with_capacity( page_cache: PageCacheOps::with_capacity(
num_page_bytes, num_page_bytes,
Arc::downgrade(&inode_impl.block_manager) as _, Arc::downgrade(&inode_impl.block_manager) as _,
) )
@ -986,7 +986,8 @@ impl InodeInner {
} }
pub fn resize(&mut self, new_size: usize) -> Result<()> { pub fn resize(&mut self, new_size: usize) -> Result<()> {
self.page_cache.resize(new_size)?; self.page_cache
.resize(new_size, self.inode_impl.file_size())?;
self.inode_impl.resize(new_size)?; self.inode_impl.resize(new_size)?;
Ok(()) Ok(())
} }
@ -999,7 +1000,7 @@ impl InodeInner {
(start, end - start) (start, end - start)
}; };
self.page_cache.pages().read(offset, writer)?; self.page_cache.read(offset, writer)?;
Ok(read_len) Ok(read_len)
} }
@ -1016,7 +1017,7 @@ impl InodeInner {
if read_len == 0 { if read_len == 0 {
return Ok(read_len); return Ok(read_len);
} }
self.page_cache.discard_range(offset..offset + read_len); self.page_cache.discard_range(offset..offset + read_len)?;
let start_bid = Bid::from_offset(offset).to_raw() as Ext2Bid; let start_bid = Bid::from_offset(offset).to_raw() as Ext2Bid;
let buf_nblocks = read_len / BLOCK_SIZE; let buf_nblocks = read_len / BLOCK_SIZE;
@ -1028,15 +1029,16 @@ impl InodeInner {
pub fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> { pub fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
let write_len = reader.remain(); let write_len = reader.remain();
self.page_cache.pages().write(offset, reader)?; self.page_cache.write(offset, reader)?;
Ok(write_len) Ok(write_len)
} }
pub fn extend_write_at(&mut self, offset: usize, reader: &mut VmReader) -> Result<usize> { pub fn extend_write_at(&mut self, offset: usize, reader: &mut VmReader) -> Result<usize> {
let write_len = reader.remain(); let write_len = reader.remain();
let new_size = offset + write_len; let new_size = offset + write_len;
self.page_cache.resize(new_size.align_up(BLOCK_SIZE))?; self.page_cache
self.page_cache.pages().write(offset, reader)?; .resize(new_size.align_up(BLOCK_SIZE), self.inode_impl.file_size())?;
self.page_cache.write(offset, reader)?;
self.inode_impl.resize(new_size)?; self.inode_impl.resize(new_size)?;
Ok(write_len) Ok(write_len)
} }
@ -1049,7 +1051,7 @@ impl InodeInner {
let start = offset.min(file_size); let start = offset.min(file_size);
let end = end_offset.min(file_size); let end = end_offset.min(file_size);
self.page_cache.discard_range(start..end); self.page_cache.discard_range(start..end)?;
if end_offset > file_size { if end_offset > file_size {
self.inode_impl.resize(end_offset)?; self.inode_impl.resize(end_offset)?;
@ -1068,12 +1070,14 @@ impl InodeInner {
return self.inode_impl.write_fast_link(target); return self.inode_impl.write_fast_link(target);
} }
self.page_cache.resize(target.len())?;
self.page_cache.pages().write_bytes(0, target.as_bytes())?;
let file_size = self.inode_impl.file_size(); let file_size = self.inode_impl.file_size();
if file_size != target.len() { if file_size != target.len() {
self.page_cache.resize(target.len(), file_size)?;
self.inode_impl.resize(target.len())?; self.inode_impl.resize(target.len())?;
} }
self.page_cache.write_bytes(0, target.as_bytes())?;
Ok(()) Ok(())
} }
@ -1084,9 +1088,7 @@ impl InodeInner {
} }
let mut symlink = vec![0u8; file_size]; let mut symlink = vec![0u8; file_size];
self.page_cache self.page_cache.read_bytes(0, symlink.as_mut_slice())?;
.pages()
.read_bytes(0, symlink.as_mut_slice())?;
Ok(String::from_utf8(symlink)?) Ok(String::from_utf8(symlink)?)
} }
@ -1125,7 +1127,7 @@ impl InodeInner {
)?; )?;
let file_size = self.file_size(); let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size(); let page_cache_size = self.page_cache.size();
if page_cache_size > file_size { if page_cache_size > file_size {
self.inode_impl.resize(page_cache_size)?; self.inode_impl.resize(page_cache_size)?;
} }
@ -1141,7 +1143,7 @@ impl InodeInner {
pub fn remove_entry_at(&mut self, name: &str, offset: usize) -> Result<()> { pub fn remove_entry_at(&mut self, name: &str, offset: usize) -> Result<()> {
let removed_entry = DirEntryWriter::new(&self.page_cache, offset).remove_entry(name)?; let removed_entry = DirEntryWriter::new(&self.page_cache, offset).remove_entry(name)?;
let file_size = self.file_size(); let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size(); let page_cache_size = self.page_cache.size();
if page_cache_size < file_size { if page_cache_size < file_size {
self.inode_impl.resize(page_cache_size)?; self.inode_impl.resize(page_cache_size)?;
} }
@ -1154,7 +1156,7 @@ impl InodeInner {
pub fn rename_entry_at(&mut self, old_name: &str, new_name: &str, offset: usize) -> Result<()> { pub fn rename_entry_at(&mut self, old_name: &str, new_name: &str, offset: usize) -> Result<()> {
DirEntryWriter::new(&self.page_cache, offset).rename_entry(old_name, new_name)?; DirEntryWriter::new(&self.page_cache, offset).rename_entry(old_name, new_name)?;
let file_size = self.file_size(); let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size(); let page_cache_size = self.page_cache.size();
if page_cache_size != file_size { if page_cache_size != file_size {
self.inode_impl.resize(page_cache_size)?; self.inode_impl.resize(page_cache_size)?;
} }
@ -1172,7 +1174,7 @@ impl InodeInner {
pub fn sync_data(&self) -> Result<()> { pub fn sync_data(&self) -> Result<()> {
// Writes back the data in page cache. // Writes back the data in page cache.
let file_size = self.file_size(); let file_size = self.file_size();
self.page_cache.evict_range(0..file_size)?; self.page_cache.flush_range(0..file_size)?;
Ok(()) Ok(())
} }
} }
@ -1828,7 +1830,7 @@ impl InodeImpl {
writer: &mut VmWriter, writer: &mut VmWriter,
) -> Result<BioWaiter>; ) -> Result<BioWaiter>;
pub fn read_blocks(&self, bid: Ext2Bid, nblocks: usize, writer: &mut VmWriter) -> Result<()>; pub fn read_blocks(&self, bid: Ext2Bid, nblocks: usize, writer: &mut VmWriter) -> Result<()>;
pub fn read_block_async(&self, bid: Ext2Bid, frame: &CachePage) -> Result<BioWaiter>; pub fn read_block_async(&self, bid: Ext2Bid, frame: LockedCachePage) -> Result<BioWaiter>;
pub fn write_blocks_async( pub fn write_blocks_async(
&self, &self,
bid: Ext2Bid, bid: Ext2Bid,
@ -1836,7 +1838,7 @@ impl InodeImpl {
reader: &mut VmReader, reader: &mut VmReader,
) -> Result<BioWaiter>; ) -> Result<BioWaiter>;
pub fn write_blocks(&self, bid: Ext2Bid, nblocks: usize, reader: &mut VmReader) -> Result<()>; pub fn write_blocks(&self, bid: Ext2Bid, nblocks: usize, reader: &mut VmReader) -> Result<()>;
pub fn write_block_async(&self, bid: Ext2Bid, frame: &CachePage) -> Result<BioWaiter>; pub fn write_block_async(&self, bid: Ext2Bid, frame: LockedCachePage) -> Result<BioWaiter>;
} }
/// Manages the inode blocks and block I/O operations. /// Manages the inode blocks and block I/O operations.
@ -1965,12 +1967,12 @@ impl InodeBlockManager {
} }
impl PageCacheBackend for InodeBlockManager { impl PageCacheBackend for InodeBlockManager {
fn read_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter> { fn read_page_async(&self, idx: usize, frame: LockedCachePage) -> Result<BioWaiter> {
let bid = idx as Ext2Bid; let bid = idx as Ext2Bid;
self.read_block_async(bid, frame) self.read_block_async(bid, frame)
} }
fn write_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter> { fn write_page_async(&self, idx: usize, frame: LockedCachePage) -> Result<BioWaiter> {
let bid = idx as Ext2Bid; let bid = idx as Ext2Bid;
self.write_block_async(bid, frame) self.write_block_async(bid, frame)
} }

View File

@ -19,7 +19,7 @@ pub(super) use ostd::{
pub(super) use super::utils::{Dirty, IsPowerOf}; pub(super) use super::utils::{Dirty, IsPowerOf};
pub(super) use crate::{ pub(super) use crate::{
fs::utils::{ fs::utils::{
CStr256, CachePage, DirentVisitor, InodeType, PageCache, PageCacheBackend, Str16, Str64, CStr256, DirentVisitor, InodeType, PageCache, PageCacheBackend, PageCacheOps, Str16, Str64,
}, },
prelude::*, prelude::*,
time::UnixTime, time::UnixTime,

View File

@ -6,14 +6,10 @@ use core::{
}; };
use align_ext::AlignExt; use align_ext::AlignExt;
use aster_block::bio::BioWaiter;
use aster_util::slot_vec::SlotVec; use aster_util::slot_vec::SlotVec;
use device_id::DeviceId; use device_id::DeviceId;
use hashbrown::HashMap; use hashbrown::HashMap;
use ostd::{ use ostd::sync::{PreemptDisabled, RwLockWriteGuard};
mm::{HasSize, io_util::HasVmReaderWriter},
sync::{PreemptDisabled, RwLockWriteGuard},
};
use super::{memfd::MemfdInode, xattr::RamXattr, *}; use super::{memfd::MemfdInode, xattr::RamXattr, *};
use crate::{ use crate::{
@ -25,16 +21,16 @@ use crate::{
pipe::Pipe, pipe::Pipe,
registry::{FsProperties, FsType}, registry::{FsProperties, FsType},
utils::{ utils::{
AccessMode, CStr256, CachePage, DirentVisitor, Extension, FallocMode, FileSystem, AccessMode, CStr256, DirentVisitor, Extension, FallocMode, FileSystem,
FsEventSubscriberStats, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata, FsEventSubscriberStats, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata,
MknodType, PageCache, PageCacheBackend, Permission, StatusFlags, SuperBlock, MknodType, PageCache, PageCacheOps, Permission, StatusFlags, SuperBlock, SymbolicLink,
SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, mkmod, XattrName, XattrNamespace, XattrSetFlags, mkmod,
}, },
}, },
prelude::*, prelude::*,
process::{Gid, Uid}, process::{Gid, Uid},
time::clocks::RealTimeCoarseClock, time::clocks::RealTimeCoarseClock,
vm::vmo::Vmo, vm::vmo::{Vmo, VmoFlags, VmoOptions},
}; };
/// A volatile file system whose data and metadata exists only in memory. /// A volatile file system whose data and metadata exists only in memory.
@ -136,8 +132,13 @@ impl Inner {
Self::Dir(RwLock::new(DirEntry::new(this, parent))) Self::Dir(RwLock::new(DirEntry::new(this, parent)))
} }
pub(self) fn new_file(this: Weak<RamInode>) -> Self { pub(self) fn new_file() -> Self {
Self::File(PageCache::new(this).unwrap()) Self::File(
VmoOptions::new(0)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap(),
)
} }
pub(self) fn new_symlink() -> Self { pub(self) fn new_symlink() -> Self {
@ -160,8 +161,13 @@ impl Inner {
Self::NamedPipe(Pipe::new()) Self::NamedPipe(Pipe::new())
} }
pub(self) fn new_file_in_memfd(this: Weak<MemfdInode>) -> Self { pub(self) fn new_file_in_memfd() -> Self {
Self::File(PageCache::new(this).unwrap()) Self::File(
VmoOptions::new(0)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap(),
)
} }
fn as_direntry(&self) -> Option<&RwLock<DirEntry>> { fn as_direntry(&self) -> Option<&RwLock<DirEntry>> {
@ -456,7 +462,7 @@ impl RamInode {
fn new_file(fs: &Arc<RamFs>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> { fn new_file(fs: &Arc<RamFs>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> {
Arc::new_cyclic(|weak_self| RamInode { Arc::new_cyclic(|weak_self| RamInode {
inner: Inner::new_file(weak_self.clone()), inner: Inner::new_file(),
metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)), metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)),
ino: fs.alloc_id(), ino: fs.alloc_id(),
typ: InodeType::File, typ: InodeType::File,
@ -475,7 +481,7 @@ impl RamInode {
gid: Gid, gid: Gid,
) -> Self { ) -> Self {
Self { Self {
inner: Inner::new_file_in_memfd(weak_self.clone()), inner: Inner::new_file_in_memfd(),
metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)), metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)),
ino: weak_self.as_ptr() as u64, ino: weak_self.as_ptr() as u64,
typ: InodeType::File, typ: InodeType::File,
@ -566,23 +572,6 @@ impl RamInode {
} }
} }
impl PageCacheBackend for RamInode {
fn read_page_async(&self, _idx: usize, frame: &CachePage) -> Result<BioWaiter> {
// Initially, any block/page in a RamFs inode contains all zeros
frame.writer().fill_zeros(frame.size());
Ok(BioWaiter::new())
}
fn write_page_async(&self, _idx: usize, _frame: &CachePage) -> Result<BioWaiter> {
// do nothing
Ok(BioWaiter::new())
}
fn npages(&self) -> usize {
self.metadata.lock().blocks
}
}
impl InodeIo for RamInode { impl InodeIo for RamInode {
fn read_at( fn read_at(
&self, &self,
@ -598,7 +587,7 @@ impl InodeIo for RamInode {
let end = file_size.min(offset + writer.avail()); let end = file_size.min(offset + writer.avail());
(start, end - start) (start, end - start)
}; };
page_cache.pages().read(offset, writer)?; page_cache.read(offset, writer)?;
read_len read_len
} }
_ => return_errno_with_message!(Errno::EISDIR, "read is not supported"), _ => return_errno_with_message!(Errno::EISDIR, "read is not supported"),
@ -626,9 +615,9 @@ impl InodeIo for RamInode {
let should_expand_size = new_size > file_size; let should_expand_size = new_size > file_size;
let new_size_aligned = new_size.align_up(BLOCK_SIZE); let new_size_aligned = new_size.align_up(BLOCK_SIZE);
if should_expand_size { if should_expand_size {
page_cache.resize(new_size_aligned)?; page_cache.resize(new_size_aligned, file_size)?;
} }
page_cache.pages().write(offset, reader)?; page_cache.write(offset, reader)?;
let now = now(); let now = now();
let mut inode_meta = self.metadata.lock(); let mut inode_meta = self.metadata.lock();
@ -648,9 +637,7 @@ impl InodeIo for RamInode {
impl Inode for RamInode { impl Inode for RamInode {
fn page_cache(&self) -> Option<Arc<Vmo>> { fn page_cache(&self) -> Option<Arc<Vmo>> {
self.inner self.inner.as_file().cloned()
.as_file()
.map(|page_cache| page_cache.pages().clone())
} }
fn size(&self) -> usize { fn size(&self) -> usize {
@ -671,7 +658,7 @@ impl Inode for RamInode {
} }
let page_cache = self.inner.as_file().unwrap(); let page_cache = self.inner.as_file().unwrap();
page_cache.resize(new_size)?; page_cache.resize(new_size, file_size)?;
let now = now(); let now = now();
let mut inode_meta = self.metadata.lock(); let mut inode_meta = self.metadata.lock();

View File

@ -6,7 +6,6 @@ use alloc::format;
use core::time::Duration; use core::time::Duration;
use align_ext::AlignExt; use align_ext::AlignExt;
use aster_block::bio::BioWaiter;
use aster_rights::Rights; use aster_rights::Rights;
use inherit_methods_macro::inherit_methods; use inherit_methods_macro::inherit_methods;
use spin::Once; use spin::Once;
@ -18,9 +17,8 @@ use crate::{
path::{Mount, Path}, path::{Mount, Path},
tmpfs::TmpFs, tmpfs::TmpFs,
utils::{ utils::{
AccessMode, CachePage, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode, AccessMode, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode, InodeType,
InodeType, Metadata, PageCacheBackend, StatusFlags, XattrName, XattrNamespace, Metadata, StatusFlags, XattrName, XattrNamespace, XattrSetFlags, mkmod,
XattrSetFlags, mkmod,
}, },
}, },
prelude::*, prelude::*,
@ -91,13 +89,6 @@ impl MemfdInode {
} }
} }
#[inherit_methods(from = "self.inode")]
impl PageCacheBackend for MemfdInode {
fn read_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
fn write_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
fn npages(&self) -> usize;
}
#[inherit_methods(from = "self.inode")] #[inherit_methods(from = "self.inode")]
impl InodeIo for MemfdInode { impl InodeIo for MemfdInode {
fn read_at( fn read_at(

View File

@ -19,7 +19,11 @@ pub use inode_ext::InodeExt;
pub use inode_mode::InodeMode; pub use inode_mode::InodeMode;
pub(crate) use inode_mode::{chmod, mkmod, perms_to_mask, who_and_perms_to_mask, who_to_mask}; pub(crate) use inode_mode::{chmod, mkmod, perms_to_mask, who_and_perms_to_mask, who_to_mask};
pub use open_args::OpenArgs; pub use open_args::OpenArgs;
pub use page_cache::{CachePage, PageCache, PageCacheBackend}; #[expect(unused_imports)]
pub use page_cache::{
CachePage, CachePageExt, CachePageMeta, LockedCachePage, PageCache, PageCacheBackend,
PageCacheOps, PageState,
};
#[cfg(ktest)] #[cfg(ktest)]
pub use random_test::{generate_random_operation, new_fs_in_memory}; pub use random_test::{generate_random_operation, new_fs_in_memory};
pub use range_lock::{FileRange, OFFSET_MAX, RangeLockItem, RangeLockList, RangeLockType}; pub use range_lock::{FileRange, OFFSET_MAX, RangeLockItem, RangeLockList, RangeLockType};

View File

@ -262,7 +262,7 @@ impl Vdso {
Self { Self {
data: SpinLock::new(vdso_data), data: SpinLock::new(vdso_data),
vmo: vdso_vmo, vmo: vdso_vmo,
data_frame, data_frame: data_frame.into(),
} }
} }

View File

@ -22,14 +22,14 @@ use super::{RssType, Vmar, interval_set::Interval, util::is_intersected, vmar_im
use crate::{ use crate::{
fs::{ fs::{
path::{Path, PathResolver}, path::{Path, PathResolver},
utils::Inode, utils::{CachePage, Inode},
}, },
prelude::*, prelude::*,
process::LockedHeap, process::LockedHeap,
thread::exception::PageFaultInfo, thread::exception::PageFaultInfo,
vm::{ vm::{
perms::VmPerms, perms::VmPerms,
vmo::{CommitFlags, Vmo, VmoCommitError}, vmo::{Vmo, VmoCommitError},
}, },
}; };
@ -434,13 +434,18 @@ impl VmMapping {
let (frame, is_readonly) = match self.prepare_page(page_aligned_addr, is_write) let (frame, is_readonly) = match self.prepare_page(page_aligned_addr, is_write)
{ {
Ok((frame, is_readonly)) => (frame, is_readonly), Ok((frame, is_readonly)) => (frame, is_readonly),
Err(VmoCommitError::Err(e)) => return Err(e), Err(
Err(VmoCommitError::NeedIo(index)) => { VmoCommitError::NeedIo(index) | VmoCommitError::WaitUntilInit(index, _),
) => {
drop(cursor); drop(cursor);
drop(preempt_guard); drop(preempt_guard);
self.vmo().unwrap().commit_on(index, CommitFlags::empty())?; self.vmo().unwrap().commit_on(index)?;
continue 'retry; continue 'retry;
} }
Err(VmoCommitError::Err(e)) => return Err(e),
Err(_) => {
unreachable!("unexpected VmoCommitError");
}
}; };
let vm_perms = { let vm_perms = {
@ -549,38 +554,42 @@ impl VmMapping {
let mut cursor = vm_space.cursor_mut(&preempt_guard, &(start_addr..end_addr))?; let mut cursor = vm_space.cursor_mut(&preempt_guard, &(start_addr..end_addr))?;
let rss_delta_ref = &mut rss_delta; let rss_delta_ref = &mut rss_delta;
let operate = let operate = move |commit_fn: &mut dyn FnMut() -> core::result::Result<
move |commit_fn: &mut dyn FnMut() (usize, CachePage),
-> core::result::Result<UFrame, VmoCommitError>| { VmoCommitError,
if let (_, None) = cursor.query().unwrap() { >| {
// We regard all the surrounding pages as accessed, no matter if let (_, None) = cursor.query().unwrap() {
// if it is really so. Then the hardware won't bother to update // We regard all the surrounding pages as accessed, no matter
// the accessed bit of the page table on following accesses. // if it is really so. Then the hardware won't bother to update
let page_flags = PageFlags::from(vm_perms) | PageFlags::ACCESSED; // the accessed bit of the page table on following accesses.
let page_prop = PageProperty::new_user(page_flags, CachePolicy::Writeback); let page_flags = PageFlags::from(vm_perms) | PageFlags::ACCESSED;
let frame = commit_fn()?; let page_prop = PageProperty::new_user(page_flags, CachePolicy::Writeback);
cursor.map(frame, page_prop); let (_, frame) = commit_fn()?;
rss_delta_ref.add(self.rss_type(), 1); cursor.map(frame.into(), page_prop);
} else { rss_delta_ref.add(self.rss_type(), 1);
let next_addr = cursor.virt_addr() + PAGE_SIZE; } else {
if next_addr < end_addr { let next_addr = cursor.virt_addr() + PAGE_SIZE;
let _ = cursor.jump(next_addr); if next_addr < end_addr {
} let _ = cursor.jump(next_addr);
} }
Ok(()) }
}; Ok(())
};
let start_offset = start_addr - self.map_to_addr; let start_offset = start_addr - self.map_to_addr;
let end_offset = end_addr - self.map_to_addr; let end_offset = end_addr - self.map_to_addr;
match vmo.try_operate_on_range(&(start_offset..end_offset), operate) { match vmo.try_operate_on_range(&(start_offset..end_offset), operate) {
Ok(_) => return Ok(()), Ok(_) => return Ok(()),
Err(VmoCommitError::NeedIo(index)) => { Err(VmoCommitError::NeedIo(index) | VmoCommitError::WaitUntilInit(index, _)) => {
drop(preempt_guard); drop(preempt_guard);
vmo.commit_on(index, CommitFlags::empty())?; vmo.commit_on(index)?;
start_addr = (index * PAGE_SIZE - vmo.offset()) + self.map_to_addr; start_addr = (index * PAGE_SIZE - vmo.offset()) + self.map_to_addr;
continue 'retry; continue 'retry;
} }
Err(VmoCommitError::Err(e)) => return Err(e), Err(VmoCommitError::Err(e)) => return Err(e),
Err(_) => {
unreachable!("unexpected VmoCommitError");
}
} }
} }
} }
@ -833,15 +842,17 @@ impl MappedVmo {
page_offset: usize, page_offset: usize,
) -> core::result::Result<UFrame, VmoCommitError> { ) -> core::result::Result<UFrame, VmoCommitError> {
debug_assert!(page_offset.is_multiple_of(PAGE_SIZE)); debug_assert!(page_offset.is_multiple_of(PAGE_SIZE));
self.vmo.try_commit_page(self.offset + page_offset) self.vmo
.try_commit_page(self.offset + page_offset)
.map(|frame| frame.into())
} }
/// Commits a page at a specific page index. /// Commits a page at a specific page index.
/// ///
/// This method may involve I/O operations if the VMO needs to fetch /// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache. /// a page from the underlying page cache.
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> { pub fn commit_on(&self, page_idx: usize) -> Result<UFrame> {
self.vmo.commit_on(page_idx, commit_flags) self.vmo.commit_on(page_idx).map(|frame| frame.into())
} }
/// Traverses the indices within a specified range of a VMO sequentially. /// Traverses the indices within a specified range of a VMO sequentially.
@ -857,7 +868,7 @@ impl MappedVmo {
) -> core::result::Result<(), VmoCommitError> ) -> core::result::Result<(), VmoCommitError>
where where
F: FnMut( F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>, &mut dyn FnMut() -> core::result::Result<(usize, CachePage), VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>, ) -> core::result::Result<(), VmoCommitError>,
{ {
let range = self.offset + range.start..self.offset + range.end; let range = self.offset + range.start..self.offset + range.end;

View File

@ -5,11 +5,14 @@
use core::sync::atomic::AtomicUsize; use core::sync::atomic::AtomicUsize;
use align_ext::AlignExt; use align_ext::AlignExt;
use ostd::mm::{FrameAllocOptions, UFrame, USegment}; use ostd::mm::{FrameAllocOptions, Segment};
use xarray::XArray; use xarray::XArray;
use super::{Pager, Vmo, VmoFlags, WritableMappingStatus}; use super::{Vmo, VmoFlags, WritableMappingStatus};
use crate::prelude::*; use crate::{
fs::utils::{CachePage, CachePageMeta, PageCacheBackend},
prelude::*,
};
/// Options for allocating a root VMO. /// Options for allocating a root VMO.
/// ///
@ -38,7 +41,7 @@ use crate::prelude::*;
pub struct VmoOptions { pub struct VmoOptions {
size: usize, size: usize,
flags: VmoFlags, flags: VmoFlags,
pager: Option<Arc<dyn Pager>>, backend: Option<Weak<dyn PageCacheBackend>>,
} }
impl VmoOptions { impl VmoOptions {
@ -50,7 +53,7 @@ impl VmoOptions {
Self { Self {
size, size,
flags: VmoFlags::empty(), flags: VmoFlags::empty(),
pager: None, backend: None,
} }
} }
@ -65,8 +68,8 @@ impl VmoOptions {
} }
/// Sets the pager of the VMO. /// Sets the pager of the VMO.
pub fn pager(mut self, pager: Arc<dyn Pager>) -> Self { pub fn backend(mut self, backend: Weak<dyn PageCacheBackend>) -> Self {
self.pager = Some(pager); self.backend = Some(backend);
self self
} }
} }
@ -75,19 +78,26 @@ impl VmoOptions {
/// Allocates the VMO according to the specified options. /// Allocates the VMO according to the specified options.
pub fn alloc(self) -> Result<Arc<Vmo>> { pub fn alloc(self) -> Result<Arc<Vmo>> {
let VmoOptions { let VmoOptions {
size, flags, pager, .. size,
flags,
backend,
..
} = self; } = self;
let vmo = alloc_vmo(size, flags, pager)?; let vmo = alloc_vmo(size, flags, backend)?;
Ok(Arc::new(vmo)) Ok(Arc::new(vmo))
} }
} }
fn alloc_vmo(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo> { fn alloc_vmo(
size: usize,
flags: VmoFlags,
backend: Option<Weak<dyn PageCacheBackend>>,
) -> Result<Vmo> {
let size = size.align_up(PAGE_SIZE); let size = size.align_up(PAGE_SIZE);
let pages = committed_pages_if_continuous(flags, size)?; let pages = committed_pages_if_continuous(flags, size)?;
let writable_mapping_status = WritableMappingStatus::default(); let writable_mapping_status = WritableMappingStatus::default();
Ok(Vmo { Ok(Vmo {
pager, backend,
flags, flags,
pages, pages,
size: AtomicUsize::new(size), size: AtomicUsize::new(size),
@ -95,11 +105,12 @@ fn alloc_vmo(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Res
}) })
} }
fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<UFrame>> { fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<CachePage>> {
if flags.contains(VmoFlags::CONTIGUOUS) { if flags.contains(VmoFlags::CONTIGUOUS) {
// if the vmo is continuous, we need to allocate frames for the vmo // if the vmo is continuous, we need to allocate frames for the vmo
let frames_num = size / PAGE_SIZE; let frames_num = size / PAGE_SIZE;
let segment: USegment = FrameAllocOptions::new().alloc_segment(frames_num)?.into(); let segment: Segment<CachePageMeta> = FrameAllocOptions::new()
.alloc_segment_with(frames_num, |_| CachePageMeta::default())?;
let committed_pages = XArray::new(); let committed_pages = XArray::new();
let mut locked_pages = committed_pages.lock(); let mut locked_pages = committed_pages.lock();
let mut cursor = locked_pages.cursor_mut(0); let mut cursor = locked_pages.cursor_mut(0);
@ -154,16 +165,18 @@ mod test {
#[ktest] #[ktest]
fn resize() { fn resize() {
use crate::fs::utils::PageCacheOps;
let vmo = VmoOptions::new(PAGE_SIZE) let vmo = VmoOptions::new(PAGE_SIZE)
.flags(VmoFlags::RESIZABLE) .flags(VmoFlags::RESIZABLE)
.alloc() .alloc()
.unwrap(); .unwrap();
vmo.write_val(10, &42u8).unwrap(); vmo.write_val(10, &42u8).unwrap();
vmo.resize(2 * PAGE_SIZE).unwrap(); vmo.resize(2 * PAGE_SIZE, vmo.size()).unwrap();
assert_eq!(vmo.size(), 2 * PAGE_SIZE); assert_eq!(vmo.size(), 2 * PAGE_SIZE);
assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42); assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42);
vmo.write_val(PAGE_SIZE + 20, &123u8).unwrap(); vmo.write_val(PAGE_SIZE + 20, &123u8).unwrap();
vmo.resize(PAGE_SIZE).unwrap(); vmo.resize(PAGE_SIZE, vmo.size()).unwrap();
assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42); assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42);
} }
} }

View File

@ -1,58 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use ostd::mm::UFrame;
use crate::prelude::*;
/// Pagers provide frame to a VMO.
///
/// A `Pager` object can be attached to a VMO. Whenever the
/// VMO needs more frames (i.e., on commits), it will turn to the pager,
/// which should then provide frames whose data have been initialized properly.
/// Any time a frame is updated through the VMO, the VMO will
/// notify the attached pager that the frame has been updated.
/// Finally, when a frame is no longer needed (i.e., on decommits),
/// the frame pager will also be notified.
pub trait Pager: Send + Sync {
/// Ask the pager to provide a frame at a specified index.
///
/// After a page of a VMO is committed, the VMO shall not call this method
/// again until the page is decommitted. But a robust implementation of
/// `Pager` should not rely on this behavior for its correctness;
/// instead, it should returns the _same_ frame.
///
/// If a VMO page has been previously committed and decommited,
/// and is to be committed again, then the pager is free to return
/// whatever frame that may or may not be the same as the last time.
///
/// It is up to the pager to decide the range of valid indices.
fn commit_page(&self, idx: usize) -> Result<UFrame>;
/// Notify the pager that the frame at a specified index has been updated.
///
/// Being aware of the updates allow the pager (e.g., an inode) to
/// know which pages are dirty and only write back the _dirty_ pages back
/// to disk.
///
/// The VMO will not call this method for an uncommitted page.
/// But a robust implementation of `Pager` should not make
/// such an assumption for its correctness; instead, it should simply ignore the
/// call or return an error.
fn update_page(&self, idx: usize) -> Result<()>;
/// Notify the pager that the frame at the specified index has been decommitted.
///
/// Knowing that a frame is no longer needed, the pager (e.g., an inode)
/// can free the frame after writing back its data to the disk.
///
/// The VMO will not call this method for an uncommitted page.
/// But a robust implementation of `Pager` should not make
/// such an assumption for its correctness; instead, it should simply ignore the
/// call or return an error.
fn decommit_page(&self, idx: usize) -> Result<()>;
/// Ask the pager to provide a frame at a specified index.
/// Notify the pager that the frame will be fully overwritten soon, so pager can
/// choose not to initialize it.
fn commit_overwrite(&self, idx: usize) -> Result<UFrame>;
}