Skip to content

Commit beb3cc5

Browse files
feat(vm): handle shared files properly
Signed-off-by: Anhad Singh <[email protected]>
1 parent 9095737 commit beb3cc5

File tree

9 files changed

+387
-66
lines changed

9 files changed

+387
-66
lines changed

src/aero_kernel/src/fs/block/mod.rs

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,46 @@ use crate::fs::{FileSystem, Result};
3232

3333
use crate::fs::ext2::Ext2;
3434
use crate::mem::paging::*;
35+
use crate::mem::AddressSpace;
3536
use crate::utils::sync::Mutex;
3637

3738
use super::cache::{Cache, CacheArc, CacheItem, Cacheable};
3839
use super::devfs::{alloc_device_marker, Device};
3940
use super::inode::INodeInterface;
4041

41-
type PageCacheKey = (usize, usize); // (block device pointer, offset)
42-
type PageCacheItem = CacheArc<CacheItem<PageCacheKey, CachedPage>>;
42+
type PageCacheKey = (usize, usize); // (owner ptr, index)
43+
pub type PageCacheItem = CacheArc<CacheItem<PageCacheKey, CachedPage>>;
4344

44-
struct CachedPage {
45-
device: Weak<dyn CachedAccess>,
45+
struct DirtyMapping {
46+
addr_space: AddressSpace,
47+
addr: VirtAddr,
48+
}
49+
50+
pub struct CachedPage {
51+
owner: Weak<dyn CachedAccess>,
4652
offset: usize,
4753
page: PhysFrame,
4854
dirty: AtomicBool,
55+
dirty_mappings: Mutex<Vec<DirtyMapping>>,
4956
}
5057

5158
impl CachedPage {
52-
fn new(device: Weak<dyn CachedAccess>, offset: usize) -> Self {
53-
Self {
54-
device,
59+
fn new(owner: Weak<dyn CachedAccess>, offset: usize) -> Self {
60+
let k = Self {
61+
owner,
5562
offset,
5663
page: FRAME_ALLOCATOR
5764
.allocate_frame()
5865
.expect("page_cache: out of memory"),
5966
dirty: AtomicBool::new(false),
60-
}
67+
dirty_mappings: Mutex::new(Vec::new()),
68+
};
69+
// TODO: temporary hack. i mean this is fine but is there a cleaner way to do this. this is
70+
// required since when the VM for the process umaps a page that contains a cached page, it
71+
// will unmap this page which will decrease the refcnt to 0 and deallocate it.
72+
get_vm_frames().unwrap()[k.page.start_address().as_u64() as usize / 4096usize]
73+
.inc_ref_count();
74+
k
6175
}
6276

6377
fn data_mut(&self) -> &mut [MaybeUninit<u8>] {
@@ -72,10 +86,14 @@ impl CachedPage {
7286
unsafe { core::slice::from_raw_parts_mut(data_ptr, Size4KiB::SIZE as usize) }
7387
}
7488

75-
fn data_addr(&self) -> PhysAddr {
89+
pub fn data_addr(&self) -> PhysAddr {
7690
self.page.start_address()
7791
}
7892

93+
pub fn page(&self) -> PhysFrame {
94+
self.page
95+
}
96+
7997
fn make_key(device: &Weak<dyn CachedAccess>, offset: usize) -> PageCacheKey {
8098
(device.as_ptr().addr(), offset)
8199
}
@@ -85,26 +103,35 @@ impl CachedPage {
85103
self.dirty.load(Ordering::SeqCst)
86104
}
87105

88-
fn mark_dirty(&self) {
106+
pub fn mark_dirty(&self) {
107+
log::error!("marking dirty --------------------------------------");
89108
self.dirty.store(true, Ordering::SeqCst);
90109
}
91110

92111
fn device(&self) -> Arc<dyn CachedAccess> {
93-
self.device.upgrade().unwrap()
112+
self.owner.upgrade().unwrap()
94113
}
95114

96115
fn sync(&self) {
97116
if !self.is_dirty() {
98117
return;
99118
}
100119

101-
// Commit the changes made to the cache to the disk.
102-
let disk = self.device();
103-
120+
// Commit the changes made to the cache to the owner.
121+
let owner = self.device();
104122
let offset_bytes = self.offset * Size4KiB::SIZE as usize;
105-
let sector = offset_bytes / disk.block_size();
123+
owner.write_direct(offset_bytes, self.page);
124+
125+
for mut mapping in self.dirty_mappings.lock_irq().drain(..) {
126+
let mut offset_table = mapping.addr_space.offset_page_table();
127+
offset_table
128+
.unmap(Page::<Size4KiB>::containing_address(mapping.addr))
129+
.unwrap()
130+
.1
131+
.flush();
132+
}
106133

107-
disk.write_dma(sector, self.data_addr(), Size4KiB::SIZE as usize);
134+
self.dirty.store(false, Ordering::SeqCst);
108135
}
109136
}
110137

@@ -116,12 +143,12 @@ impl Drop for CachedPage {
116143

117144
impl Cacheable<PageCacheKey> for CachedPage {
118145
fn cache_key(&self) -> PageCacheKey {
119-
Self::make_key(&self.device, self.offset)
146+
Self::make_key(&self.owner, self.offset)
120147
}
121148
}
122149

123150
lazy_static::lazy_static! {
124-
static ref PAGE_CACHE: Arc<Cache<PageCacheKey, CachedPage>> = Cache::new();
151+
pub(in crate::fs) static ref PAGE_CACHE: Arc<Cache<PageCacheKey, CachedPage>> = Cache::new();
125152
}
126153

127154
impl Cache<PageCacheKey, CachedPage> {
@@ -145,16 +172,16 @@ impl Cache<PageCacheKey, CachedPage> {
145172
let device = device.upgrade().expect("page_cache: device dropped");
146173

147174
let aligned_offset = align_down(offset as u64, Size4KiB::SIZE) as usize;
148-
let sector = aligned_offset / device.block_size();
149-
150175
device
151-
.read_dma(sector, page.data_addr(), Size4KiB::SIZE as usize)
176+
.read_direct(aligned_offset, page.page())
152177
.expect("page_cache: failed to read block");
153178

154179
PAGE_CACHE.make_item_cached(page)
155180
}
156181
}
157182

183+
// TODO: cache hit miss stats
184+
158185
pub struct DirtyRef<T: Sized> {
159186
cache: PageCacheItem,
160187
ptr: *mut T,
@@ -202,9 +229,12 @@ pub trait BlockDeviceInterface: Send + Sync {
202229
fn write_block(&self, sector: usize, buf: &[u8]) -> Option<usize>;
203230
}
204231

205-
pub trait CachedAccess: BlockDeviceInterface {
232+
pub trait CachedAccess: Send + Sync {
206233
fn sref(&self) -> Weak<dyn CachedAccess>;
207234

235+
fn read_direct(&self, offset: usize, dest: PhysFrame) -> Option<usize>;
236+
fn write_direct(&self, offset: usize, src: PhysFrame) -> Option<usize>;
237+
208238
fn read(&self, mut offset: usize, dest: &mut [MaybeUninit<u8>]) -> Option<usize> {
209239
let mut loc = 0;
210240

@@ -236,6 +266,9 @@ pub trait CachedAccess: BlockDeviceInterface {
236266
let mut loc = 0;
237267

238268
while loc < buffer.len() {
269+
// TODO: If it is not found in the page cache, then, when the write perfectly falls on
270+
// page size boundaries, the page is not even read from disk, but allocated and
271+
// immediately marked dirty.
239272
let page = PAGE_CACHE.get_page(&self.sref(), offset);
240273

241274
let page_offset = offset % Size4KiB::SIZE as usize;
@@ -318,6 +351,22 @@ impl CachedAccess for BlockDevice {
318351
fn sref(&self) -> Weak<dyn CachedAccess> {
319352
self.sref.clone()
320353
}
354+
355+
fn read_direct(&self, offset: usize, dest: PhysFrame) -> Option<usize> {
356+
self.dev.read_dma(
357+
offset / self.dev.block_size(),
358+
dest.start_address(),
359+
Size4KiB::SIZE as _,
360+
)
361+
}
362+
363+
fn write_direct(&self, offset: usize, src: PhysFrame) -> Option<usize> {
364+
self.dev.write_dma(
365+
offset / self.dev.block_size(),
366+
src.start_address(),
367+
Size4KiB::SIZE as _,
368+
)
369+
}
321370
}
322371

323372
impl INodeInterface for BlockDevice {}

src/aero_kernel/src/fs/devfs.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::mem::paging::*;
3131
use crate::rendy::RendyInfo;
3232

3333
use super::cache::{DirCacheItem, INodeCacheItem};
34-
use super::inode::{INodeInterface, PollFlags, PollTable};
34+
use super::inode::{INodeInterface, MMapPage, PollFlags, PollTable};
3535
use super::ramfs::RamFs;
3636
use super::{FileSystem, FileSystemError, Result, MOUNT_MANAGER};
3737

@@ -132,6 +132,10 @@ impl INodeInterface for DevINode {
132132
fn open(&self, handle: Arc<super::file_table::FileHandle>) -> Result<Option<DirCacheItem>> {
133133
self.0.inode().open(handle)
134134
}
135+
136+
fn mmap_v2(&self, offset: usize) -> Result<MMapPage> {
137+
self.0.inode().mmap_v2(offset)
138+
}
135139
}
136140

137141
/// Implementation of dev filesystem. (See the module-level documentation for more
@@ -357,6 +361,26 @@ impl INodeInterface for DevFb {
357361
.expect("/dev/fb: terminal not initialized")
358362
}
359363

364+
fn mmap_v2(&self, offset: usize) -> Result<MMapPage> {
365+
let rinfo = crate::rendy::get_rendy_info();
366+
367+
// Make sure we are in bounds.
368+
if offset > rinfo.byte_len || offset + Size4KiB::SIZE as usize > rinfo.byte_len {
369+
return Err(FileSystemError::NotSupported);
370+
}
371+
372+
let mut lock = crate::rendy::DEBUG_RENDY.get().unwrap().lock_irq();
373+
let fb = lock.get_framebuffer();
374+
375+
let fb_ptr = fb.as_ptr().cast::<u8>();
376+
let fb_ptr = unsafe { fb_ptr.add(offset) };
377+
let fb_phys_ptr = unsafe { fb_ptr.sub(crate::PHYSICAL_MEMORY_OFFSET.as_u64() as usize) };
378+
379+
Ok(MMapPage::Direct(PhysFrame::containing_address(unsafe {
380+
PhysAddr::new_unchecked(fb_phys_ptr as u64)
381+
})))
382+
}
383+
360384
fn ioctl(&self, command: usize, arg: usize) -> Result<usize> {
361385
match command {
362386
FBIOGET_VSCREENINFO => {

src/aero_kernel/src/fs/ext2/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ use crate::socket::SocketAddrRef;
3737

3838
use self::group_desc::GroupDescriptors;
3939

40-
use super::block::{self, BlockDevice, CachedAccess};
40+
use super::block::{self, BlockDevice, CachedAccess, PAGE_CACHE};
4141

4242
use super::cache::{DirCacheItem, INodeCacheItem};
4343
use super::path::PathBuf;
4444
use super::{cache, FileSystemError, Path};
4545

46-
use super::inode::{self, INodeInterface, Metadata, PollFlags, PollTable};
46+
use super::inode::{self, INodeInterface, MMapPage, Metadata, PollFlags, PollTable};
4747
use super::FileSystem;
4848

4949
pub struct INode {
@@ -358,6 +358,20 @@ impl INode {
358358
}
359359
}
360360

361+
impl CachedAccess for INode {
362+
fn sref(&self) -> Weak<dyn CachedAccess> {
363+
self.sref.clone()
364+
}
365+
366+
fn read_direct(&self, offset: usize, dest: PhysFrame) -> Option<usize> {
367+
INodeInterface::read_at(self, offset, dest.as_slice_mut()).ok()
368+
}
369+
370+
fn write_direct(&self, offset: usize, src: PhysFrame) -> Option<usize> {
371+
INodeInterface::write_at(self, offset, src.as_slice_mut()).ok()
372+
}
373+
}
374+
361375
impl INodeInterface for INode {
362376
fn weak_filesystem(&self) -> Option<Weak<dyn FileSystem>> {
363377
Some(self.fs.clone())
@@ -587,6 +601,14 @@ impl INodeInterface for INode {
587601
Ok(private_cp)
588602
}
589603

604+
// TODO: cleanup
605+
fn mmap_v2(&self, offset: usize) -> super::Result<MMapPage> {
606+
Ok(MMapPage::PageCache(PAGE_CACHE.get_page(
607+
&(self.sref.clone() as Weak<dyn CachedAccess>),
608+
offset,
609+
)))
610+
}
611+
590612
fn listen(&self, backlog: usize) -> Result<(), SyscallError> {
591613
if let Some(proxy) = self.proxy.as_ref() {
592614
return proxy.listen(backlog);

src/aero_kernel/src/fs/inode.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::socket::{SocketAddr, SocketAddrRef};
3333
use crate::userland::scheduler;
3434
use crate::utils::sync::{BMutex, Mutex, WaitQueue};
3535

36+
use super::block::PageCacheItem;
3637
use super::cache::{Cacheable, CachedINode, DirCacheItem, INodeCacheItem};
3738
use super::devfs::DevINode;
3839
use super::file_table::FileHandle;
@@ -109,6 +110,11 @@ impl From<PollFlags> for PollEventFlags {
109110
}
110111
}
111112

113+
pub enum MMapPage {
114+
Direct(PhysFrame),
115+
PageCache(PageCacheItem),
116+
}
117+
112118
/// An inode describes a file. An inode structure holds metadata of the
113119
/// inode which includes its type, size, the number of links referring to it,
114120
/// and the list of blocks holding the file's content. For example device files,
@@ -234,6 +240,14 @@ pub trait INodeInterface: Send + Sync {
234240
Err(FileSystemError::NotSupported)
235241
}
236242

243+
fn mmap_v2(&self, _offset: usize) -> Result<MMapPage> {
244+
log::error!(
245+
"{} does not support mmap_v2!",
246+
core::any::type_name_of_val(self),
247+
);
248+
Err(FileSystemError::NotSupported)
249+
}
250+
237251
// Socket operations:
238252
fn bind(&self, _address: SocketAddrRef, _length: usize) -> Result<()> {
239253
Err(FileSystemError::NotSocket)

src/aero_kernel/src/fs/ramfs.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use super::cache::{
3333
};
3434
use super::devfs::DevINode;
3535
use super::inode::{
36-
DirEntry, FileContents, FileType, INodeInterface, Metadata, PollFlags, PollTable,
36+
DirEntry, FileContents, FileType, INodeInterface, MMapPage, Metadata, PollFlags, PollTable,
3737
};
3838
use super::{FileSystem, FileSystemError, Result};
3939

@@ -384,6 +384,21 @@ impl INodeInterface for LockedRamINode {
384384
}
385385
}
386386

387+
fn mmap_v2(&self, offset: usize) -> Result<MMapPage> {
388+
let this = self.0.read();
389+
390+
match &this.contents {
391+
FileContents::Device(dev) => {
392+
let device = dev.clone();
393+
drop(this);
394+
395+
device.mmap_v2(offset)
396+
}
397+
398+
_ => todo!(),
399+
}
400+
}
401+
387402
fn lookup(&self, dir: DirCacheItem, name: &str) -> Result<DirCacheItem> {
388403
let this = self.0.read();
389404
let child = this

src/aero_kernel/src/mem/paging/addr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use bit_field::BitField;
3535
/// [`TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) trait can be used for performing conversions
3636
/// between `u64` and `usize`.
3737
38-
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
38+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
3939
#[repr(transparent)]
4040
pub struct VirtAddr(u64);
4141

0 commit comments

Comments
 (0)