devices/virtio/
balloon.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::BTreeMap;
6use std::collections::VecDeque;
7use std::io::Write;
8use std::sync::Arc;
9
10use anyhow::anyhow;
11use anyhow::Context;
12use balloon_control::BalloonStats;
13use balloon_control::BalloonTubeCommand;
14use balloon_control::BalloonTubeResult;
15use balloon_control::BalloonWS;
16use balloon_control::WSBucket;
17use balloon_control::VIRTIO_BALLOON_WS_MAX_NUM_BINS;
18use balloon_control::VIRTIO_BALLOON_WS_MIN_NUM_BINS;
19use base::debug;
20use base::error;
21use base::warn;
22use base::AsRawDescriptor;
23use base::Event;
24use base::RawDescriptor;
25#[cfg(feature = "registered_events")]
26use base::SendTube;
27use base::Tube;
28use base::WorkerThread;
29use cros_async::block_on;
30use cros_async::sync::RwLock as AsyncRwLock;
31use cros_async::AsyncTube;
32use cros_async::EventAsync;
33use cros_async::Executor;
34#[cfg(feature = "registered_events")]
35use cros_async::SendTubeAsync;
36use data_model::Le16;
37use data_model::Le32;
38use data_model::Le64;
39use futures::channel::mpsc;
40use futures::channel::oneshot;
41use futures::pin_mut;
42use futures::select;
43use futures::select_biased;
44use futures::FutureExt;
45use futures::StreamExt;
46use remain::sorted;
47use serde::Deserialize;
48use serde::Serialize;
49use snapshot::AnySnapshot;
50use thiserror::Error as ThisError;
51use vm_control::api::VmMemoryClient;
52#[cfg(feature = "registered_events")]
53use vm_control::RegisteredEventWithData;
54use vm_memory::GuestAddress;
55use vm_memory::GuestMemory;
56use zerocopy::FromBytes;
57use zerocopy::Immutable;
58use zerocopy::IntoBytes;
59use zerocopy::KnownLayout;
60
61use super::async_utils;
62use super::copy_config;
63use super::create_stop_oneshot;
64use super::DescriptorChain;
65use super::DeviceType;
66use super::Interrupt;
67use super::Queue;
68use super::Reader;
69use super::StoppedWorker;
70use super::VirtioDevice;
71use crate::UnpinRequest;
72use crate::UnpinResponse;
73
74#[sorted]
75#[derive(ThisError, Debug)]
76pub enum BalloonError {
77    /// Failed an async await
78    #[error("failed async await: {0}")]
79    AsyncAwait(cros_async::AsyncError),
80    /// Failed an async await
81    #[error("failed async await: {0}")]
82    AsyncAwaitAnyhow(anyhow::Error),
83    /// Failed to create event.
84    #[error("failed to create event: {0}")]
85    CreatingEvent(base::Error),
86    /// Failed to create async message receiver.
87    #[error("failed to create async message receiver: {0}")]
88    CreatingMessageReceiver(base::TubeError),
89    /// Failed to receive command message.
90    #[error("failed to receive command message: {0}")]
91    ReceivingCommand(base::TubeError),
92    /// Failed to send command response.
93    #[error("failed to send command response: {0}")]
94    SendResponse(base::TubeError),
95    /// Error while writing to virtqueue
96    #[error("failed to write to virtqueue: {0}")]
97    WriteQueue(std::io::Error),
98    /// Failed to write config event.
99    #[error("failed to write config event: {0}")]
100    WritingConfigEvent(base::Error),
101}
102pub type Result<T> = std::result::Result<T, BalloonError>;
103
104const QUEUE_SIZE: u16 = 128;
105
106// Virtqueue indexes that do not depend on advertised features
107const INFLATEQ: usize = 0;
108const DEFLATEQ: usize = 1;
109
110const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12;
111const VIRTIO_BALLOON_PF_SIZE: u64 = 1 << VIRTIO_BALLOON_PFN_SHIFT;
112
113// The feature bitmap for virtio balloon
114const VIRTIO_BALLOON_F_MUST_TELL_HOST: u32 = 0; // Tell before reclaiming pages
115const VIRTIO_BALLOON_F_STATS_VQ: u32 = 1; // Stats reporting enabled
116const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; // Deflate balloon on OOM
117const VIRTIO_BALLOON_F_PAGE_REPORTING: u32 = 5; // Page reporting virtqueue
118                                                // TODO(b/273973298): this should maybe be bit 6? to
119                                                // be changed later
120const VIRTIO_BALLOON_F_WS_REPORTING: u32 = 8; // Working Set Reporting virtqueues
121
122#[derive(Copy, Clone)]
123#[repr(u32)]
124// Balloon virtqueues
125pub enum BalloonFeatures {
126    // Page Reporting enabled
127    PageReporting = VIRTIO_BALLOON_F_PAGE_REPORTING,
128    // WS Reporting enabled
129    WSReporting = VIRTIO_BALLOON_F_WS_REPORTING,
130}
131
132// virtio_balloon_config is the balloon device configuration space defined by the virtio spec.
133#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
134#[repr(C)]
135struct virtio_balloon_config {
136    num_pages: Le32,
137    actual: Le32,
138    free_page_hint_cmd_id: Le32,
139    poison_val: Le32,
140    // WS field is part of proposed spec extension (b/273973298).
141    ws_num_bins: u8,
142    _reserved: [u8; 3],
143}
144
145// BalloonState is shared by the worker and device thread.
146#[derive(Clone, Default, Serialize, Deserialize)]
147struct BalloonState {
148    num_pages: u32,
149    actual_pages: u32,
150    expecting_ws: bool,
151    // Flag indicating that the balloon is in the process of a failable update. This
152    // is set by an Adjust command that has allow_failure set, and is cleared when the
153    // Adjusted success/failure response is sent.
154    failable_update: bool,
155    pending_adjusted_responses: VecDeque<u32>,
156}
157
158// The constants defining stats types in virtio_baloon_stat
159const VIRTIO_BALLOON_S_SWAP_IN: u16 = 0;
160const VIRTIO_BALLOON_S_SWAP_OUT: u16 = 1;
161const VIRTIO_BALLOON_S_MAJFLT: u16 = 2;
162const VIRTIO_BALLOON_S_MINFLT: u16 = 3;
163const VIRTIO_BALLOON_S_MEMFREE: u16 = 4;
164const VIRTIO_BALLOON_S_MEMTOT: u16 = 5;
165const VIRTIO_BALLOON_S_AVAIL: u16 = 6;
166const VIRTIO_BALLOON_S_CACHES: u16 = 7;
167const VIRTIO_BALLOON_S_HTLB_PGALLOC: u16 = 8;
168const VIRTIO_BALLOON_S_HTLB_PGFAIL: u16 = 9;
169const VIRTIO_BALLOON_S_NONSTANDARD_SHMEM: u16 = 65534;
170const VIRTIO_BALLOON_S_NONSTANDARD_UNEVICTABLE: u16 = 65535;
171
172// BalloonStat is used to deserialize stats from the stats_queue.
173#[derive(Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
174#[repr(C, packed)]
175struct BalloonStat {
176    tag: Le16,
177    val: Le64,
178}
179
180impl BalloonStat {
181    fn update_stats(&self, stats: &mut BalloonStats) {
182        let val = Some(self.val.to_native());
183        match self.tag.to_native() {
184            VIRTIO_BALLOON_S_SWAP_IN => stats.swap_in = val,
185            VIRTIO_BALLOON_S_SWAP_OUT => stats.swap_out = val,
186            VIRTIO_BALLOON_S_MAJFLT => stats.major_faults = val,
187            VIRTIO_BALLOON_S_MINFLT => stats.minor_faults = val,
188            VIRTIO_BALLOON_S_MEMFREE => stats.free_memory = val,
189            VIRTIO_BALLOON_S_MEMTOT => stats.total_memory = val,
190            VIRTIO_BALLOON_S_AVAIL => stats.available_memory = val,
191            VIRTIO_BALLOON_S_CACHES => stats.disk_caches = val,
192            VIRTIO_BALLOON_S_HTLB_PGALLOC => stats.hugetlb_allocations = val,
193            VIRTIO_BALLOON_S_HTLB_PGFAIL => stats.hugetlb_failures = val,
194            VIRTIO_BALLOON_S_NONSTANDARD_SHMEM => stats.shared_memory = val,
195            VIRTIO_BALLOON_S_NONSTANDARD_UNEVICTABLE => stats.unevictable_memory = val,
196            _ => (),
197        }
198    }
199}
200
201// virtio_balloon_ws is used to deserialize from the ws data vq.
202#[repr(C)]
203#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
204struct virtio_balloon_ws {
205    tag: Le16,
206    node_id: Le16,
207    // virtio prefers field members to align on a word boundary so we must pad. see:
208    // https://crsrc.org/o/src/third_party/kernel/v5.15/include/uapi/linux/virtio_balloon.h;l=105
209    _reserved: [u8; 4],
210    idle_age_ms: Le64,
211    // TODO(b/273973298): these should become separate fields - bytes for ANON and FILE
212    memory_size_bytes: [Le64; 2],
213}
214
215impl virtio_balloon_ws {
216    fn update_ws(&self, ws: &mut BalloonWS) {
217        let bucket = WSBucket {
218            age: self.idle_age_ms.to_native(),
219            bytes: [
220                self.memory_size_bytes[0].to_native(),
221                self.memory_size_bytes[1].to_native(),
222            ],
223        };
224        ws.ws.push(bucket);
225    }
226}
227
228const _VIRTIO_BALLOON_WS_OP_INVALID: u16 = 0;
229const VIRTIO_BALLOON_WS_OP_REQUEST: u16 = 1;
230const VIRTIO_BALLOON_WS_OP_CONFIG: u16 = 2;
231const _VIRTIO_BALLOON_WS_OP_DISCARD: u16 = 3;
232
233// virtio_balloon_op is used to serialize to the ws cmd vq.
234#[repr(C, packed)]
235#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
236struct virtio_balloon_op {
237    type_: Le16,
238}
239
240fn invoke_desc_handler<F>(ranges: Vec<(u64, u64)>, desc_handler: &mut F)
241where
242    F: FnMut(Vec<(GuestAddress, u64)>),
243{
244    desc_handler(
245        ranges
246            .into_iter()
247            .map(|range| (GuestAddress(range.0), range.1))
248            .collect(),
249    );
250}
251
252// Release a list of guest memory ranges back to the host system.
253// Unpin requests for each inflate range will be sent via `release_memory_tube`
254// if provided, and then `desc_handler` will be called for each inflate range.
255fn release_ranges<F>(
256    release_memory_tube: Option<&Tube>,
257    inflate_ranges: Vec<(u64, u64)>,
258    desc_handler: &mut F,
259) -> anyhow::Result<()>
260where
261    F: FnMut(Vec<(GuestAddress, u64)>),
262{
263    if let Some(tube) = release_memory_tube {
264        let unpin_ranges = inflate_ranges
265            .iter()
266            .map(|v| {
267                (
268                    v.0 >> VIRTIO_BALLOON_PFN_SHIFT,
269                    v.1 / VIRTIO_BALLOON_PF_SIZE,
270                )
271            })
272            .collect();
273        let req = UnpinRequest {
274            ranges: unpin_ranges,
275        };
276        if let Err(e) = tube.send(&req) {
277            error!("failed to send unpin request: {}", e);
278        } else {
279            match tube.recv() {
280                Ok(resp) => match resp {
281                    UnpinResponse::Success => invoke_desc_handler(inflate_ranges, desc_handler),
282                    UnpinResponse::Failed => error!("failed to handle unpin request"),
283                },
284                Err(e) => error!("failed to handle get unpin response: {}", e),
285            }
286        }
287    } else {
288        invoke_desc_handler(inflate_ranges, desc_handler);
289    }
290
291    Ok(())
292}
293
294// Processes one message's list of addresses.
295fn handle_address_chain<F>(
296    release_memory_tube: Option<&Tube>,
297    avail_desc: &mut DescriptorChain,
298    desc_handler: &mut F,
299) -> anyhow::Result<()>
300where
301    F: FnMut(Vec<(GuestAddress, u64)>),
302{
303    // In a long-running system, there is no reason to expect that
304    // a significant number of freed pages are consecutive. However,
305    // batching is relatively simple and can result in significant
306    // gains in a newly booted system, so it's worth attempting.
307    let mut range_start = 0;
308    let mut range_size = 0;
309    let mut inflate_ranges: Vec<(u64, u64)> = Vec::new();
310    for res in avail_desc.reader.iter::<Le32>() {
311        let pfn = match res {
312            Ok(pfn) => pfn,
313            Err(e) => {
314                error!("error while reading unused pages: {}", e);
315                break;
316            }
317        };
318        let guest_address = (u64::from(pfn.to_native())) << VIRTIO_BALLOON_PFN_SHIFT;
319        if range_start + range_size == guest_address {
320            range_size += VIRTIO_BALLOON_PF_SIZE;
321        } else if range_start == guest_address + VIRTIO_BALLOON_PF_SIZE {
322            range_start = guest_address;
323            range_size += VIRTIO_BALLOON_PF_SIZE;
324        } else {
325            // Discontinuity, so flush the previous range. Note range_size
326            // will be 0 on the first iteration, so skip that.
327            if range_size != 0 {
328                inflate_ranges.push((range_start, range_size));
329            }
330            range_start = guest_address;
331            range_size = VIRTIO_BALLOON_PF_SIZE;
332        }
333    }
334    if range_size != 0 {
335        inflate_ranges.push((range_start, range_size));
336    }
337
338    release_ranges(release_memory_tube, inflate_ranges, desc_handler)
339}
340
341// Async task that handles the main balloon inflate and deflate queues.
342async fn handle_queue<F>(
343    mut queue: Queue,
344    mut queue_event: EventAsync,
345    release_memory_tube: Option<&Tube>,
346    mut desc_handler: F,
347    mut stop_rx: oneshot::Receiver<()>,
348) -> Queue
349where
350    F: FnMut(Vec<(GuestAddress, u64)>),
351{
352    loop {
353        let mut avail_desc = match queue
354            .next_async_interruptable(&mut queue_event, &mut stop_rx)
355            .await
356        {
357            Ok(Some(res)) => res,
358            Ok(None) => return queue,
359            Err(e) => {
360                error!("Failed to read descriptor {}", e);
361                return queue;
362            }
363        };
364        if let Err(e) =
365            handle_address_chain(release_memory_tube, &mut avail_desc, &mut desc_handler)
366        {
367            error!("balloon: failed to process inflate addresses: {}", e);
368        }
369        queue.add_used(avail_desc);
370        queue.trigger_interrupt();
371    }
372}
373
374// Processes one page-reporting descriptor.
375fn handle_reported_buffer<F>(
376    release_memory_tube: Option<&Tube>,
377    avail_desc: &DescriptorChain,
378    desc_handler: &mut F,
379) -> anyhow::Result<()>
380where
381    F: FnMut(Vec<(GuestAddress, u64)>),
382{
383    let reported_ranges: Vec<(u64, u64)> = avail_desc
384        .reader
385        .get_remaining_regions()
386        .chain(avail_desc.writer.get_remaining_regions())
387        .map(|r| (r.offset, r.len as u64))
388        .collect();
389
390    release_ranges(release_memory_tube, reported_ranges, desc_handler)
391}
392
393// Async task that handles the page reporting queue.
394async fn handle_reporting_queue<F>(
395    mut queue: Queue,
396    mut queue_event: EventAsync,
397    release_memory_tube: Option<&Tube>,
398    mut desc_handler: F,
399    mut stop_rx: oneshot::Receiver<()>,
400) -> Queue
401where
402    F: FnMut(Vec<(GuestAddress, u64)>),
403{
404    loop {
405        let avail_desc = match queue
406            .next_async_interruptable(&mut queue_event, &mut stop_rx)
407            .await
408        {
409            Ok(Some(res)) => res,
410            Ok(None) => return queue,
411            Err(e) => {
412                error!("Failed to read descriptor {}", e);
413                return queue;
414            }
415        };
416        if let Err(e) = handle_reported_buffer(release_memory_tube, &avail_desc, &mut desc_handler)
417        {
418            error!("balloon: failed to process reported buffer: {}", e);
419        }
420        queue.add_used(avail_desc);
421        queue.trigger_interrupt();
422    }
423}
424
425fn parse_balloon_stats(reader: &mut Reader) -> BalloonStats {
426    let mut stats: BalloonStats = Default::default();
427    for res in reader.iter::<BalloonStat>() {
428        match res {
429            Ok(stat) => stat.update_stats(&mut stats),
430            Err(e) => {
431                error!("error while reading stats: {}", e);
432                break;
433            }
434        };
435    }
436    stats
437}
438
439// Async task that handles the stats queue. Note that the cadence of this is driven by requests for
440// balloon stats from the control pipe.
441// The guests queues an initial buffer on boot, which is read and then this future will block until
442// signaled from the command socket that stats should be collected again.
443async fn handle_stats_queue(
444    mut queue: Queue,
445    mut queue_event: EventAsync,
446    mut stats_rx: mpsc::Receiver<()>,
447    command_tube: &AsyncTube,
448    #[cfg(feature = "registered_events")] registered_evt_q: Option<&SendTubeAsync>,
449    state: Arc<AsyncRwLock<BalloonState>>,
450    mut stop_rx: oneshot::Receiver<()>,
451) -> Queue {
452    let mut avail_desc = match queue
453        .next_async_interruptable(&mut queue_event, &mut stop_rx)
454        .await
455    {
456        // Consume the first stats buffer sent from the guest at startup. It was not
457        // requested by anyone, and the stats are stale.
458        Ok(Some(res)) => res,
459        Ok(None) => return queue,
460        Err(e) => {
461            error!("Failed to read descriptor {}", e);
462            return queue;
463        }
464    };
465
466    loop {
467        select_biased! {
468            msg = stats_rx.next() => {
469                // Wait for a request to read the stats.
470                match msg {
471                    Some(()) => (),
472                    None => {
473                        error!("stats signal channel was closed");
474                        return queue;
475                    }
476                }
477            }
478            _ = stop_rx => return queue,
479        };
480
481        // Request a new stats_desc to the guest.
482        queue.add_used(avail_desc);
483        queue.trigger_interrupt();
484
485        avail_desc = match queue.next_async(&mut queue_event).await {
486            Err(e) => {
487                error!("Failed to read descriptor {}", e);
488                return queue;
489            }
490            Ok(d) => d,
491        };
492        let stats = parse_balloon_stats(&mut avail_desc.reader);
493
494        let actual_pages = state.lock().await.actual_pages as u64;
495        let result = BalloonTubeResult::Stats {
496            balloon_actual: actual_pages << VIRTIO_BALLOON_PFN_SHIFT,
497            stats,
498        };
499        let send_result = command_tube.send(result).await;
500        if let Err(e) = send_result {
501            error!("failed to send stats result: {}", e);
502        }
503
504        #[cfg(feature = "registered_events")]
505        if let Some(registered_evt_q) = registered_evt_q {
506            if let Err(e) = registered_evt_q
507                .send(&RegisteredEventWithData::VirtioBalloonResize)
508                .await
509            {
510                error!("failed to send VirtioBalloonResize event: {}", e);
511            }
512        }
513    }
514}
515
516async fn send_adjusted_response(
517    tube: &AsyncTube,
518    num_pages: u32,
519) -> std::result::Result<(), base::TubeError> {
520    let num_bytes = (num_pages as u64) << VIRTIO_BALLOON_PFN_SHIFT;
521    let result = BalloonTubeResult::Adjusted { num_bytes };
522    tube.send(result).await
523}
524
525enum WSOp {
526    WSReport,
527    WSConfig {
528        bins: Vec<u32>,
529        refresh_threshold: u32,
530        report_threshold: u32,
531    },
532}
533
534async fn handle_ws_op_queue(
535    mut queue: Queue,
536    mut queue_event: EventAsync,
537    mut ws_op_rx: mpsc::Receiver<WSOp>,
538    state: Arc<AsyncRwLock<BalloonState>>,
539    mut stop_rx: oneshot::Receiver<()>,
540) -> Result<Queue> {
541    loop {
542        let op = select_biased! {
543            next_op = ws_op_rx.next().fuse() => {
544                match next_op {
545                    Some(op) => op,
546                    None => {
547                        error!("ws op tube was closed");
548                        break;
549                    }
550                }
551            }
552            _ = stop_rx => {
553                break;
554            }
555        };
556        let mut avail_desc = queue
557            .next_async(&mut queue_event)
558            .await
559            .map_err(BalloonError::AsyncAwait)?;
560        let writer = &mut avail_desc.writer;
561
562        match op {
563            WSOp::WSReport => {
564                {
565                    let mut state = state.lock().await;
566                    state.expecting_ws = true;
567                }
568
569                let ws_r = virtio_balloon_op {
570                    type_: VIRTIO_BALLOON_WS_OP_REQUEST.into(),
571                };
572
573                writer.write_obj(ws_r).map_err(BalloonError::WriteQueue)?;
574            }
575            WSOp::WSConfig {
576                bins,
577                refresh_threshold,
578                report_threshold,
579            } => {
580                let cmd = virtio_balloon_op {
581                    type_: VIRTIO_BALLOON_WS_OP_CONFIG.into(),
582                };
583
584                writer.write_obj(cmd).map_err(BalloonError::WriteQueue)?;
585                writer
586                    .write_all(bins.as_bytes())
587                    .map_err(BalloonError::WriteQueue)?;
588                writer
589                    .write_obj(refresh_threshold)
590                    .map_err(BalloonError::WriteQueue)?;
591                writer
592                    .write_obj(report_threshold)
593                    .map_err(BalloonError::WriteQueue)?;
594            }
595        }
596
597        queue.add_used(avail_desc);
598        queue.trigger_interrupt();
599    }
600
601    Ok(queue)
602}
603
604fn parse_balloon_ws(reader: &mut Reader) -> BalloonWS {
605    let mut ws = BalloonWS::new();
606    for res in reader.iter::<virtio_balloon_ws>() {
607        match res {
608            Ok(ws_msg) => {
609                ws_msg.update_ws(&mut ws);
610            }
611            Err(e) => {
612                error!("error while reading ws: {}", e);
613                break;
614            }
615        }
616    }
617    if ws.ws.len() < VIRTIO_BALLOON_WS_MIN_NUM_BINS || ws.ws.len() > VIRTIO_BALLOON_WS_MAX_NUM_BINS
618    {
619        error!("unexpected number of WS buckets: {}", ws.ws.len());
620    }
621    ws
622}
623
624// Async task that handles the stats queue. Note that the arrival of events on
625// the WS vq may be the result of either a WS request (WS-R) command having
626// been sent to the guest, or an unprompted send due to memory pressue in the
627// guest. If the data was requested, we should also send that back on the
628// command tube.
629async fn handle_ws_data_queue(
630    mut queue: Queue,
631    mut queue_event: EventAsync,
632    command_tube: &AsyncTube,
633    #[cfg(feature = "registered_events")] registered_evt_q: Option<&SendTubeAsync>,
634    state: Arc<AsyncRwLock<BalloonState>>,
635    mut stop_rx: oneshot::Receiver<()>,
636) -> Result<Queue> {
637    loop {
638        let mut avail_desc = match queue
639            .next_async_interruptable(&mut queue_event, &mut stop_rx)
640            .await
641            .map_err(BalloonError::AsyncAwait)?
642        {
643            Some(res) => res,
644            None => return Ok(queue),
645        };
646
647        let ws = parse_balloon_ws(&mut avail_desc.reader);
648
649        let mut state = state.lock().await;
650
651        // update ws report with balloon pages now that we have a lock on state
652        let balloon_actual = (state.actual_pages as u64) << VIRTIO_BALLOON_PFN_SHIFT;
653
654        if state.expecting_ws {
655            let result = BalloonTubeResult::WorkingSet { ws, balloon_actual };
656            let send_result = command_tube.send(result).await;
657            if let Err(e) = send_result {
658                error!("failed to send ws result: {}", e);
659            }
660
661            state.expecting_ws = false;
662        } else {
663            #[cfg(feature = "registered_events")]
664            if let Some(registered_evt_q) = registered_evt_q {
665                if let Err(e) = registered_evt_q
666                    .send(RegisteredEventWithData::from_ws(&ws, balloon_actual))
667                    .await
668                {
669                    error!("failed to send VirtioBalloonWSReport event: {}", e);
670                }
671            }
672        }
673
674        queue.add_used(avail_desc);
675        queue.trigger_interrupt();
676    }
677}
678
679// Async task that handles the command socket. The command socket handles messages from the host
680// requesting that the guest balloon be adjusted or to report guest memory statistics.
681async fn handle_command_tube(
682    command_tube: &AsyncTube,
683    interrupt: Interrupt,
684    state: Arc<AsyncRwLock<BalloonState>>,
685    mut stats_tx: mpsc::Sender<()>,
686    mut ws_op_tx: mpsc::Sender<WSOp>,
687    mut stop_rx: oneshot::Receiver<()>,
688) -> Result<()> {
689    loop {
690        let cmd_res = select_biased! {
691            res = command_tube.next().fuse() => res,
692            _ = stop_rx => return Ok(())
693        };
694        match cmd_res {
695            Ok(command) => match command {
696                BalloonTubeCommand::Adjust {
697                    num_bytes,
698                    allow_failure,
699                } => {
700                    let num_pages = (num_bytes >> VIRTIO_BALLOON_PFN_SHIFT) as u32;
701                    let mut state = state.lock().await;
702
703                    state.num_pages = num_pages;
704                    interrupt.signal_config_changed();
705
706                    if allow_failure {
707                        if num_pages == state.actual_pages {
708                            send_adjusted_response(command_tube, num_pages)
709                                .await
710                                .map_err(BalloonError::SendResponse)?;
711                        } else {
712                            state.failable_update = true;
713                        }
714                    }
715                }
716                BalloonTubeCommand::WorkingSetConfig {
717                    bins,
718                    refresh_threshold,
719                    report_threshold,
720                } => {
721                    if let Err(e) = ws_op_tx.try_send(WSOp::WSConfig {
722                        bins,
723                        refresh_threshold,
724                        report_threshold,
725                    }) {
726                        error!("failed to send config to ws handler: {}", e);
727                    }
728                }
729                BalloonTubeCommand::Stats => {
730                    if let Err(e) = stats_tx.try_send(()) {
731                        error!("failed to signal the stat handler: {}", e);
732                    }
733                }
734                BalloonTubeCommand::WorkingSet => {
735                    if let Err(e) = ws_op_tx.try_send(WSOp::WSReport) {
736                        error!("failed to send report request to ws handler: {}", e);
737                    }
738                }
739            },
740            #[cfg(windows)]
741            Err(base::TubeError::Recv(e)) if e.kind() == std::io::ErrorKind::TimedOut => {
742                // On Windows, async IO tasks like the next/recv above are cancelled as the VM is
743                // shutting down. For the sake of consistency with unix, we can't *just* return
744                // here; instead, we wait for the stop request to arrive, *and then* return.
745                //
746                // The real fix is to get rid of the global unblock pool, since then we won't
747                // cancel the tasks early (b/196911556).
748                let _ = stop_rx.await;
749                return Ok(());
750            }
751            Err(e) => {
752                return Err(BalloonError::ReceivingCommand(e));
753            }
754        }
755    }
756}
757
758async fn handle_pending_adjusted_responses(
759    pending_adjusted_response_event: EventAsync,
760    command_tube: &AsyncTube,
761    state: Arc<AsyncRwLock<BalloonState>>,
762) -> Result<()> {
763    loop {
764        pending_adjusted_response_event
765            .next_val()
766            .await
767            .map_err(BalloonError::AsyncAwait)?;
768        while let Some(num_pages) = state.lock().await.pending_adjusted_responses.pop_front() {
769            send_adjusted_response(command_tube, num_pages)
770                .await
771                .map_err(BalloonError::SendResponse)?;
772        }
773    }
774}
775
776/// Represents queues & events for the balloon device.
777struct BalloonQueues {
778    inflate: Queue,
779    deflate: Queue,
780    stats: Option<Queue>,
781    reporting: Option<Queue>,
782    ws_data: Option<Queue>,
783    ws_op: Option<Queue>,
784}
785
786impl BalloonQueues {
787    fn new(inflate: Queue, deflate: Queue) -> Self {
788        BalloonQueues {
789            inflate,
790            deflate,
791            stats: None,
792            reporting: None,
793            ws_data: None,
794            ws_op: None,
795        }
796    }
797}
798
799/// When the worker is stopped, the queues are preserved here.
800struct PausedQueues {
801    inflate: Queue,
802    deflate: Queue,
803    stats: Option<Queue>,
804    reporting: Option<Queue>,
805    ws_data: Option<Queue>,
806    ws_op: Option<Queue>,
807}
808
809impl PausedQueues {
810    fn new(inflate: Queue, deflate: Queue) -> Self {
811        PausedQueues {
812            inflate,
813            deflate,
814            stats: None,
815            reporting: None,
816            ws_data: None,
817            ws_op: None,
818        }
819    }
820}
821
822fn apply_if_some<F, R>(queue_opt: Option<Queue>, mut func: F)
823where
824    F: FnMut(Queue) -> R,
825{
826    if let Some(queue) = queue_opt {
827        func(queue);
828    }
829}
830
831impl From<Box<PausedQueues>> for BTreeMap<usize, Queue> {
832    fn from(queues: Box<PausedQueues>) -> BTreeMap<usize, Queue> {
833        let mut ret = Vec::new();
834        ret.push(queues.inflate);
835        ret.push(queues.deflate);
836        apply_if_some(queues.stats, |stats| ret.push(stats));
837        apply_if_some(queues.reporting, |reporting| ret.push(reporting));
838        apply_if_some(queues.ws_data, |ws_data| ret.push(ws_data));
839        apply_if_some(queues.ws_op, |ws_op| ret.push(ws_op));
840        // WARNING: We don't use the indices from the virito spec on purpose, see comment in
841        // get_queues_from_map for the rationale.
842        ret.into_iter().enumerate().collect()
843    }
844}
845
846fn free_memory(
847    vm_memory_client: &VmMemoryClient,
848    mem: &GuestMemory,
849    ranges: Vec<(GuestAddress, u64)>,
850) {
851    // If the memory is locked and device sandboxing is enabled (inferred from
852    // `use_punchhole_locked() == false`), then free the memory directly from the sandboxed
853    // process.
854    //
855    // This used to be necessary, but isn't anymore. Instead we keep it just to avoid disrupting
856    // the behavior of crosvm on ChromeOS (possible perf loss). It is likely incompatible with
857    // non-KVM hypervisors. For now it is OK because we don't have known users that do all of (1)
858    // use non-KVM hypervisor, (2) lock guest memory, and (3) enable device sandboxing.
859    #[cfg(any(target_os = "android", target_os = "linux"))]
860    if mem.locked() && !mem.use_punchhole_locked() {
861        for (guest_address, len) in ranges {
862            if let Err(e) = mem.remove_range(guest_address, len) {
863                warn!("Marking pages unused failed: {}, addr={}", e, guest_address);
864            }
865        }
866        return;
867    }
868    if let Err(e) = vm_memory_client.dynamically_free_memory_ranges(ranges) {
869        warn!("Failed to dynamically free memory ranges: {e:#}");
870    }
871}
872
873fn reclaim_memory(vm_memory_client: &VmMemoryClient, ranges: Vec<(GuestAddress, u64)>) {
874    if let Err(e) = vm_memory_client.dynamically_reclaim_memory_ranges(ranges) {
875        warn!("Failed to dynamically reclaim memory range: {e:#}");
876    }
877}
878
879/// Stores data from the worker when it stops so that data can be re-used when
880/// the worker is restarted.
881struct WorkerReturn {
882    release_memory_tube: Option<Tube>,
883    command_tube: Tube,
884    #[cfg(feature = "registered_events")]
885    registered_evt_q: Option<SendTube>,
886    paused_queues: Option<PausedQueues>,
887    vm_memory_client: VmMemoryClient,
888}
889
890// The main worker thread. Initialized the asynchronous worker tasks and passes them to the executor
891// to be processed.
892fn run_worker(
893    inflate_queue: Queue,
894    deflate_queue: Queue,
895    stats_queue: Option<Queue>,
896    reporting_queue: Option<Queue>,
897    ws_data_queue: Option<Queue>,
898    ws_op_queue: Option<Queue>,
899    command_tube: Tube,
900    vm_memory_client: VmMemoryClient,
901    mem: GuestMemory,
902    release_memory_tube: Option<Tube>,
903    interrupt: Interrupt,
904    kill_evt: Event,
905    target_reached_evt: Event,
906    pending_adjusted_response_event: Event,
907    state: Arc<AsyncRwLock<BalloonState>>,
908    #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>,
909) -> WorkerReturn {
910    let ex = Executor::new().unwrap();
911    if let Err(e) = command_tube.send(&BalloonTubeResult::Ready) {
912        error!("failed to send balloon ready result: {}", e);
913    }
914    let command_tube = AsyncTube::new(&ex, command_tube).unwrap();
915    #[cfg(feature = "registered_events")]
916    let registered_evt_q_async = registered_evt_q
917        .as_ref()
918        .map(|q| SendTubeAsync::new(q.try_clone().unwrap(), &ex).unwrap());
919
920    let mut stop_queue_oneshots = Vec::new();
921
922    // We need a block to release all references to command_tube at the end before returning it.
923    let paused_queues = {
924        // The first queue is used for inflate messages
925        let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
926        let inflate_queue_evt = inflate_queue
927            .event()
928            .try_clone()
929            .expect("failed to clone queue event");
930        let inflate = handle_queue(
931            inflate_queue,
932            EventAsync::new(inflate_queue_evt, &ex).expect("failed to create async event"),
933            release_memory_tube.as_ref(),
934            |ranges| free_memory(&vm_memory_client, &mem, ranges),
935            stop_rx,
936        );
937        let inflate = inflate.fuse();
938        pin_mut!(inflate);
939
940        // The second queue is used for deflate messages
941        let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
942        let deflate_queue_evt = deflate_queue
943            .event()
944            .try_clone()
945            .expect("failed to clone queue event");
946        let deflate = handle_queue(
947            deflate_queue,
948            EventAsync::new(deflate_queue_evt, &ex).expect("failed to create async event"),
949            None,
950            |ranges| reclaim_memory(&vm_memory_client, ranges),
951            stop_rx,
952        );
953        let deflate = deflate.fuse();
954        pin_mut!(deflate);
955
956        // The next queue is used for stats messages if VIRTIO_BALLOON_F_STATS_VQ is negotiated.
957        let (stats_tx, stats_rx) = mpsc::channel::<()>(1);
958        let has_stats_queue = stats_queue.is_some();
959        let stats = if let Some(stats_queue) = stats_queue {
960            let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
961            let stats_queue_evt = stats_queue
962                .event()
963                .try_clone()
964                .expect("failed to clone queue event");
965            handle_stats_queue(
966                stats_queue,
967                EventAsync::new(stats_queue_evt, &ex).expect("failed to create async event"),
968                stats_rx,
969                &command_tube,
970                #[cfg(feature = "registered_events")]
971                registered_evt_q_async.as_ref(),
972                state.clone(),
973                stop_rx,
974            )
975            .left_future()
976        } else {
977            std::future::pending().right_future()
978        };
979        let stats = stats.fuse();
980        pin_mut!(stats);
981
982        // The next queue is used for reporting messages
983        let has_reporting_queue = reporting_queue.is_some();
984        let reporting = if let Some(reporting_queue) = reporting_queue {
985            let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
986            let reporting_queue_evt = reporting_queue
987                .event()
988                .try_clone()
989                .expect("failed to clone queue event");
990            handle_reporting_queue(
991                reporting_queue,
992                EventAsync::new(reporting_queue_evt, &ex).expect("failed to create async event"),
993                release_memory_tube.as_ref(),
994                |ranges| free_memory(&vm_memory_client, &mem, ranges),
995                stop_rx,
996            )
997            .left_future()
998        } else {
999            std::future::pending().right_future()
1000        };
1001        let reporting = reporting.fuse();
1002        pin_mut!(reporting);
1003
1004        // If VIRTIO_BALLOON_F_WS_REPORTING is set 2 queues must handled - one for WS data and one
1005        // for WS notifications.
1006        let has_ws_data_queue = ws_data_queue.is_some();
1007        let ws_data = if let Some(ws_data_queue) = ws_data_queue {
1008            let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
1009            let ws_data_queue_evt = ws_data_queue
1010                .event()
1011                .try_clone()
1012                .expect("failed to clone queue event");
1013            handle_ws_data_queue(
1014                ws_data_queue,
1015                EventAsync::new(ws_data_queue_evt, &ex).expect("failed to create async event"),
1016                &command_tube,
1017                #[cfg(feature = "registered_events")]
1018                registered_evt_q_async.as_ref(),
1019                state.clone(),
1020                stop_rx,
1021            )
1022            .left_future()
1023        } else {
1024            std::future::pending().right_future()
1025        };
1026        let ws_data = ws_data.fuse();
1027        pin_mut!(ws_data);
1028
1029        let (ws_op_tx, ws_op_rx) = mpsc::channel::<WSOp>(1);
1030        let has_ws_op_queue = ws_op_queue.is_some();
1031        let ws_op = if let Some(ws_op_queue) = ws_op_queue {
1032            let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
1033            let ws_op_queue_evt = ws_op_queue
1034                .event()
1035                .try_clone()
1036                .expect("failed to clone queue event");
1037            handle_ws_op_queue(
1038                ws_op_queue,
1039                EventAsync::new(ws_op_queue_evt, &ex).expect("failed to create async event"),
1040                ws_op_rx,
1041                state.clone(),
1042                stop_rx,
1043            )
1044            .left_future()
1045        } else {
1046            std::future::pending().right_future()
1047        };
1048        let ws_op = ws_op.fuse();
1049        pin_mut!(ws_op);
1050
1051        // Future to handle command messages that resize the balloon.
1052        let stop_rx = create_stop_oneshot(&mut stop_queue_oneshots);
1053        let command = handle_command_tube(
1054            &command_tube,
1055            interrupt.clone(),
1056            state.clone(),
1057            stats_tx,
1058            ws_op_tx,
1059            stop_rx,
1060        );
1061        pin_mut!(command);
1062
1063        // Send a message if balloon target reached event is triggered.
1064        let target_reached = handle_target_reached(&ex, target_reached_evt, &vm_memory_client);
1065        pin_mut!(target_reached);
1066
1067        // Exit if the kill event is triggered.
1068        let kill = async_utils::await_and_exit(&ex, kill_evt);
1069        pin_mut!(kill);
1070
1071        let pending_adjusted = handle_pending_adjusted_responses(
1072            EventAsync::new(pending_adjusted_response_event, &ex)
1073                .expect("failed to create async event"),
1074            &command_tube,
1075            state,
1076        );
1077        pin_mut!(pending_adjusted);
1078
1079        let res = ex.run_until(async {
1080            select! {
1081                _ = kill.fuse() => (),
1082                _ = inflate => return Err(anyhow!("inflate stopped unexpectedly")),
1083                _ = deflate => return Err(anyhow!("deflate stopped unexpectedly")),
1084                _ = stats => return Err(anyhow!("stats stopped unexpectedly")),
1085                _ = reporting => return Err(anyhow!("reporting stopped unexpectedly")),
1086                _ = command.fuse() => return Err(anyhow!("command stopped unexpectedly")),
1087                _ = ws_op => return Err(anyhow!("ws_op stopped unexpectedly")),
1088                _ = pending_adjusted.fuse() => return Err(anyhow!("pending_adjusted stopped unexpectedly")),
1089                _ = ws_data => return Err(anyhow!("ws_data stopped unexpectedly")),
1090                _ = target_reached.fuse() => return Err(anyhow!("target_reached stopped unexpectedly")),
1091            }
1092
1093            // Worker is shutting down. To recover the queues, we have to signal
1094            // all the queue futures to exit.
1095            for stop_tx in stop_queue_oneshots {
1096                if stop_tx.send(()).is_err() {
1097                    return Err(anyhow!("failed to request stop for queue future"));
1098                }
1099            }
1100
1101            // Collect all the queues (awaiting any queue future should now
1102            // return its Queue immediately).
1103            let mut paused_queues = PausedQueues::new(
1104                inflate.await,
1105                deflate.await,
1106            );
1107            if has_reporting_queue {
1108                paused_queues.reporting = Some(reporting.await);
1109            }
1110            if has_stats_queue {
1111                paused_queues.stats = Some(stats.await);
1112            }
1113            if has_ws_data_queue {
1114                paused_queues.ws_data = Some(ws_data.await.context("failed to stop ws_data queue")?);
1115            }
1116            if has_ws_op_queue {
1117                paused_queues.ws_op = Some(ws_op.await.context("failed to stop ws_op queue")?);
1118            }
1119            Ok(paused_queues)
1120        });
1121
1122        match res {
1123            Err(e) => {
1124                error!("error happened in executor: {}", e);
1125                None
1126            }
1127            Ok(main_future_res) => match main_future_res {
1128                Ok(paused_queues) => Some(paused_queues),
1129                Err(e) => {
1130                    error!("error happened in main balloon future: {}", e);
1131                    None
1132                }
1133            },
1134        }
1135    };
1136
1137    WorkerReturn {
1138        command_tube: command_tube.into(),
1139        paused_queues,
1140        release_memory_tube,
1141        #[cfg(feature = "registered_events")]
1142        registered_evt_q,
1143        vm_memory_client,
1144    }
1145}
1146
1147async fn handle_target_reached(
1148    ex: &Executor,
1149    target_reached_evt: Event,
1150    vm_memory_client: &VmMemoryClient,
1151) -> anyhow::Result<()> {
1152    let event_async =
1153        EventAsync::new(target_reached_evt, ex).context("failed to create EventAsync")?;
1154    loop {
1155        // Wait for target reached trigger.
1156        let _ = event_async.next_val().await;
1157        // Send the message to vm_control on the event. We don't have to read the current
1158        // size yet.
1159        if let Err(e) = vm_memory_client.balloon_target_reached(0) {
1160            warn!("Failed to send or receive allocation complete request: {e:#}");
1161        }
1162    }
1163    // The above loop will never terminate and there is no reason to terminate it either. However,
1164    // the function is used in an executor that expects a Result<> return. Make sure that clippy
1165    // doesn't enforce the unreachable_code condition.
1166    #[allow(unreachable_code)]
1167    Ok(())
1168}
1169
1170/// Virtio device for memory balloon inflation/deflation.
1171pub struct Balloon {
1172    command_tube: Option<Tube>,
1173    vm_memory_client: Option<VmMemoryClient>,
1174    release_memory_tube: Option<Tube>,
1175    pending_adjusted_response_event: Event,
1176    state: Arc<AsyncRwLock<BalloonState>>,
1177    features: u64,
1178    acked_features: u64,
1179    worker_thread: Option<WorkerThread<WorkerReturn>>,
1180    #[cfg(feature = "registered_events")]
1181    registered_evt_q: Option<SendTube>,
1182    ws_num_bins: u8,
1183    target_reached_evt: Option<Event>,
1184    queue_sizes: Vec<u16>,
1185}
1186
1187/// Snapshot of the [Balloon] state.
1188#[derive(Serialize, Deserialize)]
1189struct BalloonSnapshot {
1190    state: BalloonState,
1191    features: u64,
1192    acked_features: u64,
1193    ws_num_bins: u8,
1194}
1195
1196impl Balloon {
1197    /// Creates a new virtio balloon device.
1198    /// To let Balloon able to successfully release the memory which are pinned
1199    /// by CoIOMMU to host, the release_memory_tube will be used to send the inflate
1200    /// ranges to CoIOMMU with UnpinRequest/UnpinResponse messages, so that The
1201    /// memory in the inflate range can be unpinned first.
1202    pub fn new(
1203        base_features: u64,
1204        command_tube: Tube,
1205        vm_memory_client: VmMemoryClient,
1206        release_memory_tube: Option<Tube>,
1207        init_balloon_size: u64,
1208        enabled_features: u64,
1209        #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>,
1210        ws_num_bins: u8,
1211    ) -> Result<Balloon> {
1212        let features = base_features
1213            | 1 << VIRTIO_BALLOON_F_MUST_TELL_HOST
1214            | 1 << VIRTIO_BALLOON_F_STATS_VQ
1215            | 1 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM
1216            | enabled_features;
1217
1218        let mut queue_sizes = Vec::new();
1219        queue_sizes.push(QUEUE_SIZE); // inflateq
1220        queue_sizes.push(QUEUE_SIZE); // deflateq
1221        if features & (1 << VIRTIO_BALLOON_F_STATS_VQ) != 0 {
1222            queue_sizes.push(QUEUE_SIZE); // statsq
1223        }
1224        if features & (1 << VIRTIO_BALLOON_F_PAGE_REPORTING) != 0 {
1225            queue_sizes.push(QUEUE_SIZE); // reporting_vq
1226        }
1227        if features & (1 << VIRTIO_BALLOON_F_WS_REPORTING) != 0 {
1228            queue_sizes.push(QUEUE_SIZE); // ws_data
1229            queue_sizes.push(QUEUE_SIZE); // ws_cmd
1230        }
1231
1232        Ok(Balloon {
1233            command_tube: Some(command_tube),
1234            vm_memory_client: Some(vm_memory_client),
1235            release_memory_tube,
1236            pending_adjusted_response_event: Event::new().map_err(BalloonError::CreatingEvent)?,
1237            state: Arc::new(AsyncRwLock::new(BalloonState {
1238                num_pages: (init_balloon_size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
1239                actual_pages: 0,
1240                failable_update: false,
1241                pending_adjusted_responses: VecDeque::new(),
1242                expecting_ws: false,
1243            })),
1244            worker_thread: None,
1245            features,
1246            acked_features: 0,
1247            #[cfg(feature = "registered_events")]
1248            registered_evt_q,
1249            ws_num_bins,
1250            target_reached_evt: None,
1251            queue_sizes,
1252        })
1253    }
1254
1255    fn get_config(&self) -> virtio_balloon_config {
1256        let state = block_on(self.state.lock());
1257        virtio_balloon_config {
1258            num_pages: state.num_pages.into(),
1259            actual: state.actual_pages.into(),
1260            // crosvm does not (currently) use free_page_hint_cmd_id or
1261            // poison_val, but they must be present in the right order and size
1262            // for the virtio-balloon driver in the guest to deserialize the
1263            // config correctly.
1264            free_page_hint_cmd_id: 0.into(),
1265            poison_val: 0.into(),
1266            ws_num_bins: self.ws_num_bins,
1267            _reserved: [0, 0, 0],
1268        }
1269    }
1270
1271    fn stop_worker(&mut self) -> StoppedWorker<PausedQueues> {
1272        if let Some(worker_thread) = self.worker_thread.take() {
1273            let worker_ret = worker_thread.stop();
1274            self.release_memory_tube = worker_ret.release_memory_tube;
1275            self.command_tube = Some(worker_ret.command_tube);
1276            #[cfg(feature = "registered_events")]
1277            {
1278                self.registered_evt_q = worker_ret.registered_evt_q;
1279            }
1280            self.vm_memory_client = Some(worker_ret.vm_memory_client);
1281
1282            if let Some(queues) = worker_ret.paused_queues {
1283                StoppedWorker::WithQueues(Box::new(queues))
1284            } else {
1285                StoppedWorker::MissingQueues
1286            }
1287        } else {
1288            StoppedWorker::AlreadyStopped
1289        }
1290    }
1291
1292    /// Given a filtered queue vector from [VirtioDevice::activate], extract
1293    /// the queues (accounting for queues that are missing because the features
1294    /// are not negotiated) into a structure that is easier to work with.
1295    fn get_queues_from_map(
1296        &self,
1297        mut queues: BTreeMap<usize, Queue>,
1298    ) -> anyhow::Result<BalloonQueues> {
1299        let inflate_queue = queues.remove(&INFLATEQ).context("missing inflateq")?;
1300        let deflate_queue = queues.remove(&DEFLATEQ).context("missing deflateq")?;
1301        let mut queue_struct = BalloonQueues::new(inflate_queue, deflate_queue);
1302
1303        // Queues whose existence depends on advertised features start at queue index 2.
1304        let mut next_queue_index = 2;
1305        let mut next_queue = || {
1306            let idx = next_queue_index;
1307            next_queue_index += 1;
1308            idx
1309        };
1310
1311        if self.features & (1 << VIRTIO_BALLOON_F_STATS_VQ) != 0 {
1312            let statsq = next_queue();
1313            if self.acked_features & (1 << VIRTIO_BALLOON_F_STATS_VQ) != 0 {
1314                queue_struct.stats = Some(queues.remove(&statsq).context("missing statsq")?);
1315            }
1316        }
1317
1318        if self.features & (1 << VIRTIO_BALLOON_F_PAGE_REPORTING) != 0 {
1319            let reporting_vq = next_queue();
1320            if self.acked_features & (1 << VIRTIO_BALLOON_F_PAGE_REPORTING) != 0 {
1321                queue_struct.reporting = Some(
1322                    queues
1323                        .remove(&reporting_vq)
1324                        .context("missing reporting_vq")?,
1325                );
1326            }
1327        }
1328
1329        if self.features & (1 << VIRTIO_BALLOON_F_WS_REPORTING) != 0 {
1330            let ws_data_vq = next_queue();
1331            let ws_op_vq = next_queue();
1332            if self.acked_features & (1 << VIRTIO_BALLOON_F_WS_REPORTING) != 0 {
1333                queue_struct.ws_data =
1334                    Some(queues.remove(&ws_data_vq).context("missing ws_data_vq")?);
1335                queue_struct.ws_op = Some(queues.remove(&ws_op_vq).context("missing ws_op_vq")?);
1336            }
1337        }
1338
1339        if !queues.is_empty() {
1340            return Err(anyhow!("unexpected queues {:?}", queues.into_keys()));
1341        }
1342
1343        Ok(queue_struct)
1344    }
1345
1346    fn start_worker(
1347        &mut self,
1348        mem: GuestMemory,
1349        interrupt: Interrupt,
1350        queues: BalloonQueues,
1351    ) -> anyhow::Result<()> {
1352        let (self_target_reached_evt, target_reached_evt) = Event::new()
1353            .and_then(|e| Ok((e.try_clone()?, e)))
1354            .context("failed to create target_reached Event pair: {}")?;
1355        self.target_reached_evt = Some(self_target_reached_evt);
1356
1357        let state = self.state.clone();
1358
1359        let command_tube = self.command_tube.take().unwrap();
1360
1361        let vm_memory_client = self.vm_memory_client.take().unwrap();
1362        let release_memory_tube = self.release_memory_tube.take();
1363        #[cfg(feature = "registered_events")]
1364        let registered_evt_q = self.registered_evt_q.take();
1365        let pending_adjusted_response_event = self
1366            .pending_adjusted_response_event
1367            .try_clone()
1368            .context("failed to clone Event")?;
1369
1370        self.worker_thread = Some(WorkerThread::start("v_balloon", move |kill_evt| {
1371            run_worker(
1372                queues.inflate,
1373                queues.deflate,
1374                queues.stats,
1375                queues.reporting,
1376                queues.ws_data,
1377                queues.ws_op,
1378                command_tube,
1379                vm_memory_client,
1380                mem,
1381                release_memory_tube,
1382                interrupt,
1383                kill_evt,
1384                target_reached_evt,
1385                pending_adjusted_response_event,
1386                state,
1387                #[cfg(feature = "registered_events")]
1388                registered_evt_q,
1389            )
1390        }));
1391
1392        Ok(())
1393    }
1394}
1395
1396impl VirtioDevice for Balloon {
1397    fn keep_rds(&self) -> Vec<RawDescriptor> {
1398        let mut rds = Vec::new();
1399        if let Some(command_tube) = &self.command_tube {
1400            rds.push(command_tube.as_raw_descriptor());
1401        }
1402        if let Some(vm_memory_client) = &self.vm_memory_client {
1403            rds.push(vm_memory_client.as_raw_descriptor());
1404        }
1405        if let Some(release_memory_tube) = &self.release_memory_tube {
1406            rds.push(release_memory_tube.as_raw_descriptor());
1407        }
1408        #[cfg(feature = "registered_events")]
1409        if let Some(registered_evt_q) = &self.registered_evt_q {
1410            rds.push(registered_evt_q.as_raw_descriptor());
1411        }
1412        rds.push(self.pending_adjusted_response_event.as_raw_descriptor());
1413        rds
1414    }
1415
1416    fn device_type(&self) -> DeviceType {
1417        DeviceType::Balloon
1418    }
1419
1420    fn queue_max_sizes(&self) -> &[u16] {
1421        &self.queue_sizes
1422    }
1423
1424    fn read_config(&self, offset: u64, data: &mut [u8]) {
1425        copy_config(data, 0, self.get_config().as_bytes(), offset);
1426    }
1427
1428    fn write_config(&mut self, offset: u64, data: &[u8]) {
1429        let mut config = self.get_config();
1430        copy_config(config.as_mut_bytes(), offset, data, 0);
1431        let mut state = block_on(self.state.lock());
1432        state.actual_pages = config.actual.to_native();
1433
1434        // If balloon has updated to the requested memory, let the hypervisor know.
1435        if config.num_pages == config.actual {
1436            debug!(
1437                "sending target reached event at {}",
1438                u32::from(config.num_pages)
1439            );
1440            self.target_reached_evt.as_ref().map(|e| e.signal());
1441        }
1442        if state.failable_update && state.actual_pages == state.num_pages {
1443            state.failable_update = false;
1444            let num_pages = state.num_pages;
1445            state.pending_adjusted_responses.push_back(num_pages);
1446            let _ = self.pending_adjusted_response_event.signal();
1447        }
1448    }
1449
1450    fn features(&self) -> u64 {
1451        self.features
1452    }
1453
1454    fn ack_features(&mut self, mut value: u64) {
1455        if value & !self.features != 0 {
1456            warn!("virtio_balloon got unknown feature ack {:x}", value);
1457            value &= self.features;
1458        }
1459        self.acked_features |= value;
1460    }
1461
1462    fn activate(
1463        &mut self,
1464        mem: GuestMemory,
1465        interrupt: Interrupt,
1466        queues: BTreeMap<usize, Queue>,
1467    ) -> anyhow::Result<()> {
1468        let queues = self.get_queues_from_map(queues)?;
1469        self.start_worker(mem, interrupt, queues)
1470    }
1471
1472    fn reset(&mut self) -> anyhow::Result<()> {
1473        let _worker = self.stop_worker();
1474        if let Some(tube) = &self.command_tube {
1475            if let Err(e) = tube.send(&BalloonTubeResult::NotReady) {
1476                error!("failed to send balloon not ready result: {}", e);
1477            }
1478        }
1479        Ok(())
1480    }
1481
1482    fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
1483        match self.stop_worker() {
1484            StoppedWorker::WithQueues(paused_queues) => Ok(Some(paused_queues.into())),
1485            StoppedWorker::MissingQueues => {
1486                anyhow::bail!("balloon queue workers did not stop cleanly.")
1487            }
1488            StoppedWorker::AlreadyStopped => {
1489                // Device hasn't been activated.
1490                Ok(None)
1491            }
1492        }
1493    }
1494
1495    fn virtio_wake(
1496        &mut self,
1497        queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
1498    ) -> anyhow::Result<()> {
1499        if let Some((mem, interrupt, queues)) = queues_state {
1500            if queues.len() < 2 {
1501                anyhow::bail!("{} queues were found, but an activated balloon must have at least 2 active queues.", queues.len());
1502            }
1503
1504            let balloon_queues = self.get_queues_from_map(queues)?;
1505            self.start_worker(mem, interrupt, balloon_queues)?;
1506        }
1507        Ok(())
1508    }
1509
1510    fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
1511        let state = self
1512            .state
1513            .lock()
1514            .now_or_never()
1515            .context("failed to acquire balloon lock")?;
1516        AnySnapshot::to_any(BalloonSnapshot {
1517            features: self.features,
1518            acked_features: self.acked_features,
1519            state: state.clone(),
1520            ws_num_bins: self.ws_num_bins,
1521        })
1522        .context("failed to serialize balloon state")
1523    }
1524
1525    fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
1526        let snap: BalloonSnapshot = AnySnapshot::from_any(data).context("error deserializing")?;
1527        if snap.features != self.features {
1528            anyhow::bail!(
1529                "balloon: expected features to match, but they did not. Live: {:?}, snapshot {:?}",
1530                self.features,
1531                snap.features,
1532            );
1533        }
1534
1535        let mut state = self
1536            .state
1537            .lock()
1538            .now_or_never()
1539            .context("failed to acquire balloon lock")?;
1540        *state = snap.state;
1541        self.ws_num_bins = snap.ws_num_bins;
1542        self.acked_features = snap.acked_features;
1543        Ok(())
1544    }
1545}
1546
1547#[cfg(test)]
1548mod tests {
1549    use super::*;
1550    use crate::suspendable_virtio_tests;
1551    use crate::virtio::descriptor_utils::create_descriptor_chain;
1552    use crate::virtio::descriptor_utils::DescriptorType;
1553
1554    #[test]
1555    fn desc_parsing_inflate() {
1556        // Check that the memory addresses are parsed correctly by 'handle_address_chain' and passed
1557        // to the closure.
1558        let memory_start_addr = GuestAddress(0x0);
1559        let memory = GuestMemory::new(&[(memory_start_addr, 0x10000)]).unwrap();
1560        memory
1561            .write_obj_at_addr(0x10u32, GuestAddress(0x100))
1562            .unwrap();
1563        memory
1564            .write_obj_at_addr(0xaa55aa55u32, GuestAddress(0x104))
1565            .unwrap();
1566
1567        let mut chain = create_descriptor_chain(
1568            &memory,
1569            GuestAddress(0x0),
1570            GuestAddress(0x100),
1571            vec![(DescriptorType::Readable, 8)],
1572            0,
1573        )
1574        .expect("create_descriptor_chain failed");
1575
1576        let mut addrs = Vec::new();
1577        let res = handle_address_chain(None, &mut chain, &mut |mut ranges| {
1578            addrs.append(&mut ranges)
1579        });
1580        assert!(res.is_ok());
1581        assert_eq!(addrs.len(), 2);
1582        assert_eq!(
1583            addrs[0].0,
1584            GuestAddress(0x10u64 << VIRTIO_BALLOON_PFN_SHIFT)
1585        );
1586        assert_eq!(
1587            addrs[1].0,
1588            GuestAddress(0xaa55aa55u64 << VIRTIO_BALLOON_PFN_SHIFT)
1589        );
1590    }
1591
1592    struct BalloonContext {
1593        _ctrl_tube: Tube,
1594        _mem_client_tube: Tube,
1595    }
1596
1597    fn modify_device(_balloon_context: &mut BalloonContext, balloon: &mut Balloon) {
1598        balloon.ws_num_bins = !balloon.ws_num_bins;
1599    }
1600
1601    fn create_device() -> (BalloonContext, Balloon) {
1602        let (_ctrl_tube, ctrl_tube_device) = Tube::pair().unwrap();
1603        let (_mem_client_tube, mem_client_tube_device) = Tube::pair().unwrap();
1604        (
1605            BalloonContext {
1606                _ctrl_tube,
1607                _mem_client_tube,
1608            },
1609            Balloon::new(
1610                0,
1611                ctrl_tube_device,
1612                VmMemoryClient::new(mem_client_tube_device),
1613                None,
1614                1024,
1615                0,
1616                #[cfg(feature = "registered_events")]
1617                None,
1618                0,
1619            )
1620            .unwrap(),
1621        )
1622    }
1623
1624    suspendable_virtio_tests!(balloon, create_device, 2, modify_device);
1625}