Skip to content

Commit 1deb44a

Browse files
committed
rust: fs: allow file systems backed by a block device
Allow Rust file systems that are backed by block devices (in addition to in-memory ones). Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 0e8bedc commit 1deb44a

File tree

4 files changed

+180
-13
lines changed

4 files changed

+180
-13
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#include <kunit/test.h>
10+
#include <linux/bio.h>
1011
#include <linux/buffer_head.h>
1112
#include <linux/errname.h>
1213
#include <linux/fs.h>

rust/helpers.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
#include <kunit/test-bug.h>
24+
#include <linux/blkdev.h>
2425
#include <linux/buffer_head.h>
2526
#include <linux/bug.h>
2627
#include <linux/build_bug.h>
@@ -252,6 +253,13 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor)
252253
EXPORT_SYMBOL_GPL(rust_helper_MKDEV);
253254

254255
#ifdef CONFIG_BUFFER_HEAD
256+
struct buffer_head *rust_helper_sb_bread(struct super_block *sb,
257+
sector_t block)
258+
{
259+
return sb_bread(sb, block);
260+
}
261+
EXPORT_SYMBOL_GPL(rust_helper_sb_bread);
262+
255263
void rust_helper_get_bh(struct buffer_head *bh)
256264
{
257265
get_bh(bh);
@@ -265,6 +273,12 @@ void rust_helper_put_bh(struct buffer_head *bh)
265273
EXPORT_SYMBOL_GPL(rust_helper_put_bh);
266274
#endif
267275

276+
sector_t rust_helper_bdev_nr_sectors(struct block_device *bdev)
277+
{
278+
return bdev_nr_sectors(bdev);
279+
}
280+
EXPORT_SYMBOL_GPL(rust_helper_bdev_nr_sectors);
281+
268282
/*
269283
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
270284
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/fs.rs

Lines changed: 165 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h)
88
99
use crate::error::{code::*, from_result, to_result, Error, Result};
10-
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque};
11-
use crate::{
12-
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init,
13-
ThisModule,
14-
};
10+
use crate::folio::{LockedFolio, UniqueFolio};
11+
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque, ScopeGuard};
12+
use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule};
1513
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
1614
use macros::{pin_data, pinned_drop};
1715

@@ -21,6 +19,17 @@ pub mod buffer;
2119
/// Maximum size of an inode.
2220
pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
2321

22+
/// Type of superblock keying.
23+
///
24+
/// It determines how C's `fs_context_operations::get_tree` is implemented.
25+
pub enum Super {
26+
/// Multiple independent superblocks may exist.
27+
Independent,
28+
29+
/// Uses a block device.
30+
BlockDev,
31+
}
32+
2433
/// A file system type.
2534
pub trait FileSystem {
2635
/// Data associated with each file system instance (super-block).
@@ -29,6 +38,9 @@ pub trait FileSystem {
2938
/// The name of the file system type.
3039
const NAME: &'static CStr;
3140

41+
/// Determines how superblocks for this file system type are keyed.
42+
const SUPER_TYPE: Super = Super::Independent;
43+
3244
/// Returns the parameters to initialise a super block.
3345
fn super_params(sb: &NewSuperBlock<Self>) -> Result<SuperParams<Self::Data>>;
3446

@@ -181,7 +193,9 @@ impl Registration {
181193
fs.name = T::NAME.as_char_ptr();
182194
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
183195
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
184-
fs.fs_flags = 0;
196+
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
197+
bindings::FS_REQUIRES_DEV as i32
198+
} else { 0 };
185199

186200
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
187201
// registration is active (it is undone in `drop`).
@@ -204,9 +218,16 @@ impl Registration {
204218
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
205219
sb_ptr: *mut bindings::super_block,
206220
) {
207-
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
208-
// the appropriate function to call for cleanup.
209-
unsafe { bindings::kill_anon_super(sb_ptr) };
221+
match T::SUPER_TYPE {
222+
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
223+
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
224+
// for cleanup.
225+
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
226+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
227+
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
228+
// for cleanup.
229+
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
230+
}
210231

211232
// SAFETY: The C API contract guarantees that `sb_ptr` is valid for read.
212233
let ptr = unsafe { (*sb_ptr).s_fs_info };
@@ -479,6 +500,65 @@ impl<T: FileSystem + ?Sized> SuperBlock<T> {
479500
})))
480501
}
481502
}
503+
504+
/// Reads a block from the block device.
505+
#[cfg(CONFIG_BUFFER_HEAD)]
506+
pub fn bread(&self, block: u64) -> Result<ARef<buffer::Head>> {
507+
// Fail requests for non-blockdev file systems. This is a compile-time check.
508+
match T::SUPER_TYPE {
509+
Super::BlockDev => {}
510+
_ => return Err(EIO),
511+
}
512+
513+
// SAFETY: This function is only valid after the `NeedsInit` typestate, so the block size
514+
// is known and the superblock can be used to read blocks.
515+
let ptr =
516+
ptr::NonNull::new(unsafe { bindings::sb_bread(self.0.get(), block) }).ok_or(EIO)?;
517+
// SAFETY: `sb_bread` returns a referenced buffer head. Ownership of the increment is
518+
// passed to the `ARef` instance.
519+
Ok(unsafe { ARef::from_raw(ptr.cast()) })
520+
}
521+
522+
/// Reads `size` bytes starting from `offset` bytes.
523+
///
524+
/// Returns an iterator that returns slices based on blocks.
525+
#[cfg(CONFIG_BUFFER_HEAD)]
526+
pub fn read(
527+
&self,
528+
offset: u64,
529+
size: u64,
530+
) -> Result<impl Iterator<Item = Result<buffer::View>> + '_> {
531+
struct BlockIter<'a, T: FileSystem + ?Sized> {
532+
sb: &'a SuperBlock<T>,
533+
next_offset: u64,
534+
end: u64,
535+
}
536+
impl<'a, T: FileSystem + ?Sized> Iterator for BlockIter<'a, T> {
537+
type Item = Result<buffer::View>;
538+
539+
fn next(&mut self) -> Option<Self::Item> {
540+
if self.next_offset >= self.end {
541+
return None;
542+
}
543+
544+
// SAFETY: The superblock is valid and has had its block size initialised.
545+
let block_size = unsafe { (*self.sb.0.get()).s_blocksize };
546+
let bh = match self.sb.bread(self.next_offset / block_size) {
547+
Ok(bh) => bh,
548+
Err(e) => return Some(Err(e)),
549+
};
550+
let boffset = self.next_offset & (block_size - 1);
551+
let bsize = core::cmp::min(self.end - self.next_offset, block_size - boffset);
552+
self.next_offset += bsize;
553+
Some(Ok(buffer::View::new(bh, boffset as usize, bsize as usize)))
554+
}
555+
}
556+
Ok(BlockIter {
557+
sb: self,
558+
next_offset: offset,
559+
end: offset.checked_add(size).ok_or(ERANGE)?,
560+
})
561+
}
482562
}
483563

484564
/// Required superblock parameters.
@@ -511,6 +591,70 @@ pub struct SuperParams<T: ForeignOwnable + Send + Sync> {
511591
#[repr(transparent)]
512592
pub struct NewSuperBlock<T: FileSystem + ?Sized>(bindings::super_block, PhantomData<T>);
513593

594+
impl<T: FileSystem + ?Sized> NewSuperBlock<T> {
595+
/// Reads sectors.
596+
///
597+
/// `count` must be such that the total size doesn't exceed a page.
598+
pub fn sread(&self, sector: u64, count: usize, folio: &mut UniqueFolio) -> Result {
599+
// Fail requests for non-blockdev file systems. This is a compile-time check.
600+
match T::SUPER_TYPE {
601+
// The superblock is valid and given that it's a blockdev superblock it must have a
602+
// valid `s_bdev`.
603+
Super::BlockDev => {}
604+
_ => return Err(EIO),
605+
}
606+
607+
crate::build_assert!(count * (bindings::SECTOR_SIZE as usize) <= bindings::PAGE_SIZE);
608+
609+
// Read the sectors.
610+
let mut bio = bindings::bio::default();
611+
let bvec = Opaque::<bindings::bio_vec>::uninit();
612+
613+
// SAFETY: `bio` and `bvec` are allocated on the stack, they're both valid.
614+
unsafe {
615+
bindings::bio_init(
616+
&mut bio,
617+
self.0.s_bdev,
618+
bvec.get(),
619+
1,
620+
bindings::req_op_REQ_OP_READ,
621+
)
622+
};
623+
624+
// SAFETY: `bio` was just initialised with `bio_init` above, so it's safe to call
625+
// `bio_uninit` on the way out.
626+
let mut bio =
627+
ScopeGuard::new_with_data(bio, |mut b| unsafe { bindings::bio_uninit(&mut b) });
628+
629+
// SAFETY: We have one free `bvec` (initialsied above). We also know that size won't exceed
630+
// a page size (build_assert above).
631+
unsafe {
632+
bindings::bio_add_folio_nofail(
633+
&mut *bio,
634+
folio.0 .0.get(),
635+
count * (bindings::SECTOR_SIZE as usize),
636+
0,
637+
)
638+
};
639+
bio.bi_iter.bi_sector = sector;
640+
641+
// SAFETY: The bio was fully initialised above.
642+
to_result(unsafe { bindings::submit_bio_wait(&mut *bio) })?;
643+
Ok(())
644+
}
645+
646+
/// Returns the number of sectors in the underlying block device.
647+
pub fn sector_count(&self) -> Result<u64> {
648+
// Fail requests for non-blockdev file systems. This is a compile-time check.
649+
match T::SUPER_TYPE {
650+
// The superblock is valid and given that it's a blockdev superblock it must have a
651+
// valid `s_bdev`.
652+
Super::BlockDev => Ok(unsafe { bindings::bdev_nr_sectors(self.0.s_bdev) }),
653+
_ => Err(EIO),
654+
}
655+
}
656+
}
657+
514658
struct Tables<T: FileSystem + ?Sized>(T);
515659
impl<T: FileSystem + ?Sized> Tables<T> {
516660
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
@@ -523,9 +667,18 @@ impl<T: FileSystem + ?Sized> Tables<T> {
523667
};
524668

525669
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
526-
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
527-
// the right type and is a valid callback.
528-
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
670+
match T::SUPER_TYPE {
671+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
672+
// the right type and is a valid callback.
673+
Super::BlockDev => unsafe {
674+
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
675+
},
676+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
677+
// the right type and is a valid callback.
678+
Super::Independent => unsafe {
679+
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
680+
},
681+
}
529682
}
530683

531684
unsafe extern "C" fn fill_super_callback(

rust/kernel/fs/buffer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ pub struct View {
4949
}
5050

5151
impl View {
52-
#[allow(dead_code)]
5352
pub(crate) fn new(head: ARef<Head>, offset: usize, size: usize) -> Self {
5453
Self { head, size, offset }
5554
}

0 commit comments

Comments
 (0)