xref: /aosp_15_r20/external/crosvm/disk/src/qcow/refcount.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2018 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use libc::EINVAL;
8*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
9*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
10*bb4ee6a4SAndroid Build Coastguard Worker 
11*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::qcow_raw_file::QcowRawFile;
12*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::CacheMap;
13*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::Cacheable;
14*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::VecCache;
15*bb4ee6a4SAndroid Build Coastguard Worker 
16*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
17*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
18*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
19*bb4ee6a4SAndroid Build Coastguard Worker     /// `EvictingCache` - Error writing a refblock from the cache to disk.
20*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write a refblock from the cache to disk: {0}")]
21*bb4ee6a4SAndroid Build Coastguard Worker     EvictingRefCounts(io::Error),
22*bb4ee6a4SAndroid Build Coastguard Worker     /// `InvalidIndex` - Address requested isn't within the range of the disk.
23*bb4ee6a4SAndroid Build Coastguard Worker     #[error("address requested is not within the range of the disk")]
24*bb4ee6a4SAndroid Build Coastguard Worker     InvalidIndex,
25*bb4ee6a4SAndroid Build Coastguard Worker     /// `NeedCluster` - Handle this error by reading the cluster and calling the function again.
26*bb4ee6a4SAndroid Build Coastguard Worker     #[error("cluster with addr={0} needs to be read")]
27*bb4ee6a4SAndroid Build Coastguard Worker     NeedCluster(u64),
28*bb4ee6a4SAndroid Build Coastguard Worker     /// `NeedNewCluster` - Handle this error by allocating a cluster and calling the function
29*bb4ee6a4SAndroid Build Coastguard Worker     /// again.
30*bb4ee6a4SAndroid Build Coastguard Worker     #[error("new cluster needs to be allocated for refcounts")]
31*bb4ee6a4SAndroid Build Coastguard Worker     NeedNewCluster,
32*bb4ee6a4SAndroid Build Coastguard Worker     /// `ReadingRefCounts` - Error reading the file in to the refcount cache.
33*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read the file into the refcount cache: {0}")]
34*bb4ee6a4SAndroid Build Coastguard Worker     ReadingRefCounts(io::Error),
35*bb4ee6a4SAndroid Build Coastguard Worker }
36*bb4ee6a4SAndroid Build Coastguard Worker 
37*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>;
38*bb4ee6a4SAndroid Build Coastguard Worker 
39*bb4ee6a4SAndroid Build Coastguard Worker /// Represents the refcount entries for an open qcow file.
40*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
41*bb4ee6a4SAndroid Build Coastguard Worker pub struct RefCount {
42*bb4ee6a4SAndroid Build Coastguard Worker     ref_table: VecCache<u64>,
43*bb4ee6a4SAndroid Build Coastguard Worker     refcount_table_offset: u64,
44*bb4ee6a4SAndroid Build Coastguard Worker     refblock_cache: CacheMap<VecCache<u16>>,
45*bb4ee6a4SAndroid Build Coastguard Worker     refcount_block_entries: u64, // number of refcounts in a cluster.
46*bb4ee6a4SAndroid Build Coastguard Worker     cluster_size: u64,
47*bb4ee6a4SAndroid Build Coastguard Worker     max_valid_cluster_offset: u64,
48*bb4ee6a4SAndroid Build Coastguard Worker }
49*bb4ee6a4SAndroid Build Coastguard Worker 
50*bb4ee6a4SAndroid Build Coastguard Worker impl RefCount {
51*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a `RefCount` from `file`, reading the refcount table from `refcount_table_offset`.
52*bb4ee6a4SAndroid Build Coastguard Worker     /// `refcount_table_entries` specifies the number of refcount blocks used by this image.
53*bb4ee6a4SAndroid Build Coastguard Worker     /// `refcount_block_entries` indicates the number of refcounts in each refcount block.
54*bb4ee6a4SAndroid Build Coastguard Worker     /// Each refcount table entry points to a refcount block.
new( raw_file: &mut QcowRawFile, refcount_table_offset: u64, refcount_table_entries: u64, refcount_block_entries: u64, cluster_size: u64, ) -> io::Result<RefCount>55*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(
56*bb4ee6a4SAndroid Build Coastguard Worker         raw_file: &mut QcowRawFile,
57*bb4ee6a4SAndroid Build Coastguard Worker         refcount_table_offset: u64,
58*bb4ee6a4SAndroid Build Coastguard Worker         refcount_table_entries: u64,
59*bb4ee6a4SAndroid Build Coastguard Worker         refcount_block_entries: u64,
60*bb4ee6a4SAndroid Build Coastguard Worker         cluster_size: u64,
61*bb4ee6a4SAndroid Build Coastguard Worker     ) -> io::Result<RefCount> {
62*bb4ee6a4SAndroid Build Coastguard Worker         let ref_table = VecCache::from_vec(raw_file.read_pointer_table(
63*bb4ee6a4SAndroid Build Coastguard Worker             refcount_table_offset,
64*bb4ee6a4SAndroid Build Coastguard Worker             refcount_table_entries,
65*bb4ee6a4SAndroid Build Coastguard Worker             None,
66*bb4ee6a4SAndroid Build Coastguard Worker         )?);
67*bb4ee6a4SAndroid Build Coastguard Worker         let max_valid_cluster_index = (ref_table.len() as u64) * refcount_block_entries - 1;
68*bb4ee6a4SAndroid Build Coastguard Worker         let max_valid_cluster_offset = max_valid_cluster_index * cluster_size;
69*bb4ee6a4SAndroid Build Coastguard Worker         Ok(RefCount {
70*bb4ee6a4SAndroid Build Coastguard Worker             ref_table,
71*bb4ee6a4SAndroid Build Coastguard Worker             refcount_table_offset,
72*bb4ee6a4SAndroid Build Coastguard Worker             refblock_cache: CacheMap::new(50),
73*bb4ee6a4SAndroid Build Coastguard Worker             refcount_block_entries,
74*bb4ee6a4SAndroid Build Coastguard Worker             cluster_size,
75*bb4ee6a4SAndroid Build Coastguard Worker             max_valid_cluster_offset,
76*bb4ee6a4SAndroid Build Coastguard Worker         })
77*bb4ee6a4SAndroid Build Coastguard Worker     }
78*bb4ee6a4SAndroid Build Coastguard Worker 
79*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns the number of refcounts per block.
refcounts_per_block(&self) -> u6480*bb4ee6a4SAndroid Build Coastguard Worker     pub fn refcounts_per_block(&self) -> u64 {
81*bb4ee6a4SAndroid Build Coastguard Worker         self.refcount_block_entries
82*bb4ee6a4SAndroid Build Coastguard Worker     }
83*bb4ee6a4SAndroid Build Coastguard Worker 
84*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns the maximum valid cluster offset in the raw file for this refcount table.
max_valid_cluster_offset(&self) -> u6485*bb4ee6a4SAndroid Build Coastguard Worker     pub fn max_valid_cluster_offset(&self) -> u64 {
86*bb4ee6a4SAndroid Build Coastguard Worker         self.max_valid_cluster_offset
87*bb4ee6a4SAndroid Build Coastguard Worker     }
88*bb4ee6a4SAndroid Build Coastguard Worker 
89*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns `NeedNewCluster` if a new cluster needs to be allocated for refcounts. If an
90*bb4ee6a4SAndroid Build Coastguard Worker     /// existing cluster needs to be read, `NeedCluster(addr)` is returned. The Caller should
91*bb4ee6a4SAndroid Build Coastguard Worker     /// allocate a cluster or read the required one and call this function again with the cluster.
92*bb4ee6a4SAndroid Build Coastguard Worker     /// On success, an optional address of a dropped cluster is returned. The dropped cluster can
93*bb4ee6a4SAndroid Build Coastguard Worker     /// be reused for other purposes.
set_cluster_refcount( &mut self, raw_file: &mut QcowRawFile, cluster_address: u64, refcount: u16, mut new_cluster: Option<(u64, VecCache<u16>)>, ) -> Result<Option<u64>>94*bb4ee6a4SAndroid Build Coastguard Worker     pub fn set_cluster_refcount(
95*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
96*bb4ee6a4SAndroid Build Coastguard Worker         raw_file: &mut QcowRawFile,
97*bb4ee6a4SAndroid Build Coastguard Worker         cluster_address: u64,
98*bb4ee6a4SAndroid Build Coastguard Worker         refcount: u16,
99*bb4ee6a4SAndroid Build Coastguard Worker         mut new_cluster: Option<(u64, VecCache<u16>)>,
100*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<Option<u64>> {
101*bb4ee6a4SAndroid Build Coastguard Worker         let (table_index, block_index) = self.get_refcount_index(cluster_address);
102*bb4ee6a4SAndroid Build Coastguard Worker 
103*bb4ee6a4SAndroid Build Coastguard Worker         let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?;
104*bb4ee6a4SAndroid Build Coastguard Worker 
105*bb4ee6a4SAndroid Build Coastguard Worker         // Fill the cache if this block isn't yet there.
106*bb4ee6a4SAndroid Build Coastguard Worker         if !self.refblock_cache.contains_key(&table_index) {
107*bb4ee6a4SAndroid Build Coastguard Worker             // Need a new cluster
108*bb4ee6a4SAndroid Build Coastguard Worker             if let Some((addr, table)) = new_cluster.take() {
109*bb4ee6a4SAndroid Build Coastguard Worker                 self.ref_table[table_index] = addr;
110*bb4ee6a4SAndroid Build Coastguard Worker                 let ref_table = &self.ref_table;
111*bb4ee6a4SAndroid Build Coastguard Worker                 self.refblock_cache
112*bb4ee6a4SAndroid Build Coastguard Worker                     .insert(table_index, table, |index, evicted| {
113*bb4ee6a4SAndroid Build Coastguard Worker                         raw_file.write_refcount_block(ref_table[index], evicted.get_values())
114*bb4ee6a4SAndroid Build Coastguard Worker                     })
115*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(Error::EvictingRefCounts)?;
116*bb4ee6a4SAndroid Build Coastguard Worker             } else {
117*bb4ee6a4SAndroid Build Coastguard Worker                 if block_addr_disk == 0 {
118*bb4ee6a4SAndroid Build Coastguard Worker                     return Err(Error::NeedNewCluster);
119*bb4ee6a4SAndroid Build Coastguard Worker                 }
120*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::NeedCluster(block_addr_disk));
121*bb4ee6a4SAndroid Build Coastguard Worker             }
122*bb4ee6a4SAndroid Build Coastguard Worker         }
123*bb4ee6a4SAndroid Build Coastguard Worker 
124*bb4ee6a4SAndroid Build Coastguard Worker         // Unwrap is safe here as the entry was filled directly above.
125*bb4ee6a4SAndroid Build Coastguard Worker         let dropped_cluster = if !self.refblock_cache.get(&table_index).unwrap().dirty() {
126*bb4ee6a4SAndroid Build Coastguard Worker             // Free the previously used block and use a new one. Writing modified counts to new
127*bb4ee6a4SAndroid Build Coastguard Worker             // blocks keeps the on-disk state consistent even if it's out of date.
128*bb4ee6a4SAndroid Build Coastguard Worker             if let Some((addr, _)) = new_cluster.take() {
129*bb4ee6a4SAndroid Build Coastguard Worker                 self.ref_table[table_index] = addr;
130*bb4ee6a4SAndroid Build Coastguard Worker                 Some(block_addr_disk)
131*bb4ee6a4SAndroid Build Coastguard Worker             } else {
132*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::NeedNewCluster);
133*bb4ee6a4SAndroid Build Coastguard Worker             }
134*bb4ee6a4SAndroid Build Coastguard Worker         } else {
135*bb4ee6a4SAndroid Build Coastguard Worker             None
136*bb4ee6a4SAndroid Build Coastguard Worker         };
137*bb4ee6a4SAndroid Build Coastguard Worker 
138*bb4ee6a4SAndroid Build Coastguard Worker         self.refblock_cache.get_mut(&table_index).unwrap()[block_index] = refcount;
139*bb4ee6a4SAndroid Build Coastguard Worker         Ok(dropped_cluster)
140*bb4ee6a4SAndroid Build Coastguard Worker     }
141*bb4ee6a4SAndroid Build Coastguard Worker 
142*bb4ee6a4SAndroid Build Coastguard Worker     /// Flush the dirty refcount blocks. This must be done before flushing the table that points to
143*bb4ee6a4SAndroid Build Coastguard Worker     /// the blocks.
flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()>144*bb4ee6a4SAndroid Build Coastguard Worker     pub fn flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()> {
145*bb4ee6a4SAndroid Build Coastguard Worker         // Write out all dirty L2 tables.
146*bb4ee6a4SAndroid Build Coastguard Worker         for (table_index, block) in self.refblock_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
147*bb4ee6a4SAndroid Build Coastguard Worker             let addr = self.ref_table[*table_index];
148*bb4ee6a4SAndroid Build Coastguard Worker             if addr != 0 {
149*bb4ee6a4SAndroid Build Coastguard Worker                 raw_file.write_refcount_block(addr, block.get_values())?;
150*bb4ee6a4SAndroid Build Coastguard Worker             } else {
151*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(std::io::Error::from_raw_os_error(EINVAL));
152*bb4ee6a4SAndroid Build Coastguard Worker             }
153*bb4ee6a4SAndroid Build Coastguard Worker             block.mark_clean();
154*bb4ee6a4SAndroid Build Coastguard Worker         }
155*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
156*bb4ee6a4SAndroid Build Coastguard Worker     }
157*bb4ee6a4SAndroid Build Coastguard Worker 
158*bb4ee6a4SAndroid Build Coastguard Worker     /// Flush the refcount table that keeps the address of the refcounts blocks.
159*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns true if the table changed since the previous `flush_table()` call.
flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool>160*bb4ee6a4SAndroid Build Coastguard Worker     pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> {
161*bb4ee6a4SAndroid Build Coastguard Worker         if self.ref_table.dirty() {
162*bb4ee6a4SAndroid Build Coastguard Worker             raw_file.write_pointer_table(
163*bb4ee6a4SAndroid Build Coastguard Worker                 self.refcount_table_offset,
164*bb4ee6a4SAndroid Build Coastguard Worker                 self.ref_table.get_values(),
165*bb4ee6a4SAndroid Build Coastguard Worker                 0,
166*bb4ee6a4SAndroid Build Coastguard Worker             )?;
167*bb4ee6a4SAndroid Build Coastguard Worker             self.ref_table.mark_clean();
168*bb4ee6a4SAndroid Build Coastguard Worker             Ok(true)
169*bb4ee6a4SAndroid Build Coastguard Worker         } else {
170*bb4ee6a4SAndroid Build Coastguard Worker             Ok(false)
171*bb4ee6a4SAndroid Build Coastguard Worker         }
172*bb4ee6a4SAndroid Build Coastguard Worker     }
173*bb4ee6a4SAndroid Build Coastguard Worker 
174*bb4ee6a4SAndroid Build Coastguard Worker     /// Gets the refcount for a cluster with the given address.
get_cluster_refcount( &mut self, raw_file: &mut QcowRawFile, address: u64, ) -> Result<u16>175*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_cluster_refcount(
176*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
177*bb4ee6a4SAndroid Build Coastguard Worker         raw_file: &mut QcowRawFile,
178*bb4ee6a4SAndroid Build Coastguard Worker         address: u64,
179*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<u16> {
180*bb4ee6a4SAndroid Build Coastguard Worker         let (table_index, block_index) = self.get_refcount_index(address);
181*bb4ee6a4SAndroid Build Coastguard Worker         let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?;
182*bb4ee6a4SAndroid Build Coastguard Worker         if block_addr_disk == 0 {
183*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(0);
184*bb4ee6a4SAndroid Build Coastguard Worker         }
185*bb4ee6a4SAndroid Build Coastguard Worker         if !self.refblock_cache.contains_key(&table_index) {
186*bb4ee6a4SAndroid Build Coastguard Worker             let table = VecCache::from_vec(
187*bb4ee6a4SAndroid Build Coastguard Worker                 raw_file
188*bb4ee6a4SAndroid Build Coastguard Worker                     .read_refcount_block(block_addr_disk)
189*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(Error::ReadingRefCounts)?,
190*bb4ee6a4SAndroid Build Coastguard Worker             );
191*bb4ee6a4SAndroid Build Coastguard Worker             let ref_table = &self.ref_table;
192*bb4ee6a4SAndroid Build Coastguard Worker             self.refblock_cache
193*bb4ee6a4SAndroid Build Coastguard Worker                 .insert(table_index, table, |index, evicted| {
194*bb4ee6a4SAndroid Build Coastguard Worker                     raw_file.write_refcount_block(ref_table[index], evicted.get_values())
195*bb4ee6a4SAndroid Build Coastguard Worker                 })
196*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(Error::EvictingRefCounts)?;
197*bb4ee6a4SAndroid Build Coastguard Worker         }
198*bb4ee6a4SAndroid Build Coastguard Worker         Ok(self.refblock_cache.get(&table_index).unwrap()[block_index])
199*bb4ee6a4SAndroid Build Coastguard Worker     }
200*bb4ee6a4SAndroid Build Coastguard Worker 
201*bb4ee6a4SAndroid Build Coastguard Worker     // Gets the address of the refcount block and the index into the block for the given address.
get_refcount_index(&self, address: u64) -> (usize, usize)202*bb4ee6a4SAndroid Build Coastguard Worker     fn get_refcount_index(&self, address: u64) -> (usize, usize) {
203*bb4ee6a4SAndroid Build Coastguard Worker         let block_index = (address / self.cluster_size) % self.refcount_block_entries;
204*bb4ee6a4SAndroid Build Coastguard Worker         let refcount_table_index = (address / self.cluster_size) / self.refcount_block_entries;
205*bb4ee6a4SAndroid Build Coastguard Worker         (refcount_table_index as usize, block_index as usize)
206*bb4ee6a4SAndroid Build Coastguard Worker     }
207*bb4ee6a4SAndroid Build Coastguard Worker }
208