@@ -9,19 +9,23 @@ use std::collections::HashMap;
99use std:: fs:: OpenOptions ;
1010use std:: io:: Write ;
1111use std:: path:: Path ;
12+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
1213use std:: sync:: { Arc , Mutex } ;
1314
1415#[ cfg( target_arch = "x86_64" ) ]
1516use kvm_bindings:: KVM_IRQCHIP_IOAPIC ;
1617use kvm_bindings:: {
17- KVM_IRQ_ROUTING_IRQCHIP , KVM_MEM_LOG_DIRTY_PAGES , kvm_irq_routing_entry ,
18- kvm_userspace_memory_region,
18+ KVM_IRQ_ROUTING_IRQCHIP , KVM_IRQ_ROUTING_MSI , KVM_MEM_LOG_DIRTY_PAGES , KVM_MSI_VALID_DEVID ,
19+ KvmIrqRouting , kvm_irq_routing_entry , kvm_userspace_memory_region,
1920} ;
2021use kvm_ioctls:: VmFd ;
22+ use log:: debug;
23+ use vm_device:: interrupt:: { InterruptSourceGroup , MsiIrqSourceConfig } ;
2124use vmm_sys_util:: errno;
2225use vmm_sys_util:: eventfd:: EventFd ;
2326
2427pub use crate :: arch:: { ArchVm as Vm , ArchVmError , VmState } ;
28+ use crate :: device_manager:: resources:: ResourceAllocator ;
2529use crate :: logger:: info;
2630use crate :: persist:: CreateSnapshotError ;
2731use crate :: utils:: u64_to_usize;
@@ -52,6 +56,148 @@ pub struct RoutingEntry {
5256 masked : bool ,
5357}
5458
59+ /// Type that describes an allocated interrupt
60+ #[ derive( Debug ) ]
61+ pub struct MsiVector {
62+ /// GSI used for this vector
63+ pub gsi : u32 ,
64+ /// EventFd used for this vector
65+ pub event_fd : EventFd ,
66+ /// Flag determining whether the vector is enabled
67+ pub enabled : AtomicBool ,
68+ }
69+
70+ impl MsiVector {
71+ /// Create a new [`MsiVector`] of a particular type
72+ pub fn new ( gsi : u32 , enabled : bool ) -> Result < MsiVector , InterruptError > {
73+ Ok ( MsiVector {
74+ gsi,
75+ event_fd : EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( InterruptError :: EventFd ) ?,
76+ enabled : AtomicBool :: new ( enabled) ,
77+ } )
78+ }
79+ }
80+
81+ impl MsiVector {
82+ /// Enable vector
83+ fn enable ( & self , vmfd : & VmFd ) -> Result < ( ) , errno:: Error > {
84+ if !self . enabled . load ( Ordering :: Acquire ) {
85+ vmfd. register_irqfd ( & self . event_fd , self . gsi ) ?;
86+ self . enabled . store ( true , Ordering :: Release ) ;
87+ }
88+
89+ Ok ( ( ) )
90+ }
91+
92+ /// Disable vector
93+ fn disable ( & self , vmfd : & VmFd ) -> Result < ( ) , errno:: Error > {
94+ if self . enabled . load ( Ordering :: Acquire ) {
95+ vmfd. unregister_irqfd ( & self . event_fd , self . gsi ) ?;
96+ self . enabled . store ( false , Ordering :: Release ) ;
97+ }
98+
99+ Ok ( ( ) )
100+ }
101+ }
102+
103+ #[ derive( Debug ) ]
104+ /// MSI interrupts created for a VirtIO device
105+ pub struct MsiVectorGroup {
106+ vm : Arc < Vm > ,
107+ irq_routes : HashMap < u32 , MsiVector > ,
108+ }
109+
110+ impl MsiVectorGroup {
111+ /// Returns the number of vectors in this group
112+ pub fn num_vectors ( & self ) -> u16 {
113+ u16:: try_from ( self . irq_routes . len ( ) ) . unwrap ( )
114+ }
115+ }
116+
117+ impl InterruptSourceGroup for MsiVectorGroup {
118+ fn enable ( & self ) -> vm_device:: interrupt:: Result < ( ) > {
119+ for ( _, route) in self . irq_routes . iter ( ) {
120+ route. enable ( & self . vm . common . fd ) ?;
121+ }
122+
123+ Ok ( ( ) )
124+ }
125+
126+ fn disable ( & self ) -> vm_device:: interrupt:: Result < ( ) > {
127+ for ( _, route) in self . irq_routes . iter ( ) {
128+ route. disable ( & self . vm . common . fd ) ?;
129+ }
130+
131+ Ok ( ( ) )
132+ }
133+
134+ fn trigger (
135+ & self ,
136+ index : vm_device:: interrupt:: InterruptIndex ,
137+ ) -> vm_device:: interrupt:: Result < ( ) > {
138+ if let Some ( route) = self . irq_routes . get ( & index) {
139+ return route. event_fd . write ( 1 ) ;
140+ }
141+
142+ Err ( std:: io:: Error :: other ( format ! (
143+ "trigger: invalid interrupt index {index}"
144+ ) ) )
145+ }
146+
147+ fn notifier ( & self , index : vm_device:: interrupt:: InterruptIndex ) -> Option < & EventFd > {
148+ self . irq_routes . get ( & index) . map ( |route| & route. event_fd )
149+ }
150+
151+ fn update (
152+ & self ,
153+ index : vm_device:: interrupt:: InterruptIndex ,
154+ config : vm_device:: interrupt:: InterruptSourceConfig ,
155+ masked : bool ,
156+ set_gsi : bool ,
157+ ) -> vm_device:: interrupt:: Result < ( ) > {
158+ let msi_config = match config {
159+ vm_device:: interrupt:: InterruptSourceConfig :: LegacyIrq ( _) => {
160+ return Err ( std:: io:: Error :: other (
161+ "MSI-x update: invalid configuration type" ,
162+ ) ) ;
163+ }
164+ vm_device:: interrupt:: InterruptSourceConfig :: MsiIrq ( config) => config,
165+ } ;
166+
167+ if let Some ( route) = self . irq_routes . get ( & index) {
168+ // When an interrupt is masked the GSI will not be passed to KVM through
169+ // KVM_SET_GSI_ROUTING. So, call [`disable()`] to unregister the interrupt file
170+ // descriptor before passing the interrupt routes to KVM
171+ if masked {
172+ route. disable ( & self . vm . common . fd ) ?;
173+ }
174+
175+ self . vm . register_msi ( route, masked, msi_config) ?;
176+ if set_gsi {
177+ self . vm . set_gsi_routes ( ) . unwrap ( ) ;
178+ }
179+
180+ // Assign KVM_IRQFD after KVM_SET_GSI_ROUTING to avoid
181+ // panic on kernel which does not have commit a80ced6ea514
182+ // (KVM: SVM: fix panic on out-of-bounds guest IRQ).
183+ if !masked {
184+ route. enable ( & self . vm . common . fd ) ?;
185+ }
186+
187+ return Ok ( ( ) ) ;
188+ }
189+
190+ Err ( std:: io:: Error :: other ( format ! (
191+ "MSI-X update: invalid vector index {index}"
192+ ) ) )
193+ }
194+
195+ fn set_gsi ( & self ) -> vm_device:: interrupt:: Result < ( ) > {
196+ self . vm . set_gsi_routes ( ) . unwrap ( ) ;
197+ Ok ( ( ) )
198+ }
199+ }
200+
55201/// Architecture independent parts of a VM.
56202#[ derive( Debug ) ]
57203pub struct VmCommon {
@@ -323,7 +469,6 @@ impl Vm {
323469 {
324470 entry. u . irqchip . irqchip = 0 ;
325471 }
326-
327472 entry. u . irqchip . pin = gsi;
328473
329474 self . common
@@ -339,6 +484,92 @@ impl Vm {
339484 ) ;
340485 Ok ( ( ) )
341486 }
487+
488+ /// Register an MSI device interrupt
489+ pub fn register_msi (
490+ & self ,
491+ route : & MsiVector ,
492+ masked : bool ,
493+ config : MsiIrqSourceConfig ,
494+ ) -> Result < ( ) , errno:: Error > {
495+ let mut entry = kvm_irq_routing_entry {
496+ gsi : route. gsi ,
497+ type_ : KVM_IRQ_ROUTING_MSI ,
498+ ..Default :: default ( )
499+ } ;
500+ entry. u . msi . address_lo = config. low_addr ;
501+ entry. u . msi . address_hi = config. high_addr ;
502+ entry. u . msi . data = config. data ;
503+
504+ if self . common . fd . check_extension ( kvm_ioctls:: Cap :: MsiDevid ) {
505+ // On AArch64, there is limitation on the range of the 'devid',
506+ // it cannot be greater than 65536 (the max of u16).
507+ //
508+ // BDF cannot be used directly, because 'segment' is in high
509+ // 16 bits. The layout of the u32 BDF is:
510+ // |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
511+ // | segment | bus | device | function |
512+ //
513+ // Now that we support 1 bus only in a segment, we can build a
514+ // 'devid' by replacing the 'bus' bits with the low 8 bits of
515+ // 'segment' data.
516+ // This way we can resolve the range checking problem and give
517+ // different `devid` to all the devices. Limitation is that at
518+ // most 256 segments can be supported.
519+ //
520+ let modified_devid = ( ( config. devid & 0x00ff_0000 ) >> 8 ) | config. devid & 0xff ;
521+
522+ entry. flags = KVM_MSI_VALID_DEVID ;
523+ entry. u . msi . __bindgen_anon_1 . devid = modified_devid;
524+ }
525+
526+ self . common
527+ . interrupts
528+ . lock ( )
529+ . expect ( "Poisoned lock" )
530+ . insert ( route. gsi , RoutingEntry { entry, masked } ) ;
531+
532+ Ok ( ( ) )
533+ }
534+
535+ /// Create a group of MSI-X interrupts
536+ pub fn create_msix_group (
537+ vm : Arc < Vm > ,
538+ resource_allocator : & ResourceAllocator ,
539+ base : u32 ,
540+ count : u16 ,
541+ ) -> Result < MsiVectorGroup , InterruptError > {
542+ debug ! ( "Creating new MSI group with {count} vectors" ) ;
543+ let mut irq_routes = HashMap :: with_capacity ( count as usize ) ;
544+ for ( i, gsi) in resource_allocator
545+ . allocate_gsi ( count as u32 ) ?
546+ . iter ( )
547+ . enumerate ( )
548+ {
549+ irq_routes. insert (
550+ u32:: try_from ( i) . unwrap ( ) + base,
551+ MsiVector :: new ( * gsi, false ) ?,
552+ ) ;
553+ }
554+
555+ Ok ( MsiVectorGroup { vm, irq_routes } )
556+ }
557+
558+ /// Set GSI routes to KVM
559+ pub fn set_gsi_routes ( & self ) -> Result < ( ) , InterruptError > {
560+ let entries = self . common . interrupts . lock ( ) . expect ( "Poisoned lock" ) ;
561+ let mut routes = KvmIrqRouting :: new ( 0 ) ?;
562+
563+ for ( _, entry) in entries. iter ( ) {
564+ if entry. masked {
565+ continue ;
566+ }
567+ routes. push ( entry. entry ) ?;
568+ }
569+
570+ self . common . fd . set_gsi_routing ( & routes) ?;
571+ Ok ( ( ) )
572+ }
342573}
343574
344575#[ cfg( test) ]
0 commit comments