66use std:: error;
77use std:: fs:: File ;
88use std:: io;
9- use std:: os:: unix:: io:: AsRawFd ;
9+ use std:: os:: unix:: io:: { AsRawFd , FromRawFd } ;
1010use std:: sync:: Arc ;
1111use std:: thread;
1212
1313use vhost:: vhost_user:: message:: {
14- VhostUserConfigFlags , VhostUserMemoryRegion , VhostUserProtocolFeatures ,
14+ DescStatePacked , DescStateSplit , QueueRegionPacked , QueueRegionSplit , VhostUserConfigFlags ,
15+ VhostUserInflight , VhostUserMemoryRegion , VhostUserProtocolFeatures ,
1516 VhostUserSingleMemoryRegion , VhostUserVirtioFeatures , VhostUserVringAddrFlags ,
1617 VhostUserVringState ,
1718} ;
@@ -23,9 +24,15 @@ use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
2324use vm_memory:: bitmap:: Bitmap ;
2425use vm_memory:: mmap:: NewBitmap ;
2526use vm_memory:: {
26- FileOffset , GuestAddress , GuestAddressSpace , GuestMemoryMmap , GuestRegionMmap , MmapRegion ,
27+ Address , FileOffset , GuestAddress , GuestAddressSpace , GuestMemoryMmap , GuestRegionMmap ,
28+ MmapRegion ,
2729} ;
2830
31+ use libc:: c_void;
32+ use std:: mem;
33+ use std:: os:: unix:: io:: RawFd ;
34+ use virtio_bindings:: bindings:: virtio_net:: VIRTIO_F_RING_PACKED ;
35+
2936use super :: backend:: VhostUserBackend ;
3037use super :: event_loop:: VringEpollHandler ;
3138use super :: event_loop:: { VringEpollError , VringEpollResult } ;
8996 atomic_mem : GM < B > ,
9097 vrings : Vec < V > ,
9198 worker_threads : Vec < thread:: JoinHandle < VringEpollResult < ( ) > > > ,
99+ inflight_file : Option < File > ,
100+ inflight_mapping_addr : Option < GuestAddress > ,
101+ inflight_mmap_size : usize ,
92102}
93103
94104impl < S , V , B > VhostUserHandler < S , V , B >
@@ -146,6 +156,9 @@ where
146156 atomic_mem,
147157 vrings,
148158 worker_threads,
159+ inflight_file : None ,
160+ inflight_mapping_addr : None ,
161+ inflight_mmap_size : 0 ,
149162 } )
150163 }
151164}
@@ -535,20 +548,193 @@ where
535548
536549 fn get_inflight_fd (
537550 & mut self ,
538- _inflight : & vhost:: vhost_user:: message:: VhostUserInflight ,
551+ inflight : & vhost:: vhost_user:: message:: VhostUserInflight ,
539552 ) -> VhostUserResult < ( vhost:: vhost_user:: message:: VhostUserInflight , File ) > {
540- // Assume the backend hasn't negotiated the inflight feature; it
541- // wouldn't be correct for the backend to do so, as we don't (yet)
542- // provide a way for it to handle such requests.
543- Err ( VhostUserError :: InvalidOperation )
553+ let ret_val = -1 ;
554+ // Total size of the inflight queue region
555+ let mut total_mmap_size =
556+ self . get_inflight_queue_size ( inflight. queue_size ) * inflight. num_queues as usize ;
557+
558+ // Create a memfd region to hold the queues for inflight I/O tracking
559+ let mut inflight_fd = -1 ;
560+ let mmap_ptr = self . memfd_alloc ( total_mmap_size, & mut inflight_fd) ?;
561+
562+ self . inflight_file = Some ( unsafe { File :: from_raw_fd ( inflight_fd as i32 ) } ) ;
563+
564+ if mmap_ptr == ret_val as * mut c_void {
565+ total_mmap_size = 0 ;
566+ self . inflight_mapping_addr = None ;
567+ } else {
568+ unsafe { libc:: memset ( mmap_ptr, 0 , total_mmap_size) } ;
569+ self . inflight_mapping_addr = Some ( GuestAddress :: new ( mmap_ptr as u64 ) ) ;
570+ }
571+ self . inflight_mmap_size = total_mmap_size;
572+
573+ Ok ( (
574+ VhostUserInflight {
575+ mmap_size : total_mmap_size as u64 ,
576+ mmap_offset : 0 ,
577+ num_queues : inflight. num_queues ,
578+ queue_size : inflight. queue_size ,
579+ } ,
580+ unsafe { File :: from_raw_fd ( inflight_fd as RawFd ) } ,
581+ ) )
544582 }
545583
546584 fn set_inflight_fd (
547585 & mut self ,
548- _inflight : & vhost:: vhost_user:: message:: VhostUserInflight ,
549- _file : File ,
586+ inflight : & vhost:: vhost_user:: message:: VhostUserInflight ,
587+ file : File ,
550588 ) -> VhostUserResult < ( ) > {
551- Err ( VhostUserError :: InvalidOperation )
589+ let ret_val = -1 ;
590+
591+ // Need to unmap any previously mmaped regions as closing the
592+ // associated file doesn't unmap it automatically
593+ if let Some ( inflight_addr) = self . inflight_mapping_addr {
594+ unsafe {
595+ libc:: munmap (
596+ inflight_addr. raw_value ( ) as * mut c_void ,
597+ self . inflight_mmap_size ,
598+ )
599+ } ;
600+ }
601+
602+ let inflight_fd = file. as_raw_fd ( ) ;
603+
604+ self . inflight_file = Some ( file) ;
605+ let mmap_size = inflight. mmap_size ;
606+ let mmap_offset = inflight. mmap_offset ;
607+
608+ let mmap_ptr = unsafe {
609+ libc:: mmap (
610+ std:: ptr:: null_mut :: < c_void > ( ) ,
611+ mmap_size as usize ,
612+ libc:: PROT_READ | libc:: PROT_WRITE ,
613+ libc:: MAP_SHARED ,
614+ inflight_fd,
615+ mmap_offset as i64 ,
616+ )
617+ } ;
618+
619+ if mmap_ptr == ret_val as * mut c_void {
620+ self . inflight_mapping_addr = None ;
621+ self . inflight_mmap_size = 0 ;
622+ } else {
623+ self . inflight_mapping_addr = Some ( GuestAddress :: new ( mmap_ptr as u64 ) ) ;
624+ self . inflight_mmap_size = mmap_size as usize ;
625+ }
626+
627+ self . set_inflight_region_desc_num ( inflight. num_queues , inflight. queue_size ) ;
628+
629+ Ok ( ( ) )
630+ }
631+ }
632+
633+ impl < S , V , B > VhostUserHandler < S , V , B >
634+ where
635+ S : VhostUserBackend < V , B > ,
636+ V : VringT < GM < B > > ,
637+ B : NewBitmap + Clone ,
638+ {
639+ fn get_inflight_queue_size ( & mut self , queue_size : u16 ) -> usize {
640+ let queue_region_size;
641+ let descr_state_size;
642+ let virtio_features = self . get_features ( ) . unwrap ( ) ;
643+
644+ if virtio_features & ( 1 << VIRTIO_F_RING_PACKED ) == 0 {
645+ // Use descriptor and queue states for split virtqueues
646+ queue_region_size = mem:: size_of :: < QueueRegionSplit > ( ) ;
647+ descr_state_size = mem:: size_of :: < DescStateSplit > ( ) ;
648+ } else {
649+ // Use descriptor and queue states for packed virtqueues
650+ queue_region_size = mem:: size_of :: < QueueRegionPacked > ( ) ;
651+ descr_state_size = mem:: size_of :: < DescStatePacked > ( ) ;
652+ }
653+ queue_region_size + descr_state_size * queue_size as usize
654+ }
655+
656+ fn memfd_alloc (
657+ & self ,
658+ mmap_size : usize ,
659+ inflight_file : & mut i64 ,
660+ ) -> VhostUserResult < * mut c_void > {
661+ let mut ret_val;
662+
663+ ret_val = unsafe {
664+ libc:: syscall (
665+ libc:: SYS_memfd_create ,
666+ & std:: ffi:: CString :: new ( "inflight-region" ) . unwrap ( ) ,
667+ libc:: MFD_ALLOW_SEALING ,
668+ )
669+ } ;
670+
671+ if ret_val == -1 {
672+ return Err ( vhost:: vhost_user:: Error :: MemFdCreateError ) ;
673+ }
674+
675+ * inflight_file = ret_val;
676+
677+ ret_val = unsafe { libc:: ftruncate ( * inflight_file as RawFd , mmap_size as i64 ) } as i64 ;
678+
679+ if ret_val == -1 {
680+ return Err ( vhost:: vhost_user:: Error :: FileTrucateError ) ;
681+ }
682+
683+ ret_val = unsafe {
684+ libc:: fcntl (
685+ * inflight_file as i32 ,
686+ libc:: F_ADD_SEALS ,
687+ libc:: F_SEAL_GROW | libc:: F_SEAL_SHRINK | libc:: F_SEAL_SEAL ,
688+ )
689+ } as i64 ;
690+
691+ if ret_val == -1 {
692+ return Err ( vhost:: vhost_user:: Error :: MemFdSealError ) ;
693+ }
694+
695+ Ok ( unsafe {
696+ libc:: mmap (
697+ std:: ptr:: null_mut ( ) ,
698+ mmap_size,
699+ libc:: PROT_READ | libc:: PROT_WRITE ,
700+ libc:: MAP_SHARED ,
701+ * inflight_file as i32 ,
702+ 0 ,
703+ )
704+ } )
705+ }
706+
707+ fn set_desc_num_packed ( & mut self , inflight_region : u64 , num_queues : u16 , queue_size : u16 ) {
708+ let raw_ptr = inflight_region as * mut QueueRegionPacked ;
709+
710+ for i in 0 ..num_queues {
711+ unsafe {
712+ let queue_region = raw_ptr. offset ( i as isize ) . as_mut ( ) . unwrap ( ) ;
713+ queue_region. desc_num = queue_size;
714+ }
715+ }
716+ }
717+
718+ fn set_desc_num_split ( & mut self , inflight_region : u64 , num_queues : u16 , queue_size : u16 ) {
719+ let raw_ptr = inflight_region as * mut QueueRegionSplit ;
720+
721+ for i in 0 ..num_queues {
722+ unsafe {
723+ let queue_region = raw_ptr. offset ( i as isize ) . as_mut ( ) . unwrap ( ) ;
724+ queue_region. desc_num = queue_size;
725+ }
726+ }
727+ }
728+
729+ fn set_inflight_region_desc_num ( & mut self , num_queues : u16 , queue_size : u16 ) {
730+ let inflight_region = self . inflight_mapping_addr . unwrap ( ) . raw_value ( ) ;
731+
732+ let virtio_features = self . get_features ( ) . unwrap ( ) ;
733+
734+ match virtio_features & ( 1 << VIRTIO_F_RING_PACKED ) {
735+ 0 => self . set_desc_num_split ( inflight_region, num_queues, queue_size) ,
736+ _ => self . set_desc_num_packed ( inflight_region, num_queues, queue_size) ,
737+ } ;
552738 }
553739}
554740
0 commit comments