Skip to main content

imago/qcow2/
allocation.rs

1//! Cluster allocation.
2//!
3//! Functionality for allocating single clusters and ranges of clusters, and general handling of
4//! refcount structures.
5
6use super::*;
7use crate::sync_primitives::MutexGuard;
8use parking_lot::Mutex as SyncMutex;
9use std::mem;
10use tracing::{event, warn, Level};
11
12/// List of clusters that are no longer in use, and should be freed.
13///
14/// Shared between [`Qcow2`] and its [`Allocator`].
15#[derive(Default)]
16pub(super) struct ClustersToFreeList {
17    /// The list itself.
18    ///
19    /// Synchronous mutex because it is never to be held across `.await` points.
20    list: SyncMutex<Vec<ClustersToFreeEntry>>,
21}
22
23impl ClustersToFreeList {
24    /// Mark `entry` as a cluster to be freed.
25    pub fn push(&self, entry: ClustersToFreeEntry) {
26        self.list.lock().push(entry);
27    }
28
29    /// Check whether the list is empty.
30    pub fn is_empty(&self) -> bool {
31        self.list.lock().is_empty()
32    }
33
34    /// Remove all entries from the list and return them.
35    pub fn take(&self) -> Vec<ClustersToFreeEntry> {
36        mem::take(&mut *self.list.lock())
37    }
38
39    /// Create a guard that enters `entry` into the list when dropped, unless disarmed.
40    ///
41    /// This is for use in allocation paths, where a cluster is allocated, not yet hooked up, and
42    /// anything might cause the hooking-up not to happen, in which case `entry` should be dropped.
43    /// Once it is hooked up, the guard will be disarmed, thus making the cluster permanent.
44    pub fn pending_guard(self: &Arc<Self>, entry: ClustersToFreeEntry) -> PendingClusters {
45        PendingClusters {
46            clusters_to_free: Arc::clone(self),
47            entry: Some(entry),
48        }
49    }
50}
51
52/// Guard for newly allocated clusters that are not yet referenced by any metadata.
53///
54/// If the operation that allocated them fails or is cancelled (i.e. its future is dropped) before
55/// it can reference them, dropping this guard enters them into [`ClustersToFreeList`], so they are
56/// not leaked.  Once they are referenced (at least in memory), disarm the guard.
57#[must_use = "Dropping the guard frees the clusters"]
58pub(super) struct PendingClusters {
59    /// List to enter the clusters into.
60    clusters_to_free: Arc<ClustersToFreeList>,
61
62    /// The clusters; `None` when disarmed.
63    entry: Option<ClustersToFreeEntry>,
64}
65
66impl PendingClusters {
67    /// First cluster of the allocation.
68    pub fn cluster(&self) -> HostCluster {
69        // Only `None` after `make_persistent()`, which consumes `self`
70        self.entry.as_ref().unwrap().first_cluster
71    }
72
73    /// Disarm the guard, moving the clusters from a pending state (to be freed when something goes
74    /// wrong) to a permanent state, because they are referenced now.
75    pub fn make_persistent(mut self) {
76        self.entry.take();
77    }
78}
79
80impl Drop for PendingClusters {
81    fn drop(&mut self) {
82        if let Some(entry) = self.entry.take() {
83            self.clusters_to_free.push(entry);
84        }
85    }
86}
87
88/// Entry in [`ClustersToFreeList`]: A range of clusters to be freed.
89pub(super) struct ClustersToFreeEntry {
90    /// First cluster in the range.
91    first_cluster: HostCluster,
92
93    /// Number of clusters in the range.
94    count: ClusterCount,
95
96    /// Whether these are metadata clusters (or data clusters).
97    metadata: bool,
98}
99
100impl ClustersToFreeEntry {
101    /// Create an entry for the given range of data clusters.
102    pub fn data(first_cluster: HostCluster, count: ClusterCount) -> Self {
103        ClustersToFreeEntry {
104            first_cluster,
105            count,
106            metadata: false,
107        }
108    }
109
110    /// Create an entry for the given range of metadata clusters.
111    pub fn metadata(first_cluster: HostCluster, count: ClusterCount) -> Self {
112        ClustersToFreeEntry {
113            first_cluster,
114            count,
115            metadata: true,
116        }
117    }
118}
119
120/// Central facility for cluster allocation.
121pub(super) struct Allocator<S: Storage> {
122    /// Qcow2 metadata file.
123    file: Arc<S>,
124
125    /// Qcow2 refcount table.
126    reftable: RefTable,
127
128    /// The first free cluster index in the qcow2 file, to speed up allocation.
129    first_free_cluster: HostCluster,
130
131    /// Qcow2 image header.
132    header: Arc<Header>,
133
134    /// L2 and refblock caches with dependency management.
135    caches: Arc<MetadataCaches<S>>,
136
137    /// Clusters that are no longer referenced by image metadata, but not yet freed.
138    ///
139    /// Shared with [`Qcow2`].
140    clusters_to_free: Arc<ClustersToFreeList>,
141}
142
143#[maybe_async]
144impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2<S, F> {
145    /// Return the central allocator instance.
146    ///
147    /// Returns an error for read-only images.
148    async fn allocator(&self) -> io::Result<MutexGuard<'_, Allocator<S>>> {
149        Ok(self
150            .allocator
151            .as_ref()
152            .ok_or_else(|| io::Error::other("Image is read-only"))?
153            .lock()
154            .await)
155    }
156
157    /// Allocate one metadata cluster.
158    ///
159    /// Metadata clusters are allocated exclusively in the metadata (image) file.
160    ///
161    /// The returned guard frees the cluster when dropped, unless it is disarmed by referencing the
162    /// cluster.
163    pub(super) async fn allocate_meta_cluster(&self) -> io::Result<PendingClusters> {
164        self.allocate_meta_clusters(ClusterCount(1)).await
165    }
166
167    /// Allocate multiple continuous metadata clusters.
168    ///
169    /// Useful e.g. for the L1 table or refcount table.
170    ///
171    /// The returned guard frees the clusters when dropped, unless it is disarmed by referencing
172    /// the clusters.
173    pub(super) async fn allocate_meta_clusters(
174        &self,
175        count: ClusterCount,
176    ) -> io::Result<PendingClusters> {
177        self.allocator()
178            .await?
179            .allocate_clusters(count, None, true)
180            .await
181    }
182
183    /// Allocate one data clusters for the given guest cluster.
184    ///
185    /// Without an external data file, data clusters are allocated in the image file, just like
186    /// metadata clusters.
187    ///
188    /// With an external data file, data clusters aren’t really allocated, but just put there at
189    /// the same offset as their guest offset.  Their refcount is not tracked by the qcow2 metadata
190    /// structures (which only cover the metadata (image) file).
191    ///
192    /// The returned guard frees the cluster when dropped, unless it is disarmed by referencing the
193    /// cluster.  (With an external data file, freeing is a no-op.)
194    pub(super) async fn allocate_data_cluster(
195        &self,
196        guest_cluster: GuestCluster,
197    ) -> io::Result<PendingClusters> {
198        if self.header.external_data_file() {
199            let cluster = HostCluster(guest_cluster.0);
200            Ok(self
201                .clusters_to_free
202                .pending_guard(ClustersToFreeEntry::data(cluster, ClusterCount(1))))
203        } else {
204            let mut allocator = self.allocator().await?;
205
206            // Allocate clusters before setting up L2 entries
207            self.caches.l2_depends_on_rb().await?;
208
209            allocator
210                .allocate_clusters(ClusterCount(1), None, false)
211                .await
212        }
213    }
214
215    /// Allocate the data cluster with the given index.
216    ///
217    /// Without a `mandatory_host_cluster` given, this is the same as
218    /// [`Qcow2::allocate_data_cluster()`].
219    ///
220    /// With a `mandatory_host_cluster` given, try to allocate that cluster.  If that is not
221    /// possible because it is already allocated, return `Ok(None)`.
222    pub(super) async fn allocate_data_cluster_at(
223        &self,
224        guest_cluster: GuestCluster,
225        mandatory_host_cluster: Option<HostCluster>,
226    ) -> io::Result<Option<PendingClusters>> {
227        let Some(mandatory_host_cluster) = mandatory_host_cluster else {
228            return self.allocate_data_cluster(guest_cluster).await.map(Some);
229        };
230
231        let allocated = if self.header.external_data_file() {
232            HostCluster(guest_cluster.0) == mandatory_host_cluster
233        } else {
234            let mut allocator = self.allocator().await?;
235
236            // Allocate clusters before setting up L2 entries
237            self.caches.l2_depends_on_rb().await?;
238
239            allocator
240                .allocate_cluster_at(mandatory_host_cluster)
241                .await?
242        };
243
244        Ok(allocated.then(|| {
245            self.clusters_to_free
246                .pending_guard(ClustersToFreeEntry::data(
247                    mandatory_host_cluster,
248                    ClusterCount(1),
249                ))
250        }))
251    }
252
253    /// Free all clusters in `self.clusters_to_free`.
254    ///
255    /// Best-effort operation, like [`Allocator::process_clusters_to_free()`].
256    pub(super) async fn process_clusters_to_free(&self) {
257        // Skip taking the allocator lock if there is nothing to do
258        if self.clusters_to_free.is_empty() {
259            return;
260        }
261
262        if let Ok(mut allocator) = self.allocator().await {
263            allocator.process_clusters_to_free().await;
264        }
265    }
266}
267
268#[maybe_async]
269impl<S: Storage> Allocator<S> {
270    /// Create a new allocator for the given image file.
271    pub async fn new(
272        image: Arc<S>,
273        header: Arc<Header>,
274        caches: Arc<MetadataCaches<S>>,
275        clusters_to_free: Arc<ClustersToFreeList>,
276    ) -> io::Result<Self> {
277        let cb = header.cluster_bits();
278        let rt_offset = header.reftable_offset();
279        let rt_cluster = rt_offset
280            .checked_cluster(cb)
281            .ok_or_else(|| invalid_data(format!("Unaligned refcount table: {rt_offset}")))?;
282
283        let reftable = RefTable::load(
284            image.as_ref(),
285            &header,
286            rt_cluster,
287            header.reftable_entries(),
288        )
289        .await?;
290
291        Ok(Allocator {
292            file: image,
293            reftable,
294            first_free_cluster: HostCluster(0),
295            header,
296            caches,
297            clusters_to_free,
298        })
299    }
300
301    /// Free all clusters in `self.clusters_to_free` (i.e. decrement their refcount).
302    ///
303    /// Must not be called while allocating clusters: Freeing data clusters requires the refblock
304    /// cache to depend on the L2 cache, which is the opposite of what allocating requires.
305    ///
306    /// Best-effort operation.  On error, the given clusters may be leaked, but no errors are ever
307    /// returned (because there is no good way to handle such errors anyway).
308    async fn process_clusters_to_free(&mut self) {
309        let (metadata_clusters, mut data_clusters): (Vec<_>, Vec<_>) = self
310            .clusters_to_free
311            .take()
312            .into_iter()
313            .partition(|entry| entry.metadata);
314
315        // The refcount structures only track allocations in the metadata file (the qcow2 file), so
316        // when freeing data clusters with an external data file, nothing is to be done
317        if self.header.external_data_file() {
318            data_clusters.clear();
319        }
320
321        if metadata_clusters.is_empty() && data_clusters.is_empty() {
322            return;
323        }
324
325        // Writes whose futures have been dropped may still be in flight to these clusters, and
326        // must not be performed after the clusters have been reused.
327        // For data clusters, that can be any cancelled guest write; for metadata clusters, it
328        // could for example be a canceled L1 or reftable entry write, after which the table is
329        // grown and the old one freed.
330        // In any case, all of these clusters will be in the qcow2 (metadata) file, because data
331        // clusters in external data files do not need this treatment.
332        if let Err(err) = self.file.settle_writes().await {
333            warn!("Leaking clusters; cannot settle writes: {err}");
334            return;
335        }
336
337        for entry in metadata_clusters {
338            self.free_clusters(entry.first_cluster, entry.count).await;
339        }
340
341        if data_clusters.is_empty() {
342            return;
343        }
344
345        // Clear L2 entries before deallocating data clusters
346        if let Err(err) = self.caches.rb_depends_on_l2().await {
347            warn!("Leaking clusters; cannot set up cache dependency: {err}");
348            return;
349        }
350
351        for entry in data_clusters {
352            self.free_clusters(entry.first_cluster, entry.count).await;
353        }
354    }
355
356    /// Invaidate the refcount block cache.
357    ///
358    /// # Safety
359    /// May cause image corruption, you must guarantee the on-disk state is consistent.
360    pub async unsafe fn invalidate_rb_cache(&self) -> io::Result<()> {
361        // Safe: Caller says so.
362        unsafe { self.caches.invalidate_rb() }.await
363    }
364
365    /// Allocate clusters in the image file.
366    ///
367    /// `end_cluster` should only be used when allocating refblocks.  When reaching this cluster
368    /// index, abort trying to allocate.  (This is used for allocating refblocks, to prevent
369    /// infinite recursion and speed things up.)
370    ///
371    /// `metadata` specifies whether the clusters are to be used for metadata (true) or data
372    /// (false), in order to create the right kind of [`ClustersToFreeEntry`] for the returned
373    /// [`PendingClusters`] guard.  That returned guard frees the cluster when dropped, unless it
374    /// is disarmed by referencing the cluster.
375    async fn allocate_clusters(
376        &mut self,
377        count: ClusterCount,
378        end_cluster: Option<HostCluster>,
379        metadata: bool,
380    ) -> io::Result<PendingClusters> {
381        let mut index = self.first_free_cluster;
382        loop {
383            if end_cluster == Some(index) {
384                return Err(io::Error::other("Maximum cluster index reached"));
385            }
386
387            let alloc_count = self.allocate_clusters_at(index, count).await?;
388            if alloc_count == count {
389                let to_free_entry = if metadata {
390                    ClustersToFreeEntry::metadata(index, count)
391                } else {
392                    ClustersToFreeEntry::data(index, count)
393                };
394                return Ok(self.clusters_to_free.pending_guard(to_free_entry));
395            }
396
397            index += alloc_count + ClusterCount(1);
398            if index.offset(self.header.cluster_bits()) > MAX_OFFSET {
399                return Err(io::Error::other("Cannot grow qcow2 file any further"));
400            }
401        }
402    }
403
404    /// Allocate the given clusters in the image file.
405    ///
406    /// Allocate up to `count` unallocated clusters starting from `index`.  When encountering an
407    /// already allocated cluster (or any other error), stop, and free the clusters that were just
408    /// newly allocated.
409    ///
410    /// Returns the number of clusters that could be allocated (starting from `index`), which may
411    /// be 0 if `index` has already been allocated.  Note again that in case this is less than
412    /// `count`, those clusters will have been freed again already, so this is just a hint to
413    /// callers that the cluster at `index + count` is already allocated.
414    async fn allocate_clusters_at(
415        &mut self,
416        mut index: HostCluster,
417        mut count: ClusterCount,
418    ) -> io::Result<ClusterCount> {
419        let start_index = index;
420
421        while count > ClusterCount(0) {
422            // Note that `ensure_rb()` in `allocate_cluster_at()` may allocate clusters (new
423            // refblocks), and also a new refcount table.  This can interfere with us allocating a
424            // large continuous region like so (A is our allocation, R is a refblock, imagine a
425            // refblock covers four clusters):
426            //
427            // |AAAA| -- allocated four clusters need new refblock
428            // |AAAA|R   | -- made refblock self-describing, but now allocation cannot go on
429            //
430            // This gets resolved by us retrying, and future refblocks using the region that has
431            // now become free but already has refblocks to cover it:
432            //
433            // |    |RAAA| -- retry after refblock; need a new refblock again
434            // |R   |RAAA|AAAA| -- the new refblock allocates itself in the region we abandoned
435            //
436            // However, eventually, the new refblocks will run into the new start of our allocation
437            // again:
438            //
439            // |RRRR|RAAA|AAAA|AAAA|AAAA|AAAA| -- need new refblock
440            // |RRRR|RAAA|AAAA|AAAA|AAAA|AAAA|R   | -- allocation cannot go on, again
441            // |RRRR|R   |    |    |    |    |RAAA| -- another attempt
442            // |RRRR|RRRR|R...|    |    |    |RAAA|AAAA|AAAA|AAAA|AAAA|...
443            //
444            // As you can see, the hole we leave behind gets larger each time.  So eventually, this
445            // must converge.
446            //
447            // The same applies to the refcount table being allocated instead of just refblocks.
448
449            let result = self.allocate_cluster_at(index).await;
450            if !matches!(result, Ok(true)) {
451                // Already allocated, or some real error occurred; free everything allocated so far.
452                // Do not go through `self.clusters_to_free`: As described above, the freed range
453                // must be available for allocation immediately, so that retrying converges.
454                self.free_clusters(start_index, index - start_index).await;
455                return result.map(|_| index - start_index);
456            }
457
458            count -= ClusterCount(1);
459            index += ClusterCount(1);
460        }
461
462        Ok(index - start_index)
463    }
464
465    /// Allocate the given cluster in the image file.
466    ///
467    /// Return `Ok(true)` if allocation was successful, or `Ok(false)` if the cluster was already
468    /// allocated before.
469    async fn allocate_cluster_at(&mut self, index: HostCluster) -> io::Result<bool> {
470        let rb_bits = self.header.rb_bits();
471        let (rt_index, rb_index) = index.rt_rb_indices(rb_bits);
472
473        let rb = self.ensure_rb(rt_index).await?;
474        let mut rb = rb.lock_write().await;
475        let can_allocate = rb.is_zero(rb_index);
476        if can_allocate {
477            rb.increment(rb_index)?;
478        }
479
480        // We now know this is allocated
481        if index == self.first_free_cluster {
482            self.first_free_cluster = index + ClusterCount(1);
483        }
484
485        Ok(can_allocate)
486    }
487
488    /// Get the refblock referenced by the given reftable index, if any.
489    ///
490    /// If there is no refblock for the given reftable index, return `Ok(None)`.
491    async fn get_rb(&mut self, rt_index: usize) -> io::Result<Option<Arc<RefBlock>>> {
492        let rt_entry = self.reftable.get(rt_index);
493        if let Some(rb_offset) = rt_entry.refblock_offset() {
494            let cb = self.header.cluster_bits();
495            let rb_cluster = rb_offset.checked_cluster(cb).ok_or_else(|| {
496                invalid_data(format!("Unaligned refcount block with index {rt_index}; refcount table entry: {rt_entry:?}"))
497            })?;
498
499            self.caches.rb_get_or_insert(rb_cluster).await.map(Some)
500        } else {
501            Ok(None)
502        }
503    }
504
505    /// Get a refblock for the given reftable index.
506    ///
507    /// If there already is a refblock at that index, return it.  Otherwise, create one and hook it
508    /// up.
509    async fn ensure_rb(&mut self, rt_index: usize) -> io::Result<Arc<RefBlock>> {
510        if let Some(rb) = self.get_rb(rt_index).await? {
511            return Ok(rb);
512        }
513
514        if !self.reftable.in_bounds(rt_index) {
515            self.grow_reftable(rt_index).await?;
516            // `grow_reftable` will allocate new refblocks, so check the index again
517            if let Some(rb) = self.get_rb(rt_index).await? {
518                return Ok(rb);
519            }
520        }
521
522        let mut new_rb = RefBlock::new_cleared(self.file.as_ref(), &self.header)?;
523
524        // This is the first cluster covered by the new refblock
525        let rb_cluster = HostCluster::from_ref_indices(rt_index, 0, self.header.rb_bits());
526
527        // Try to allocate a cluster in the already existing refcount structures.
528        // By stopping looking for clusters at `rb_cluster`, we ensure that we will not land here
529        // in this exact function again, trying to allocate the very same refblock (it is possible
530        // we allocate one before the current one, though), and so prevent any possible infinite
531        // recursion.
532        let alloc_fut_or_result = self.allocate_clusters(ClusterCount(1), Some(rb_cluster), true);
533
534        // Recursion is possible, though, so in async mode, any future must be boxed
535        #[cfg(feature = "async")]
536        let alloc_result = Box::pin(alloc_fut_or_result).await;
537        // Whereas in sync mode, we now already have the result
538        #[cfg(feature = "sync")]
539        let alloc_result = alloc_fut_or_result;
540
541        let new_rb_allocation = if let Ok(new_rb_allocation) = alloc_result {
542            new_rb.set_cluster(new_rb_allocation.cluster());
543            Some(new_rb_allocation)
544        } else {
545            // Place the refblock such that it covers itself.  Its refcount is stored only in
546            // itself, so until it is referenced, there is nothing that could be leaked.
547            new_rb.set_cluster(rb_cluster);
548            new_rb.lock_write().await.increment(0)?;
549            None
550        };
551        new_rb.write(self.file.as_ref()).await?;
552
553        self.reftable
554            .enter_refblock(rt_index, &new_rb, new_rb_allocation)?;
555        self.reftable
556            .write_entry(self.file.as_ref(), rt_index)
557            .await?;
558
559        let new_rb = Arc::new(new_rb);
560        self.caches.rb_insert(Arc::clone(&new_rb)).await?;
561        Ok(new_rb)
562    }
563
564    /// Create a new refcount table covering at least `at_least_index`.
565    ///
566    /// Create a new reftable of the required size, copy all existing refblock references into it,
567    /// ensure it is refcounted itself (also creating new refblocks if necessary), and have the
568    /// image header reference the new refcount table.
569    async fn grow_reftable(&mut self, at_least_index: usize) -> io::Result<()> {
570        let cb = self.header.cluster_bits();
571        let rb_bits = self.header.rb_bits();
572        let rb_entries = 1 << rb_bits;
573
574        let mut new_rt = self.reftable.clone_and_grow(&self.header, at_least_index)?;
575        let rt_clusters = ClusterCount::from_byte_size(new_rt.byte_size() as u64, cb);
576
577        // Find free range
578        let (mut rt_index, mut rb_index) = self.first_free_cluster.rt_rb_indices(rb_bits);
579        let mut free_cluster_index: Option<HostCluster> = None;
580        let mut free_cluster_count = ClusterCount(0);
581
582        // Number of clusters required to allocate both the new reftable and all new refblocks.
583        // Note that `clone_and_grow()` *guarantees* we can fit the final count in there.
584        let mut required_clusters = rt_clusters;
585
586        while free_cluster_count < required_clusters {
587            // `clone_and_grow()` guarantees it can fit
588            assert!(new_rt.in_bounds(rt_index));
589
590            let rt_entry = new_rt.get(rt_index);
591            if let Some(rb_offset) = rt_entry.refblock_offset() {
592                let rb_cluster = rb_offset.checked_cluster(cb).ok_or_else(|| {
593                    invalid_data(format!(
594                        "Unaligned refcount block with index {rt_index}; refcount table entry: {rt_entry:?}"
595                    ))
596                })?;
597
598                let rb = self.caches.rb_get_or_insert(rb_cluster).await?;
599                for i in rb_index..rb_entries {
600                    if rb.is_zero(i) {
601                        let index = HostCluster::from_ref_indices(rt_index, i, rb_bits);
602                        free_cluster_index.get_or_insert(index);
603                        free_cluster_count += ClusterCount(1);
604
605                        if free_cluster_count >= required_clusters {
606                            break;
607                        }
608                    } else if free_cluster_index.is_some() {
609                        free_cluster_index.take();
610                        free_cluster_count = ClusterCount(0);
611                        required_clusters = rt_clusters; // reset
612                    }
613                }
614            } else {
615                let start_index = HostCluster::from_ref_indices(rt_index, rb_index, rb_bits);
616                free_cluster_index.get_or_insert(start_index);
617                free_cluster_count += ClusterCount((rb_entries - rb_index) as u64);
618                // Need to allocate this RB
619                required_clusters += ClusterCount(1);
620            }
621
622            rb_index = 0;
623            rt_index += 1;
624        }
625
626        let mut index = free_cluster_index.unwrap();
627        let mut count = required_clusters;
628
629        // Put refblocks first
630        let rt_index_start = index.rt_index(rb_bits);
631        let rt_index_end = (index + count).0.div_ceil(rb_entries as u64) as usize;
632
633        let mut refblocks = Vec::<Arc<RefBlock>>::new();
634        for rt_i in rt_index_start..rt_index_end {
635            if let Some(rb_offset) = new_rt.get(rt_i).refblock_offset() {
636                // Checked in the loop above
637                let rb_cluster = rb_offset.checked_cluster(cb).unwrap();
638                let rb = self.caches.rb_get_or_insert(rb_cluster).await?;
639                refblocks.push(rb);
640                continue;
641            }
642
643            let mut rb = RefBlock::new_cleared(self.file.as_ref(), &self.header)?;
644            rb.set_cluster(index);
645            new_rt.enter_refblock(rt_i, &rb, None)?;
646            let rb = Arc::new(rb);
647            self.caches.rb_insert(Arc::clone(&rb)).await?;
648            refblocks.push(rb);
649            index += ClusterCount(1);
650            count -= ClusterCount(1);
651        }
652
653        assert!(count >= rt_clusters);
654        new_rt.set_cluster(index);
655
656        // Now set allocation information
657        let start_index = free_cluster_index.unwrap();
658        let end_index = index + rt_clusters;
659
660        for index in start_index.0..end_index.0 {
661            let index = HostCluster(index);
662            let (rt_i, rb_i) = index.rt_rb_indices(rb_bits);
663
664            // `refblocks[0]` is for `rt_index_start`
665            let rb_vec_i = rt_i - rt_index_start;
666            // Incrementing from 0 to 1 must succeed
667            refblocks[rb_vec_i]
668                .lock_write()
669                .await
670                .increment(rb_i)
671                .unwrap();
672        }
673
674        // Any errors from here on may lead to leaked clusters if there are refblocks in
675        // `refblocks` that are already part of the old reftable.
676        // TODO: Try to clean that up, though it seems quite hard for little gain.
677        self.caches.flush_rb().await?;
678        new_rt.write(self.file.as_ref()).await?;
679
680        self.header.set_reftable(&new_rt)?;
681        self.header
682            .write_reftable_pointer(self.file.as_ref())
683            .await?;
684
685        // Must set new reftable before freeing the old reftable’s clusters
686        let mut old_reftable = mem::replace(&mut self.reftable, new_rt);
687        if let Some(old_rt_cluster) = old_reftable.get_cluster() {
688            let old_rt_size = old_reftable.cluster_count();
689            old_reftable.unset_cluster();
690            // Do not run `process_clusters_to_free()` here: We are in the middle of allocating
691            // clusters, and that function would flip the L2-RB cache dependency. Our caller will
692            // drain the list eventually.
693            self.clusters_to_free
694                .push(ClustersToFreeEntry::metadata(old_rt_cluster, old_rt_size));
695        }
696
697        Ok(())
698    }
699
700    /// Free clusters (i.e. decrement their refcount).
701    ///
702    /// Best-effort operation.  On error, the given clusters may be leaked, but no errors are ever
703    /// returned (because there is no good way to handle such errors anyway).
704    async fn free_clusters(&mut self, start: HostCluster, mut count: ClusterCount) {
705        if count.0 == 0 {
706            return;
707        }
708
709        if start < self.first_free_cluster {
710            self.first_free_cluster = start;
711        }
712
713        let rb_bits = self.header.rb_bits();
714        let rb_entries = 1 << rb_bits;
715        let (mut rt_index, mut rb_index) = start.rt_rb_indices(rb_bits);
716
717        while count > ClusterCount(0) {
718            let in_rb_count = cmp::min((rb_entries - rb_index) as u64, count.0) as usize;
719
720            match self.get_rb(rt_index).await {
721                Ok(Some(rb)) => {
722                    let mut rb = rb.lock_write().await;
723                    for i in rb_index..(rb_index + in_rb_count) {
724                        if let Err(err) = rb.decrement(i) {
725                            event!(Level::WARN, "Failed to free cluster: {err}");
726                        }
727                    }
728                }
729
730                Ok(None) => {
731                    event!(
732                        Level::WARN,
733                        "Failed to free {in_rb_count} clusters: Not allocated"
734                    )
735                }
736                Err(err) => event!(Level::WARN, "Failed to free {in_rb_count} clusters: {err}"),
737            }
738
739            count -= ClusterCount(in_rb_count as u64);
740            rb_index = 0;
741            rt_index += 1;
742        }
743    }
744}
745
746#[cfg(test)]
747mod tests {
748    use super::*;
749    use crate::null::Null;
750
751    /// Regression test for a bug in `Allocator::grow_reftable()`'s free-space search loop: when it
752    /// hit a reftable index that had no refblock at all (i.e. a range that has never been
753    /// allocated), it `continue`d without advancing `rt_index` (the increment happened only at the
754    /// bottom of the `while` loop, which the `continue` skipped). If a single iteration wasn't
755    /// enough to satisfy `required_clusters` (which happens once the *total* size of the freshly
756    /// grown reftable reaches or exceeds one refblock's cluster coverage), the loop revisited the
757    /// very same unmapped index again and again, adding its whole refblock-sized range to the
758    /// free-space count each time. This inflated the apparent free space far beyond what was
759    /// actually free starting at that index, so the subsequent "mark allocated" step walked past
760    /// the single truly free refblock-range and right into the next reftable index -- which can be
761    /// a real, already-in-use refblock -- and incremented its refcounts without ever checking they
762    /// were zero. That silently claimed an already-allocated cluster for the new reftable
763    /// structure.
764    #[maybe_async::test(feature = "sync", async(feature = "async", tokio::test))]
765    async fn test_grow_reftable_keeps_in_use_refblock() {
766        // Minimal cluster size and maximal refcount width shrink one refblock's cluster coverage
767        // (and also one reftable cluster's entry count) down to 64, so we don't need a huge image
768        // to reach "new reftable total size >= one refblock's coverage".
769        let cluster_bits = 9u32; // 512-byte clusters
770        let refcount_order = 6u32; // 64-bit refcounts
771        let header = Arc::new(Header::new(cluster_bits, refcount_order, None, None, None));
772        let rb_bits = header.rb_bits();
773        let rb_entries = header.rb_entries();
774        assert_eq!(rb_bits, 6);
775        assert_eq!(rb_entries, 64);
776
777        let null = Arc::new(Null::new(1 << 30));
778        let caches = Arc::new(MetadataCaches::new(&null, &header, 128, 32));
779
780        // We will create two refblocks, one at RT index 4093 (cluster 261952), the other at RT
781        // index 4095 (cluster 262080), the first one completely set (all refcounts 1), the second
782        // one only having its first two entries set (to 1).
783        // Therefore:
784        // - Clusters 261952..=262015 are occupied
785        // - Clusters 262016..=262079 are free
786        // - Clusters 262080..=262081 are occupied
787        // - Rest is free
788        //
789        // The reftable has 4096 entries (to cover everything up to index 4095), thus would need 64
790        // clusters if it were stored anywhere. We ask it to grow to cover index 4096, which
791        // `clone_and_grow()` rounds up to full clusters, i.e. 4160 entries, so 65 clusters, which
792        // does not fit into the 64-cluster long 262016..=262079 range. If we are to put it
793        // at 262082..=262146, then that would go beyond the range of refblock B (262080..=262143),
794        // so we also need a refblock for this range, at RT index 4096. The reftable growth
795        // algorithm puts the new refblocks and the reftable together, so it goes into cluster
796        // 262082, and the reftable starts at cluster 262083 (range 262083..=262147).
797
798        // RB_A: a fully-used refblock at rt_index 4093, where the free-space search starts. It has
799        // no free clusters, so the search legitimately advances to rt_index 4094.
800        let rt_index_a = 4093usize;
801        let rb_a_cluster = HostCluster((rt_index_a * rb_entries) as u64);
802        let mut rb_a = RefBlock::new_cleared(null.as_ref(), &header).unwrap();
803        rb_a.set_cluster(rb_a_cluster);
804        {
805            let mut g = rb_a.lock_write().await;
806            for i in 0..rb_entries {
807                g.increment(i).unwrap();
808            }
809        }
810        let rb_a = Arc::new(rb_a);
811        caches.rb_insert(Arc::clone(&rb_a)).await.unwrap();
812
813        // rt_index 4094 intentionally has no refblock at all: it is a genuinely unmapped, fully
814        // free range.
815
816        // RB_B: the "victim" refblock at rt_index 4095, representing clusters that are already
817        // genuinely allocated (e.g. real guest data/metadata clusters elsewhere in the image).
818        let rt_index_b = 4095usize;
819        let rb_b_cluster = HostCluster((rt_index_b * rb_entries) as u64);
820        let mut rb_b = RefBlock::new_cleared(null.as_ref(), &header).unwrap();
821        rb_b.set_cluster(rb_b_cluster);
822        {
823            let mut g = rb_b.lock_write().await;
824            g.increment(0).unwrap();
825            g.increment(1).unwrap();
826        }
827        let rb_b = Arc::new(rb_b);
828        caches.rb_insert(Arc::clone(&rb_b)).await.unwrap();
829
830        // Build the (old) reftable: entries up to (and including) rt_index_b, with only
831        // rt_index_a and rt_index_b actually mapped to a refblock.
832        let mut data = vec![RefTableEntry::default(); rt_index_b + 1];
833        data[rt_index_a] = <RefTableEntry as TableEntry>::try_from_plain(
834            rb_a_cluster.offset(cluster_bits).0,
835            &header,
836        )
837        .unwrap();
838        data[rt_index_b] = <RefTableEntry as TableEntry>::try_from_plain(
839            rb_b_cluster.offset(cluster_bits).0,
840            &header,
841        )
842        .unwrap();
843        let old_reftable = RefTable::from_data(data.into_boxed_slice(), &header);
844
845        let mut allocator = Allocator {
846            file: Arc::clone(&null),
847            reftable: old_reftable,
848            first_free_cluster: rb_a_cluster,
849            header: Arc::clone(&header),
850            caches: Arc::clone(&caches),
851            clusters_to_free: Arc::new(ClustersToFreeList::default()),
852        };
853
854        // Trigger reftable growth just one index beyond the current bound.
855        allocator.grow_reftable(rt_index_b + 1).await.unwrap();
856
857        // RB_B's clusters were already allocated (refcount 1 each) before the grow, and must
858        // remain untouched by this unrelated reftable-growth operation.
859        assert_eq!(
860            rb_b.get(0),
861            1,
862            "cluster already in use had its refcount corrupted by grow_reftable()"
863        );
864        assert_eq!(
865            rb_b.get(1),
866            1,
867            "cluster already in use had its refcount corrupted by grow_reftable()"
868        );
869
870        // Also verify the reftable is where it is expected, three clusters after the start of what
871        // refblock B describes (the first cluster is B itself, the second is marked as allocated
872        // just like so, and the third is the new refblock at RT index 4096, allocated because the
873        // reftable goes across two refblocks).
874        assert_eq!(
875            allocator.reftable.get_cluster(),
876            Some(HostCluster((rt_index_b * rb_entries + 3) as u64)),
877            "reftable should be placed after refblock B"
878        );
879    }
880}