dbs_interrupt/
notifier.rs

1// Copyright 2019 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
3
4//! Event notifier to inject device interrupts to virtual machines.
5
6use std::any::Any;
7use std::io::Error;
8use std::sync::Arc;
9
10use vmm_sys_util::eventfd::EventFd;
11
12use crate::{InterruptIndex, InterruptSourceGroup, InterruptStatusRegister32};
13
14#[cfg(feature = "legacy-irq")]
15pub use self::legacy::*;
16#[cfg(feature = "msi-irq")]
17pub use self::msi::*;
18
19/// Trait to inject device interrupts to virtual machines.
20pub trait InterruptNotifier: Send + Sync {
21    /// Inject a device interrupt to the virtual machine.
22    fn notify(&self) -> Result<(), Error>;
23
24    /// Get the optional `EventFd` object to inject interrupt to the virtual machine.
25    fn notifier(&self) -> Option<&EventFd>;
26
27    /// Clone a boxed dyn trait object.
28    fn clone_boxed(&self) -> Box<dyn InterruptNotifier>;
29
30    /// Convert `self` to `std::any::Any`.
31    fn as_any(&self) -> &dyn Any;
32}
33
34#[cfg(feature = "legacy-irq")]
35mod legacy {
36    use super::*;
37
38    /// Struct to inject legacy interrupt to guest.
39    #[derive(Clone)]
40    pub struct LegacyNotifier {
41        pub(crate) intr_group: Arc<Box<dyn InterruptSourceGroup>>,
42        pub(crate) intr_status: Arc<InterruptStatusRegister32>,
43        pub(crate) status_bits: u32,
44    }
45
46    impl LegacyNotifier {
47        /// Create a legacy notifier.
48        pub fn new(
49            intr_group: Arc<Box<dyn InterruptSourceGroup>>,
50            intr_status: Arc<InterruptStatusRegister32>,
51            status_bits: u32,
52        ) -> Self {
53            Self {
54                intr_group,
55                intr_status,
56                status_bits,
57            }
58        }
59    }
60
61    impl InterruptNotifier for LegacyNotifier {
62        fn notify(&self) -> Result<(), Error> {
63            self.intr_status.set_bits(self.status_bits);
64            self.intr_group.trigger(0)
65        }
66
67        fn notifier(&self) -> Option<&EventFd> {
68            self.intr_group.notifier(0)
69        }
70
71        fn clone_boxed(&self) -> Box<dyn InterruptNotifier> {
72            Box::new(self.clone())
73        }
74
75        fn as_any(&self) -> &dyn Any {
76            self
77        }
78    }
79}
80
81#[cfg(feature = "msi-irq")]
82mod msi {
83    use super::*;
84
85    /// Struct to inject message signalled interrupt to guest.
86    #[derive(Clone)]
87    pub struct MsiNotifier {
88        pub(crate) intr_group: Arc<Box<dyn InterruptSourceGroup>>,
89        pub(crate) intr_index: InterruptIndex,
90    }
91
92    impl MsiNotifier {
93        /// Create a notifier to inject message signalled interrupt to guest.
94        pub fn new(
95            intr_group: Arc<Box<dyn InterruptSourceGroup>>,
96            intr_index: InterruptIndex,
97        ) -> Self {
98            MsiNotifier {
99                intr_group,
100                intr_index,
101            }
102        }
103    }
104
105    impl InterruptNotifier for MsiNotifier {
106        fn notify(&self) -> Result<(), Error> {
107            self.intr_group.trigger(self.intr_index)
108        }
109
110        fn notifier(&self) -> Option<&EventFd> {
111            self.intr_group.notifier(self.intr_index)
112        }
113
114        fn clone_boxed(&self) -> Box<dyn InterruptNotifier> {
115            Box::new(self.clone())
116        }
117
118        fn as_any(&self) -> &dyn Any {
119            self
120        }
121    }
122}
123
124/// Struct to discard interrupts.
125#[derive(Copy, Clone, Debug, Default)]
126pub struct NoopNotifier {}
127
128impl NoopNotifier {
129    /// Create a noop notifier to discard interrupts.
130    pub fn new() -> Self {
131        NoopNotifier {}
132    }
133}
134
135impl InterruptNotifier for NoopNotifier {
136    fn notify(&self) -> Result<(), Error> {
137        Ok(())
138    }
139
140    fn notifier(&self) -> Option<&EventFd> {
141        None
142    }
143
144    fn clone_boxed(&self) -> Box<dyn InterruptNotifier> {
145        Box::new(*self)
146    }
147
148    fn as_any(&self) -> &dyn Any {
149        self
150    }
151}
152
153/// Clone a boxed interrupt notifier object.
154pub fn clone_notifier(notifier: &dyn InterruptNotifier) -> Box<dyn InterruptNotifier> {
155    notifier.clone_boxed()
156}
157
158#[cfg(test)]
159mod tests {
160    #![allow(unused_imports)]
161    #![allow(dead_code)]
162    use super::*;
163
164    use crate::{InterruptManager, InterruptSourceType};
165
166    const VIRTIO_INTR_VRING: u32 = 0x01;
167    const VIRTIO_INTR_CONFIG: u32 = 0x02;
168
169    #[test]
170    fn create_virtio_null_notifier() {
171        let notifier = NoopNotifier::new();
172
173        notifier.notify().unwrap();
174        assert!(notifier.notifier().is_none());
175    }
176
177    #[cfg(feature = "kvm-legacy-irq")]
178    #[test]
179    fn test_create_legacy_notifier() {
180        let (_vmfd, irq_manager) = crate::kvm::tests::create_kvm_irq_manager();
181        let group = irq_manager
182            .create_group(InterruptSourceType::LegacyIrq, 0, 1)
183            .unwrap();
184        let status = Arc::new(InterruptStatusRegister32::new());
185        assert_eq!(status.read(), 0);
186
187        let notifer = LegacyNotifier::new(group.clone(), status.clone(), VIRTIO_INTR_CONFIG);
188        notifer.notify().unwrap();
189        assert!(notifer.notifier().is_some());
190        assert_eq!(notifer.status_bits, VIRTIO_INTR_CONFIG);
191        assert_eq!(status.read_and_clear(), VIRTIO_INTR_CONFIG);
192        assert_eq!(status.read(), 0);
193
194        let notifier = LegacyNotifier::new(group.clone(), status.clone(), VIRTIO_INTR_VRING);
195        notifier.notify().unwrap();
196        assert!(notifier.notifier().is_some());
197        assert_eq!(status.read(), VIRTIO_INTR_VRING);
198        status.clear_bits(VIRTIO_INTR_VRING);
199        assert_eq!(status.read(), 0);
200        let eventfd = notifier.notifier().unwrap();
201        assert_eq!(eventfd.read().unwrap(), 2);
202
203        let clone = clone_notifier(&notifier);
204        assert_eq!(clone.as_any().type_id(), notifier.as_any().type_id());
205    }
206
207    #[cfg(feature = "kvm-msi-irq")]
208    #[test]
209    fn test_virtio_msi_notifier() {
210        let (_vmfd, irq_manager) = crate::kvm::tests::create_kvm_irq_manager();
211        let group = irq_manager
212            .create_group(InterruptSourceType::MsiIrq, 0, 3)
213            .unwrap();
214        let notifier1 = MsiNotifier::new(group.clone(), 1);
215        let notifier2 = MsiNotifier::new(group.clone(), 2);
216        let notifier3 = MsiNotifier::new(group.clone(), 3);
217        assert!(notifier1.notifier().is_some());
218        assert!(notifier2.notifier().is_some());
219        assert!(notifier3.notifier().is_none());
220
221        notifier1.notify().unwrap();
222        notifier1.notify().unwrap();
223        notifier2.notify().unwrap();
224        assert_eq!(notifier1.notifier().unwrap().read().unwrap(), 2);
225        assert_eq!(notifier2.notifier().unwrap().read().unwrap(), 1);
226
227        let clone = clone_notifier(&notifier1);
228        assert_eq!(clone.as_any().type_id(), notifier1.as_any().type_id());
229    }
230}