Skip to content

Commit 541e1fb

Browse files
committed
rust: fs: introduce FileSystem::read_folio
We also allow the creation of regular file inodes. File systems can now use `read_folio` to expose the file contents via the page cache. Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent 1914d26 commit 541e1fb

3 files changed

Lines changed: 122 additions & 23 deletions

File tree

rust/kernel/folio.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ impl LockedFolio<'_> {
6161
/// Callers must ensure that the folio is valid and locked. Additionally, that the
6262
/// responsibility of unlocking is transferred to the new instance of [`LockedFolio`]. Lastly,
6363
/// that the returned [`LockedFolio`] doesn't outlive the refcount that keeps it alive.
64-
#[allow(dead_code)]
6564
pub(crate) unsafe fn from_raw(folio: *const bindings::folio) -> Self {
6665
let ptr = folio.cast();
6766
// SAFETY: The safety requirements ensure that `folio` (from which `ptr` is derived) is

rust/kernel/fs.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
99
use crate::error::{code::*, from_result, to_result, Error, Result};
1010
use crate::types::{ARef, AlwaysRefCounted, Either, Opaque};
11-
use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule};
11+
use crate::{
12+
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init,
13+
ThisModule,
14+
};
1215
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
1316
use macros::{pin_data, pinned_drop};
1417

@@ -41,6 +44,9 @@ pub trait FileSystem {
4144

4245
/// Returns the inode corresponding to the directory entry with the given name.
4346
fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>>;
47+
48+
/// Reads the contents of the inode into the given folio.
49+
fn read_folio(inode: &INode<Self>, folio: LockedFolio<'_>) -> Result;
4450
}
4551

4652
/// The types of directory entries reported by [`FileSystem::read_dir`].
@@ -79,6 +85,7 @@ impl From<INodeType> for DirEntryType {
7985
fn from(value: INodeType) -> Self {
8086
match value {
8187
INodeType::Dir => DirEntryType::Dir,
88+
INodeType::Reg => DirEntryType::Reg,
8289
}
8390
}
8491
}
@@ -231,6 +238,15 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
231238
inode.i_op = &Tables::<T>::DIR_INODE_OPERATIONS;
232239
bindings::S_IFDIR
233240
}
241+
INodeType::Reg => {
242+
// SAFETY: `generic_ro_fops` never changes, it's safe to reference it.
243+
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::generic_ro_fops };
244+
inode.i_data.a_ops = &Tables::<T>::FILE_ADDRESS_SPACE_OPERATIONS;
245+
246+
// SAFETY: The `i_mapping` pointer doesn't change and is valid.
247+
unsafe { bindings::mapping_set_large_folios(inode.i_mapping) };
248+
bindings::S_IFREG
249+
}
234250
};
235251

236252
inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?;
@@ -267,6 +283,9 @@ impl<T: FileSystem + ?Sized> Drop for NewINode<T> {
267283
pub enum INodeType {
268284
/// Directory type.
269285
Dir,
286+
287+
/// Regular file type.
288+
Reg,
270289
}
271290

272291
/// Required inode parameters.
@@ -612,6 +631,55 @@ impl<T: FileSystem + ?Sized> Tables<T> {
612631
},
613632
}
614633
}
634+
635+
const FILE_ADDRESS_SPACE_OPERATIONS: bindings::address_space_operations =
636+
bindings::address_space_operations {
637+
writepage: None,
638+
read_folio: Some(Self::read_folio_callback),
639+
writepages: None,
640+
dirty_folio: None,
641+
readahead: None,
642+
write_begin: None,
643+
write_end: None,
644+
bmap: None,
645+
invalidate_folio: None,
646+
release_folio: None,
647+
free_folio: None,
648+
direct_IO: None,
649+
migrate_folio: None,
650+
launder_folio: None,
651+
is_partially_uptodate: None,
652+
is_dirty_writeback: None,
653+
error_remove_page: None,
654+
swap_activate: None,
655+
swap_deactivate: None,
656+
swap_rw: None,
657+
};
658+
659+
extern "C" fn read_folio_callback(
660+
_file: *mut bindings::file,
661+
folio: *mut bindings::folio,
662+
) -> i32 {
663+
from_result(|| {
664+
// SAFETY: All pointers are valid and stable.
665+
let inode = unsafe {
666+
&*(*(*folio)
667+
.__bindgen_anon_1
668+
.page
669+
.__bindgen_anon_1
670+
.__bindgen_anon_1
671+
.mapping)
672+
.host
673+
.cast::<INode<T>>()
674+
};
675+
676+
// SAFETY: The C contract guarantees that the folio is valid and locked, with ownership
677+
// of the lock transferred to the callee (this function). The folio is also guaranteed
678+
// not to outlive this function.
679+
T::read_folio(inode, unsafe { LockedFolio::from_raw(folio) })?;
680+
Ok(0)
681+
})
682+
}
615683
}
616684

617685
/// Kernel module that exposes a single file system implemented by `T`.
@@ -642,7 +710,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
642710
/// # mod module_ro_fs_sample {
643711
/// use kernel::fs::{DirEntryType, INode, NewSuperBlock, SuperBlock};
644712
/// use kernel::prelude::*;
645-
/// use kernel::{c_str, fs, types::ARef};
713+
/// use kernel::{c_str, folio::LockedFolio, fs, types::ARef};
646714
///
647715
/// kernel::module_fs! {
648716
/// type: MyFs,
@@ -668,6 +736,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
668736
/// fn lookup(_: &INode<Self>, _: &[u8]) -> Result<ARef<INode<Self>>> {
669737
/// todo!()
670738
/// }
739+
/// fn read_folio(_: &INode<Self>, _: LockedFolio<'_>) -> Result {
740+
/// todo!()
741+
/// }
671742
/// }
672743
/// # }
673744
/// ```

samples/rust/rust_rofs.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use kernel::fs::{
66
DirEntryType, INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams,
77
};
88
use kernel::prelude::*;
9-
use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either};
9+
use kernel::{c_str, folio::LockedFolio, fs, time::UNIX_EPOCH, types::ARef, types::Either};
1010

1111
kernel::module_fs! {
1212
type: RoFs,
@@ -20,23 +20,27 @@ struct Entry {
2020
name: &'static [u8],
2121
ino: u64,
2222
etype: INodeType,
23+
contents: &'static [u8],
2324
}
2425

2526
const ENTRIES: [Entry; 3] = [
2627
Entry {
2728
name: b".",
2829
ino: 1,
2930
etype: INodeType::Dir,
31+
contents: b"",
3032
},
3133
Entry {
3234
name: b"..",
3335
ino: 1,
3436
etype: INodeType::Dir,
37+
contents: b"",
3538
},
3639
Entry {
37-
name: b"subdir",
40+
name: b"test.txt",
3841
ino: 2,
39-
etype: INodeType::Dir,
42+
etype: INodeType::Reg,
43+
contents: b"hello\n",
4044
},
4145
];
4246

@@ -99,23 +103,48 @@ impl fs::FileSystem for RoFs {
99103
return Err(ENOENT);
100104
}
101105

102-
match name {
103-
b"subdir" => match parent.super_block().get_or_create_inode(2)? {
104-
Either::Left(existing) => Ok(existing),
105-
Either::Right(new) => new.init(INodeParams {
106-
typ: INodeType::Dir,
107-
mode: 0o555,
108-
size: 0,
109-
blocks: 1,
110-
nlink: 2,
111-
uid: 0,
112-
gid: 0,
113-
atime: UNIX_EPOCH,
114-
ctime: UNIX_EPOCH,
115-
mtime: UNIX_EPOCH,
116-
}),
117-
},
118-
_ => Err(ENOENT),
106+
for e in &ENTRIES {
107+
if name == e.name {
108+
return match parent.super_block().get_or_create_inode(e.ino)? {
109+
Either::Left(existing) => Ok(existing),
110+
Either::Right(new) => new.init(INodeParams {
111+
typ: e.etype,
112+
mode: 0o444,
113+
size: e.contents.len().try_into()?,
114+
blocks: 1,
115+
nlink: 1,
116+
uid: 0,
117+
gid: 0,
118+
atime: UNIX_EPOCH,
119+
ctime: UNIX_EPOCH,
120+
mtime: UNIX_EPOCH,
121+
}),
122+
};
123+
}
119124
}
125+
126+
Err(ENOENT)
127+
}
128+
129+
fn read_folio(inode: &INode<Self>, mut folio: LockedFolio<'_>) -> Result {
130+
let data = match inode.ino() {
131+
2 => ENTRIES[2].contents,
132+
_ => return Err(EINVAL),
133+
};
134+
135+
let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX);
136+
let copied = if pos >= data.len() {
137+
0
138+
} else {
139+
let to_copy = core::cmp::min(data.len() - pos, folio.size());
140+
folio.write(0, &data[pos..][..to_copy])?;
141+
to_copy
142+
};
143+
144+
folio.zero_out(copied, folio.size() - copied)?;
145+
folio.mark_uptodate();
146+
folio.flush_dcache();
147+
148+
Ok(())
120149
}
121150
}

0 commit comments

Comments
 (0)