Skip to main content

CachingDevice

Struct CachingDevice 

Source
pub struct CachingDevice { /* private fields */ }
Expand description

LRU read-cache wrapper.

§It caches a READ device, and writes through one only if it has one

This took an Arc<dyn BlockDevice> — the read write trait — and every driver in this family mounts a volume through an Arc<dyn BlockRead>. So a read-only mount could not wrap it at all, and four of the six drivers used no cache: not by choice, but because it was not expressible.

The read path never needed to write. It holds the read half now, and the writable half only when the caller had one to give: CachingDevice::new for a device that can be written, CachingDevice::read_only for one that cannot. A write to a cache built the second way is [Error::ReadOnly], which is what the underlying device would have said.

Implementations§

Source§

impl CachingDevice

Source

pub fn new( inner: Arc<dyn BlockDevice>, block_size: u64, capacity: usize, ) -> Arc<Self>

Cache a device that can be written. Writes invalidate the entries they overlap and go through to inner.

Source

pub fn read_only( inner: Arc<dyn BlockRead>, block_size: u64, capacity: usize, ) -> Arc<Self>

Cache a device that is only ever read.

The case every driver here actually has: a volume mounted for reading, behind a BlockRead that was never a BlockDevice.

Source

pub fn stats(&self) -> (u64, u64)

Source

pub fn invalidate_all(&self)

Trait Implementations§

Source§

impl BlockDevice for CachingDevice

Source§

fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()>

Write exactly buf.len() bytes at offset. Default: returns Error::ReadOnly.
Source§

fn flush(&self) -> Result<()>

Flush pending writes to stable storage. No-op by default.
Source§

fn is_writable(&self) -> bool

Whether write_at is likely to succeed. Mount paths use this to decide whether to attempt journal replay or stay strict-read-only.
Source§

impl BlockRead for CachingDevice

Source§

fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>

§A read is served from the blocks it falls in, whatever its size

This used to serve a read only when it was exactly one aligned block, and pass everything else through untouched — including reads of bytes it was already holding.

The drivers almost never read a whole block. Measured on am-fs-xfs against a fixture with a 4096-byte block size, the average read during a directory walk was 1040 bytes: inodes are read at inode size and group headers at sector size, so roughly three quarters of reads missed by construction.

§What it costs

A 512-byte read of an uncached block now fetches 4096. That is a trade of bytes for calls, and it is the right way round for these drivers: the block being fetched is the one holding the inode, and the next inode read is very often in it.

§Where it still passes through

A read larger than the cache’s own capacity would evict everything to hold one answer, so anything spanning more blocks than a useful fraction of the cache goes straight to the device. File data is read in large pieces and would otherwise push out the metadata this exists to keep.

Source§

fn size_bytes(&self) -> u64

Total device size in bytes. Used for bounds checks.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.