pub(super) struct MetadataCaches<S: Storage> {
l2: AsyncLruCache<HostCluster, L2Table, L2CacheBackend<S>>,
rb: AsyncLruCache<HostCluster, RefBlock, RefBlockCacheBackend<S>>,
direction: RwLock<CacheDependency>,
}Expand description
Qcow2 metadata caches with flush-ordering coordination.
When allocating clusters, we need to increment the refcounts (from 0 to 1) before writing the L2 pointers. Therefore, before anything is written from the L2 cache to disk, we need to flush the refblock cache.
When freeing clusters, we need to clear the L2 pointers before decrementing the refcounts (to 0). Therefore before anything is written from the refblock cache to disk, we need to flush the L2 cache.
This structure takes care to heed those dependencies (though the general qcow2 code still needs
to announce them by calling .l2_depends_on_rb() for allocations and .rb_depends_on_l2() for
freeing).
Announcing a dependency takes the direction lock and flushes the other cache while holding
it, so direction is the innermost lock: Never acquire a metadata table’s writer_lock (the
lock guarding modifications to its entries, e.g. on L2Table) while holding that direction
lock.
Conversely, note that for an allocating write, an L2 table is held write-locked across its data
I/O while its prepared data mapping is uncommitted, and allocating the clusters announces its
dependency (taking the direction lock) while that write lock is held. Therefore, writing a
table back to disk must never take the table’s writer_lock, because doing so would deadlock
against the still-held lock. It does take the table’s separate writeback_lock (see
L2Table::write_if_modified()); that is a leaf, held only across the write itself and never
across anything that acquires direction.
Fields§
§l2: AsyncLruCache<HostCluster, L2Table, L2CacheBackend<S>>L2 table cache
rb: AsyncLruCache<HostCluster, RefBlock, RefBlockCacheBackend<S>>Refblock cache
direction: RwLock<CacheDependency>Current dependency direction
Wrapped methods hold a read guard for their entire execution, preventing the direction from changing mid-operation. Direction switch methods take a write guard.
Implementations§
Source§impl<S: Storage> MetadataCaches<S>
impl<S: Storage> MetadataCaches<S>
Sourcepub fn new(
file: &Arc<S>,
header: &Arc<Header>,
l2_entries: usize,
rb_entries: usize,
) -> Self
pub fn new( file: &Arc<S>, header: &Arc<Header>, l2_entries: usize, rb_entries: usize, ) -> Self
Create metadata caches for the given file, with the given header.
The L2 cache is going to hold l2_entries tables, the refblock cache will have
rb_entries refcount blocks.
Sourcepub async fn l2_depends_on_rb(&self) -> Result<()>
pub async fn l2_depends_on_rb(&self) -> Result<()>
Make sure the refblock cache is flushed before the L2 cache.
Use before allocating new clusters.
Sourcepub async fn rb_depends_on_l2(&self) -> Result<()>
pub async fn rb_depends_on_l2(&self) -> Result<()>
Make sure the L2 cache is flushed before the refblock cache.
Use before freeing clusters.
Sourcepub async fn l2_get_or_insert(
&self,
cluster_index: HostCluster,
) -> Result<Arc<L2Table>>
pub async fn l2_get_or_insert( &self, cluster_index: HostCluster, ) -> Result<Arc<L2Table>>
Retrieve an L2 table from the cache.
See AsyncLruCache::get_or_insert() for details.
Sourcepub async fn l2_insert(&self, table: Arc<L2Table>) -> Result<()>
pub async fn l2_insert(&self, table: Arc<L2Table>) -> Result<()>
Force-insert an L2 table.
The L2 table must have a cluster associated with it (L2Table::set_cluster()).
See AsyncLruCache::insert() for details.
Sourcepub async unsafe fn invalidate_l2(&self) -> Result<()>
pub async unsafe fn invalidate_l2(&self) -> Result<()>
Invalidate the L2 cache.
§Safety
May cause image corruption, you must guarantee the on-disk state is consistent.
Sourcepub async fn rb_get_or_insert(
&self,
cluster_index: HostCluster,
) -> Result<Arc<RefBlock>>
pub async fn rb_get_or_insert( &self, cluster_index: HostCluster, ) -> Result<Arc<RefBlock>>
Retrieve a refblock from the cache.
See AsyncLruCache::get_or_insert() for details.
Sourcepub async fn rb_insert(&self, rb: Arc<RefBlock>) -> Result<()>
pub async fn rb_insert(&self, rb: Arc<RefBlock>) -> Result<()>
Force-insert a refblock.
The refblock must have a cluster associated with it (RefBlock::set_cluster()).
See AsyncLruCache::insert() for details.
Sourcepub async unsafe fn invalidate_rb(&self) -> Result<()>
pub async unsafe fn invalidate_rb(&self) -> Result<()>
Invalidate the refblock cache.
§Safety
May cause image corruption, you must guarantee the on-disk state is consistent.