imago/qcow2/
allocation.rs1use super::*;
7use crate::sync_primitives::MutexGuard;
8use std::mem;
9use tracing::{event, warn, Level};
10
11pub(super) struct Allocator<S: Storage> {
13 file: Arc<S>,
15
16 reftable: RefTable,
18
19 first_free_cluster: HostCluster,
21
22 header: Arc<Header>,
24
25 caches: Arc<MetadataCaches<S>>,
27}
28
29#[maybe_async]
30impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2<S, F> {
31 async fn allocator(&self) -> io::Result<MutexGuard<'_, Allocator<S>>> {
35 Ok(self
36 .allocator
37 .as_ref()
38 .ok_or_else(|| io::Error::other("Image is read-only"))?
39 .lock()
40 .await)
41 }
42
43 pub(super) async fn allocate_meta_cluster(&self) -> io::Result<HostCluster> {
47 self.allocate_meta_clusters(ClusterCount(1)).await
48 }
49
50 pub(super) async fn allocate_meta_clusters(
54 &self,
55 count: ClusterCount,
56 ) -> io::Result<HostCluster> {
57 self.allocator().await?.allocate_clusters(count, None).await
58 }
59
60 pub(super) async fn allocate_data_cluster(
69 &self,
70 guest_cluster: GuestCluster,
71 ) -> io::Result<HostCluster> {
72 if self.header.external_data_file() {
73 Ok(HostCluster(guest_cluster.0))
74 } else {
75 let mut allocator = self.allocator().await?;
76
77 self.caches.l2_depends_on_rb().await?;
79
80 allocator.allocate_clusters(ClusterCount(1), None).await
81 }
82 }
83
84 pub(super) async fn allocate_data_cluster_at(
92 &self,
93 guest_cluster: GuestCluster,
94 mandatory_host_cluster: Option<HostCluster>,
95 ) -> io::Result<Option<HostCluster>> {
96 let Some(mandatory_host_cluster) = mandatory_host_cluster else {
97 return self.allocate_data_cluster(guest_cluster).await.map(Some);
98 };
99
100 if self.header.external_data_file() {
101 let cluster = HostCluster(guest_cluster.0);
102 Ok((cluster == mandatory_host_cluster).then_some(cluster))
103 } else {
104 let mut allocator = self.allocator().await?;
105
106 self.caches.l2_depends_on_rb().await?;
108
109 let cluster = allocator
110 .allocate_cluster_at(mandatory_host_cluster)
111 .await?
112 .then_some(mandatory_host_cluster);
113 Ok(cluster)
114 }
115 }
116
117 pub(super) async fn free_meta_clusters(&self, cluster: HostCluster, count: ClusterCount) {
122 if let Ok(mut allocator) = self.allocator().await {
123 allocator.free_clusters(cluster, count).await
124 }
125 }
126
127 pub(super) async fn free_data_clusters(&self, cluster: HostCluster, count: ClusterCount) {
132 if !self.header.external_data_file() {
133 if let Ok(mut allocator) = self.allocator().await {
134 if let Err(err) = self.caches.rb_depends_on_l2().await {
136 warn!("Leaking clusters; cannot set up cache dependency: {err}");
137 return;
138 }
139
140 allocator.free_clusters(cluster, count).await;
141 }
142 }
143 }
144}
145
146#[maybe_async]
147impl<S: Storage> Allocator<S> {
148 pub async fn new(
150 image: Arc<S>,
151 header: Arc<Header>,
152 caches: Arc<MetadataCaches<S>>,
153 ) -> io::Result<Self> {
154 let cb = header.cluster_bits();
155 let rt_offset = header.reftable_offset();
156 let rt_cluster = rt_offset
157 .checked_cluster(cb)
158 .ok_or_else(|| invalid_data(format!("Unaligned refcount table: {rt_offset}")))?;
159
160 let reftable = RefTable::load(
161 image.as_ref(),
162 &header,
163 rt_cluster,
164 header.reftable_entries(),
165 )
166 .await?;
167
168 Ok(Allocator {
169 file: image,
170 reftable,
171 first_free_cluster: HostCluster(0),
172 header,
173 caches,
174 })
175 }
176
177 pub async unsafe fn invalidate_rb_cache(&self) -> io::Result<()> {
182 unsafe { self.caches.invalidate_rb() }.await
184 }
185
186 async fn allocate_clusters(
192 &mut self,
193 count: ClusterCount,
194 end_cluster: Option<HostCluster>,
195 ) -> io::Result<HostCluster> {
196 let mut index = self.first_free_cluster;
197 loop {
198 if end_cluster == Some(index) {
199 return Err(io::Error::other("Maximum cluster index reached"));
200 }
201
202 let alloc_count = self.allocate_clusters_at(index, count).await?;
203 if alloc_count == count {
204 return Ok(index);
205 }
206
207 index += alloc_count + ClusterCount(1);
208 if index.offset(self.header.cluster_bits()) > MAX_OFFSET {
209 return Err(io::Error::other("Cannot grow qcow2 file any further"));
210 }
211 }
212 }
213
214 async fn allocate_clusters_at(
225 &mut self,
226 mut index: HostCluster,
227 mut count: ClusterCount,
228 ) -> io::Result<ClusterCount> {
229 let start_index = index;
230
231 while count > ClusterCount(0) {
232 let result = self.allocate_cluster_at(index).await;
260 if !matches!(result, Ok(true)) {
261 self.free_clusters(start_index, index - start_index).await;
263 return result.map(|_| index - start_index);
264 }
265
266 count -= ClusterCount(1);
267 index += ClusterCount(1);
268 }
269
270 Ok(index - start_index)
271 }
272
273 async fn allocate_cluster_at(&mut self, index: HostCluster) -> io::Result<bool> {
278 let rb_bits = self.header.rb_bits();
279 let (rt_index, rb_index) = index.rt_rb_indices(rb_bits);
280
281 let rb = self.ensure_rb(rt_index).await?;
282 let mut rb = rb.lock_write().await;
283 let can_allocate = rb.is_zero(rb_index);
284 if can_allocate {
285 rb.increment(rb_index)?;
286 }
287
288 if index == self.first_free_cluster {
290 self.first_free_cluster = index + ClusterCount(1);
291 }
292
293 Ok(can_allocate)
294 }
295
296 async fn get_rb(&mut self, rt_index: usize) -> io::Result<Option<Arc<RefBlock>>> {
300 let rt_entry = self.reftable.get(rt_index);
301 if let Some(rb_offset) = rt_entry.refblock_offset() {
302 let cb = self.header.cluster_bits();
303 let rb_cluster = rb_offset.checked_cluster(cb).ok_or_else(|| {
304 invalid_data(format!("Unaligned refcount block with index {rt_index}; refcount table entry: {rt_entry:?}"))
305 })?;
306
307 self.caches.rb_get_or_insert(rb_cluster).await.map(Some)
308 } else {
309 Ok(None)
310 }
311 }
312
313 async fn ensure_rb(&mut self, rt_index: usize) -> io::Result<Arc<RefBlock>> {
318 if let Some(rb) = self.get_rb(rt_index).await? {
319 return Ok(rb);
320 }
321
322 if !self.reftable.in_bounds(rt_index) {
323 self.grow_reftable(rt_index).await?;
324 if let Some(rb) = self.get_rb(rt_index).await? {
326 return Ok(rb);
327 }
328 }
329
330 let mut new_rb = RefBlock::new_cleared(self.file.as_ref(), &self.header)?;
331
332 let rb_cluster = HostCluster::from_ref_indices(rt_index, 0, self.header.rb_bits());
334
335 let alloc_fut_or_result = self.allocate_clusters(ClusterCount(1), Some(rb_cluster));
341
342 #[cfg(feature = "async")]
344 let alloc_result = Box::pin(alloc_fut_or_result).await;
345 #[cfg(feature = "sync")]
347 let alloc_result = alloc_fut_or_result;
348
349 if let Ok(new_rb_cluster) = alloc_result {
350 new_rb.set_cluster(new_rb_cluster);
351 } else {
352 new_rb.set_cluster(rb_cluster);
354 new_rb.lock_write().await.increment(0)?;
355 }
356 new_rb.write(self.file.as_ref()).await?;
357
358 self.reftable.enter_refblock(rt_index, &new_rb)?;
359 self.reftable
360 .write_entry(self.file.as_ref(), rt_index)
361 .await?;
362
363 let new_rb = Arc::new(new_rb);
364 self.caches
365 .rb_insert(new_rb.get_cluster().unwrap(), Arc::clone(&new_rb))
366 .await?;
367 Ok(new_rb)
368 }
369
370 async fn grow_reftable(&mut self, at_least_index: usize) -> io::Result<()> {
376 let cb = self.header.cluster_bits();
377 let rb_bits = self.header.rb_bits();
378 let rb_entries = 1 << rb_bits;
379
380 let mut new_rt = self.reftable.clone_and_grow(&self.header, at_least_index)?;
381 let rt_clusters = ClusterCount::from_byte_size(new_rt.byte_size() as u64, cb);
382
383 let (mut rt_index, mut rb_index) = self.first_free_cluster.rt_rb_indices(rb_bits);
385 let mut free_cluster_index: Option<HostCluster> = None;
386 let mut free_cluster_count = ClusterCount(0);
387
388 let mut required_clusters = rt_clusters;
391
392 while free_cluster_count < required_clusters {
393 assert!(new_rt.in_bounds(rt_index));
395
396 let rt_entry = new_rt.get(rt_index);
397 let Some(rb_offset) = rt_entry.refblock_offset() else {
398 let start_index = HostCluster::from_ref_indices(rt_index, 0, rb_bits);
399 free_cluster_index.get_or_insert(start_index);
400 free_cluster_count += ClusterCount(rb_entries as u64);
401 required_clusters += ClusterCount(1);
403 continue;
404 };
405
406 let rb_cluster = rb_offset.checked_cluster(cb).ok_or_else(|| {
407 invalid_data(format!("Unaligned refcount block with index {rt_index}; refcount table entry: {rt_entry:?}"))
408 })?;
409
410 let rb = self.caches.rb_get_or_insert(rb_cluster).await?;
411 for i in rb_index..rb_entries {
412 if rb.is_zero(i) {
413 let index = HostCluster::from_ref_indices(rt_index, i, rb_bits);
414 free_cluster_index.get_or_insert(index);
415 free_cluster_count += ClusterCount(1);
416
417 if free_cluster_count >= required_clusters {
418 break;
419 }
420 } else if free_cluster_index.is_some() {
421 free_cluster_index.take();
422 free_cluster_count = ClusterCount(0);
423 required_clusters = rt_clusters; }
425 }
426
427 rb_index = 0;
428 rt_index += 1;
429 }
430
431 let mut index = free_cluster_index.unwrap();
432 let mut count = required_clusters;
433
434 let rt_index_start = index.rt_index(rb_bits);
436 let rt_index_end = (index + count).0.div_ceil(rb_entries as u64) as usize;
437
438 let mut refblocks = Vec::<Arc<RefBlock>>::new();
439 for rt_i in rt_index_start..rt_index_end {
440 if let Some(rb_offset) = new_rt.get(rt_i).refblock_offset() {
441 let rb_cluster = rb_offset.checked_cluster(cb).unwrap();
443 let rb = self.caches.rb_get_or_insert(rb_cluster).await?;
444 refblocks.push(rb);
445 continue;
446 }
447
448 let mut rb = RefBlock::new_cleared(self.file.as_ref(), &self.header)?;
449 rb.set_cluster(index);
450 new_rt.enter_refblock(rt_i, &rb)?;
451 let rb = Arc::new(rb);
452 self.caches.rb_insert(index, Arc::clone(&rb)).await?;
453 refblocks.push(rb);
454 index += ClusterCount(1);
455 count -= ClusterCount(1);
456 }
457
458 assert!(count >= rt_clusters);
459 new_rt.set_cluster(index);
460
461 let start_index = free_cluster_index.unwrap();
463 let end_index = index + rt_clusters;
464
465 for index in start_index.0..end_index.0 {
466 let index = HostCluster(index);
467 let (rt_i, rb_i) = index.rt_rb_indices(rb_bits);
468
469 let rb_vec_i = rt_i - rt_index_start;
471 refblocks[rb_vec_i]
473 .lock_write()
474 .await
475 .increment(rb_i)
476 .unwrap();
477 }
478
479 self.caches.flush_rb().await?;
483 new_rt.write(self.file.as_ref()).await?;
484
485 self.header.set_reftable(&new_rt)?;
486 self.header
487 .write_reftable_pointer(self.file.as_ref())
488 .await?;
489
490 let mut old_reftable = mem::replace(&mut self.reftable, new_rt);
492 if let Some(old_rt_cluster) = old_reftable.get_cluster() {
493 let old_rt_size = old_reftable.cluster_count();
494 old_reftable.unset_cluster();
495 self.free_clusters(old_rt_cluster, old_rt_size).await;
496 }
497
498 Ok(())
499 }
500
501 async fn free_clusters(&mut self, start: HostCluster, mut count: ClusterCount) {
506 if count.0 == 0 {
507 return;
508 }
509
510 if start < self.first_free_cluster {
511 self.first_free_cluster = start;
512 }
513
514 let rb_bits = self.header.rb_bits();
515 let rb_entries = 1 << rb_bits;
516 let (mut rt_index, mut rb_index) = start.rt_rb_indices(rb_bits);
517
518 while count > ClusterCount(0) {
519 let in_rb_count = cmp::min((rb_entries - rb_index) as u64, count.0) as usize;
520
521 match self.get_rb(rt_index).await {
522 Ok(Some(rb)) => {
523 let mut rb = rb.lock_write().await;
524 for i in rb_index..(rb_index + in_rb_count) {
525 if let Err(err) = rb.decrement(i) {
526 event!(Level::WARN, "Failed to free cluster: {err}");
527 }
528 }
529 }
530
531 Ok(None) => {
532 event!(
533 Level::WARN,
534 "Failed to free {in_rb_count} clusters: Not allocated"
535 )
536 }
537 Err(err) => event!(Level::WARN, "Failed to free {in_rb_count} clusters: {err}"),
538 }
539
540 count -= ClusterCount(in_rb_count as u64);
541 rb_index = 0;
542 rt_index += 1;
543 }
544 }
545}