vmm/devices/virtio/balloon/
event_handler.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use event_manager::{EventOps, Events, MutEventSubscriber};
5use vmm_sys_util::epoll::EventSet;
6
7use super::{DEFLATE_INDEX, INFLATE_INDEX, STATS_INDEX, report_balloon_event_fail};
8use crate::devices::virtio::balloon::device::Balloon;
9use crate::devices::virtio::device::VirtioDevice;
10use crate::logger::{error, warn};
11
12impl Balloon {
13    const PROCESS_ACTIVATE: u32 = 0;
14    const PROCESS_VIRTQ_INFLATE: u32 = 1;
15    const PROCESS_VIRTQ_DEFLATE: u32 = 2;
16    const PROCESS_VIRTQ_STATS: u32 = 3;
17    const PROCESS_STATS_TIMER: u32 = 4;
18    const PROCESS_VIRTQ_FREE_PAGE_HINTING: u32 = 5;
19    const PROCESS_VIRTQ_FREE_PAGE_REPORTING: u32 = 6;
20
21    fn register_runtime_events(&self, ops: &mut EventOps) {
22        if let Err(err) = ops.add(Events::with_data(
23            &self.queue_evts[INFLATE_INDEX],
24            Self::PROCESS_VIRTQ_INFLATE,
25            EventSet::IN,
26        )) {
27            error!("Failed to register inflate queue event: {}", err);
28        }
29        if let Err(err) = ops.add(Events::with_data(
30            &self.queue_evts[DEFLATE_INDEX],
31            Self::PROCESS_VIRTQ_DEFLATE,
32            EventSet::IN,
33        )) {
34            error!("Failed to register deflate queue event: {}", err);
35        }
36        if self.stats_enabled() {
37            if let Err(err) = ops.add(Events::with_data(
38                &self.queue_evts[STATS_INDEX],
39                Self::PROCESS_VIRTQ_STATS,
40                EventSet::IN,
41            )) {
42                error!("Failed to register stats queue event: {}", err);
43            }
44            if let Err(err) = ops.add(Events::with_data(
45                &self.stats_timer,
46                Self::PROCESS_STATS_TIMER,
47                EventSet::IN,
48            )) {
49                error!("Failed to register stats timerfd event: {}", err);
50            }
51        }
52
53        if self.free_page_hinting()
54            && let Err(err) = ops.add(Events::with_data(
55                &self.queue_evts[self.free_page_hinting_idx()],
56                Self::PROCESS_VIRTQ_FREE_PAGE_HINTING,
57                EventSet::IN,
58            ))
59        {
60            error!("Failed to register free page hinting queue event: {}", err);
61        }
62
63        if self.free_page_reporting()
64            && let Err(err) = ops.add(Events::with_data(
65                &self.queue_evts[self.free_page_reporting_idx()],
66                Self::PROCESS_VIRTQ_FREE_PAGE_REPORTING,
67                EventSet::IN,
68            ))
69        {
70            error!(
71                "Failed to register free page reporting queue event: {}",
72                err
73            );
74        }
75    }
76
77    fn register_activate_event(&self, ops: &mut EventOps) {
78        if let Err(err) = ops.add(Events::with_data(
79            &self.activate_evt,
80            Self::PROCESS_ACTIVATE,
81            EventSet::IN,
82        )) {
83            error!("Failed to register activate event: {}", err);
84        }
85    }
86
87    fn process_activate_event(&self, ops: &mut EventOps) {
88        if let Err(err) = self.activate_evt.read() {
89            error!("Failed to consume balloon activate event: {:?}", err);
90        }
91        self.register_runtime_events(ops);
92        if let Err(err) = ops.remove(Events::with_data(
93            &self.activate_evt,
94            Self::PROCESS_ACTIVATE,
95            EventSet::IN,
96        )) {
97            error!("Failed to un-register activate event: {}", err);
98        }
99    }
100}
101
102impl MutEventSubscriber for Balloon {
103    fn process(&mut self, event: Events, ops: &mut EventOps) {
104        let source = event.data();
105        let event_set = event.event_set();
106        let supported_events = EventSet::IN;
107
108        if !supported_events.contains(event_set) {
109            warn!(
110                "Received unknown event: {:?} from source: {:?}",
111                event_set, source
112            );
113            return;
114        }
115
116        if self.is_activated() {
117            match source {
118                Self::PROCESS_ACTIVATE => self.process_activate_event(ops),
119                Self::PROCESS_VIRTQ_INFLATE => self
120                    .process_inflate_queue_event()
121                    .unwrap_or_else(report_balloon_event_fail),
122                Self::PROCESS_VIRTQ_DEFLATE => self
123                    .process_deflate_queue_event()
124                    .unwrap_or_else(report_balloon_event_fail),
125                Self::PROCESS_VIRTQ_STATS => self
126                    .process_stats_queue_event()
127                    .unwrap_or_else(report_balloon_event_fail),
128                Self::PROCESS_STATS_TIMER => self
129                    .process_stats_timer_event()
130                    .unwrap_or_else(report_balloon_event_fail),
131                Self::PROCESS_VIRTQ_FREE_PAGE_HINTING => self
132                    .process_free_page_hinting_queue_event()
133                    .unwrap_or_else(report_balloon_event_fail),
134                Self::PROCESS_VIRTQ_FREE_PAGE_REPORTING => self
135                    .process_free_page_reporting_queue_event()
136                    .unwrap_or_else(report_balloon_event_fail),
137                _ => {
138                    warn!("Balloon: Spurious event received: {:?}", source);
139                }
140            };
141        } else {
142            warn!(
143                "Balloon: The device is not yet activated. Spurious event received: {:?}",
144                source
145            );
146        }
147    }
148
149    fn init(&mut self, ops: &mut EventOps) {
150        // This function can be called during different points in the device lifetime:
151        //  - shortly after device creation,
152        //  - on device activation (is-activated already true at this point),
153        //  - on device restore from snapshot.
154        if self.is_activated() {
155            self.register_runtime_events(ops);
156        } else {
157            self.register_activate_event(ops);
158        }
159    }
160}
161
162#[cfg(test)]
163pub mod tests {
164    use std::sync::{Arc, Mutex};
165
166    use event_manager::{EventManager, SubscriberOps};
167
168    use super::*;
169    use crate::devices::virtio::balloon::test_utils::set_request;
170    use crate::devices::virtio::test_utils::{VirtQueue, default_interrupt, default_mem};
171    use crate::vstate::memory::GuestAddress;
172
173    #[test]
174    fn test_event_handler() {
175        let mut event_manager = EventManager::new().unwrap();
176        let mut balloon = Balloon::new(0, true, 10, false, false).unwrap();
177        let mem = default_mem();
178        let interrupt = default_interrupt();
179        let infq = VirtQueue::new(GuestAddress(0), &mem, 16);
180        balloon.set_queue(INFLATE_INDEX, infq.create_queue());
181        balloon.set_queue(DEFLATE_INDEX, infq.create_queue());
182        balloon.set_queue(STATS_INDEX, infq.create_queue());
183
184        let balloon = Arc::new(Mutex::new(balloon));
185        let _id = event_manager.add_subscriber(balloon.clone());
186
187        // Push a queue event, use the inflate queue in this test.
188        {
189            let addr = 0x100;
190            set_request(&infq, 0, addr, 4, 0);
191            balloon.lock().unwrap().queue_evts[INFLATE_INDEX]
192                .write(1)
193                .unwrap();
194        }
195
196        // EventManager should report no events since balloon has only registered
197        // its activation event so far (even though there is also a queue event pending).
198        let ev_count = event_manager.run_with_timeout(50).unwrap();
199        assert_eq!(ev_count, 0);
200
201        // Manually force a queue event and check it's ignored pre-activation.
202        {
203            let b = balloon.lock().unwrap();
204            // Artificially push event.
205            b.queue_evts[INFLATE_INDEX].write(1).unwrap();
206            // Process the pushed event.
207            let ev_count = event_manager.run_with_timeout(50).unwrap();
208            // Validate there was no queue operation.
209            assert_eq!(ev_count, 0);
210            assert_eq!(infq.used.idx.get(), 0);
211        }
212
213        // Now activate the device.
214        balloon
215            .lock()
216            .unwrap()
217            .activate(mem.clone(), interrupt)
218            .unwrap();
219        // Process the activate event.
220        let ev_count = event_manager.run_with_timeout(50).unwrap();
221        assert_eq!(ev_count, 1);
222
223        // Handle the previously pushed queue event through EventManager.
224        event_manager
225            .run_with_timeout(100)
226            .expect("Metrics event timeout or error.");
227        // Make sure the data queue advanced.
228        assert_eq!(infq.used.idx.get(), 1);
229    }
230}