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
impl CachingDevice
Sourcepub fn new(
inner: Arc<dyn BlockDevice>,
block_size: u64,
capacity: usize,
) -> Arc<Self> ⓘ
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.
Sourcepub fn read_only(
inner: Arc<dyn BlockRead>,
block_size: u64,
capacity: usize,
) -> Arc<Self> ⓘ
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.
pub fn stats(&self) -> (u64, u64)
pub fn invalidate_all(&self)
Trait Implementations§
Source§impl BlockDevice for CachingDevice
impl BlockDevice for CachingDevice
Source§impl BlockRead for CachingDevice
impl BlockRead for CachingDevice
Source§fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>
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.