88use std:: mem:: size_of;
99use std:: os:: unix:: io:: AsRawFd ;
1010use std:: ptr:: { NonNull , null_mut} ;
11+ use std:: sync:: atomic:: { Ordering , fence} ;
1112
1213use kvm_bindings:: {
13- KVM_COALESCED_MMIO_PAGE_OFFSET , kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_run,
14+ KVM_COALESCED_MMIO_PAGE_OFFSET , KVM_DIRTY_GFN_F_DIRTY , KVM_DIRTY_GFN_F_RESET ,
15+ KVM_DIRTY_LOG_PAGE_OFFSET , kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_dirty_gfn, kvm_run,
1416} ;
1517use vmm_sys_util:: errno;
1618
@@ -29,6 +31,110 @@ pub mod vm;
2931/// is otherwise a direct mapping to Result.
3032pub type Result < T > = std:: result:: Result < T , errno:: Error > ;
3133
34+ /// A wrapper around the KVM dirty log ring page.
35+ #[ derive( Debug ) ]
36+ pub ( crate ) struct KvmDirtyLogRing {
37+ /// Next potentially dirty guest frame number slot index
38+ next_dirty : u64 ,
39+ /// Memory-mapped array of dirty guest frame number entries
40+ gfns : NonNull < kvm_dirty_gfn > ,
41+ /// Ring size mask (size-1) for efficient modulo operations
42+ mask : u64 ,
43+ }
44+
45+ impl KvmDirtyLogRing {
46+ /// Maps the KVM dirty log ring from the vCPU file descriptor.
47+ ///
48+ /// # Arguments
49+ /// * `fd` - vCPU file descriptor to mmap from.
50+ /// * `size` - Size of memory region in bytes.
51+ pub ( crate ) fn mmap_from_fd < F : AsRawFd > ( fd : & F , bytes : usize ) -> Result < Self > {
52+ // SAFETY: We trust the sysconf libc function and we're calling it
53+ // with a correct parameter.
54+ let page_size = match unsafe { libc:: sysconf ( libc:: _SC_PAGESIZE) } {
55+ -1 => return Err ( errno:: Error :: last ( ) ) ,
56+ ps => ps as usize ,
57+ } ;
58+
59+ let offset = page_size * KVM_DIRTY_LOG_PAGE_OFFSET as usize ;
60+
61+ if bytes % std:: mem:: size_of :: < kvm_dirty_gfn > ( ) != 0 {
62+ // Size of dirty ring in bytes must be multiples of slot size
63+ return Err ( errno:: Error :: new ( libc:: EINVAL ) ) ;
64+ }
65+ let slots = bytes / std:: mem:: size_of :: < kvm_dirty_gfn > ( ) ;
66+ if !slots. is_power_of_two ( ) {
67+ // Number of slots must be power of two
68+ return Err ( errno:: Error :: new ( libc:: EINVAL ) ) ;
69+ }
70+
71+ // SAFETY: KVM guarantees that there is a page at offset
72+ // KVM_DIRTY_LOG_PAGE_OFFSET * PAGE_SIZE if the appropriate
73+ // capability is available. If it is not, the call will simply
74+ // fail.
75+ let gfns = unsafe {
76+ NonNull :: < kvm_dirty_gfn > :: new ( libc:: mmap (
77+ null_mut ( ) ,
78+ bytes,
79+ libc:: PROT_READ | libc:: PROT_WRITE ,
80+ libc:: MAP_SHARED ,
81+ fd. as_raw_fd ( ) ,
82+ offset as i64 ,
83+ ) as * mut kvm_dirty_gfn )
84+ . filter ( |addr| addr. as_ptr ( ) != libc:: MAP_FAILED as * mut kvm_dirty_gfn )
85+ . ok_or_else ( errno:: Error :: last) ?
86+ } ;
87+ Ok ( Self {
88+ next_dirty : 0 ,
89+ gfns,
90+ mask : ( slots - 1 ) as u64 ,
91+ } )
92+ }
93+ }
94+
95+ impl Drop for KvmDirtyLogRing {
96+ fn drop ( & mut self ) {
97+ // SAFETY: This is safe because we mmap the page ourselves, and nobody
98+ // else is holding a reference to it.
99+ unsafe {
100+ libc:: munmap (
101+ self . gfns . as_ptr ( ) . cast ( ) ,
102+ ( self . mask + 1 ) as usize * std:: mem:: size_of :: < kvm_dirty_gfn > ( ) ,
103+ ) ;
104+ }
105+ }
106+ }
107+
108+ impl Iterator for KvmDirtyLogRing {
109+ type Item = ( u32 , u64 ) ;
110+ fn next ( & mut self ) -> Option < Self :: Item > {
111+ let i = self . next_dirty & self . mask ;
112+ // SAFETY: i is not larger than mask, thus is a valid offset into self.gfns,
113+ // therefore this operation produces a valid pointer to a kvm_dirty_gfn
114+ let gfn_ptr = unsafe { self . gfns . add ( i as usize ) . as_ptr ( ) } ;
115+
116+ fence ( Ordering :: Acquire ) ;
117+
118+ // SAFETY: Can read a valid pointer to a kvm_dirty_gfn
119+ let gfn = unsafe { gfn_ptr. read_volatile ( ) } ;
120+
121+ if gfn. flags & KVM_DIRTY_GFN_F_DIRTY == 0 {
122+ // next_dirty stays the same, it will become the next dirty element
123+ None
124+ } else {
125+ self . next_dirty += 1 ;
126+ let mut updated_gfn = gfn;
127+ updated_gfn. flags ^= KVM_DIRTY_GFN_F_RESET ;
128+ // SAFETY: Can write to a valid pointer to a kvm_dirty_gfn
129+ unsafe {
130+ gfn_ptr. write_volatile ( updated_gfn) ;
131+ } ;
132+ fence ( Ordering :: Release ) ;
133+ Some ( ( gfn. slot , gfn. offset ) )
134+ }
135+ }
136+ }
137+
32138/// A wrapper around the coalesced MMIO ring page.
33139#[ derive( Debug ) ]
34140pub ( crate ) struct KvmCoalescedIoRing {
0 commit comments