@@ -21,10 +21,47 @@ use std::time::Duration;
2121
2222use serde:: { Deserialize , Serialize } ;
2323use userfaultfd:: { Error , Event , Uffd } ;
24+ use vmm_sys_util:: ioctl:: ioctl_with_mut_ref;
2425use vmm_sys_util:: sock_ctrl_msg:: ScmSocket ;
26+ use vmm_sys_util:: { ioctl_ioc_nr, ioctl_iowr_nr} ;
2527
2628use crate :: uffd_utils:: userfault_bitmap:: UserfaultBitmap ;
2729
30+ // TODO: remove when UFFDIO_CONTINUE for guest_memfd is available in the crate
31+ #[ repr( C ) ]
32+ struct uffdio_continue {
33+ range : uffdio_range ,
34+ mode : u64 ,
35+ mapped : u64 ,
36+ }
37+
38+ ioctl_iowr_nr ! ( UFFDIO_CONTINUE , 0xAA , 0x7 , uffdio_continue) ;
39+
40+ #[ repr( C ) ]
41+ struct uffdio_range {
42+ start : u64 ,
43+ len : u64 ,
44+ }
45+
46+ pub fn uffd_continue ( uffd : RawFd , fault_addr : u64 , len : u64 ) -> std:: io:: Result < ( ) > {
47+ let mut cont = uffdio_continue {
48+ range : uffdio_range {
49+ start : fault_addr,
50+ len,
51+ } ,
52+ mode : 0 , // Normal continuation mode
53+ mapped : 0 ,
54+ } ;
55+
56+ let ret = unsafe { ioctl_with_mut_ref ( & uffd, UFFDIO_CONTINUE ( ) , & mut cont) } ;
57+
58+ if ret == -1 {
59+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
60+ }
61+
62+ Ok ( ( ) )
63+ }
64+
2865// This is the same with the one used in src/vmm.
2966/// This describes the mapping between Firecracker base virtual address and offset in the
3067/// buffer or file backend for a guest memory region. It is used to tell an external
@@ -119,7 +156,7 @@ pub struct UffdHandler {
119156 pub mem_regions : Vec < GuestRegionUffdMapping > ,
120157 pub page_size : usize ,
121158 backing_buffer : * const u8 ,
122- uffd : Uffd ,
159+ pub uffd : Uffd ,
123160 removed_pages : HashSet < u64 > ,
124161 pub guest_memfd : Option < File > ,
125162 pub guest_memfd_addr : Option < * mut u8 > ,
@@ -263,6 +300,20 @@ impl UffdHandler {
263300 }
264301 }
265302
303+ pub fn addr_to_offset ( & self , addr : * mut u8 ) -> u64 {
304+ let addr = addr as u64 ;
305+ for region in & self . mem_regions {
306+ if region. contains ( addr) {
307+ return addr - region. base_host_virt_addr + region. offset as u64 ;
308+ }
309+ }
310+
311+ panic ! (
312+ "Could not find addr: {:#x} within guest region mappings." ,
313+ addr
314+ ) ;
315+ }
316+
266317 pub fn serve_pf ( & mut self , addr : * mut u8 , len : usize ) -> bool {
267318 // Find the start of the page that the current faulting address belongs to.
268319 let dst = ( addr as usize & !( self . page_size - 1 ) ) as * mut libc:: c_void ;
@@ -275,7 +326,7 @@ impl UffdHandler {
275326 } else {
276327 for region in self . mem_regions . iter ( ) {
277328 if region. contains ( fault_page_addr) {
278- return self . populate_from_file ( region, fault_page_addr, len) ;
329+ return self . populate_from_file ( & region. clone ( ) , fault_page_addr, len) ;
279330 }
280331 }
281332 }
@@ -290,12 +341,61 @@ impl UffdHandler {
290341 self . mem_regions . iter ( ) . map ( |r| r. size ) . sum ( )
291342 }
292343
293- fn populate_from_file ( & self , region : & GuestRegionUffdMapping , dst : u64 , len : usize ) -> bool {
294- let offset = dst - region. base_host_virt_addr ;
295- let src = self . backing_buffer as u64 + region. offset + offset;
344+ pub fn populate_via_write ( & mut self , offset : usize , len : usize ) -> usize {
345+ // man 2 write:
346+ //
347+ // On Linux, write() (and similar system calls) will transfer at most
348+ // 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes
349+ // actually transferred. (This is true on both 32-bit and 64-bit
350+ // systems.)
351+ const MAX_WRITE_LEN : usize = 2_147_479_552 ;
352+
353+ assert ! (
354+ offset. checked_add( len) . unwrap( ) <= self . size( ) ,
355+ "{} + {} >= {}" ,
356+ offset,
357+ len,
358+ self . size( )
359+ ) ;
360+
361+ let mut total_written = 0 ;
362+
363+ while total_written < len {
364+ let src = unsafe { self . backing_buffer . add ( offset + total_written) } ;
365+ let len_to_write = ( len - total_written) . min ( MAX_WRITE_LEN ) ;
366+ let bytes_written = unsafe {
367+ libc:: pwrite64 (
368+ self . guest_memfd . as_ref ( ) . unwrap ( ) . as_raw_fd ( ) ,
369+ src. cast ( ) ,
370+ len_to_write,
371+ ( offset + total_written) as libc:: off64_t ,
372+ )
373+ } ;
374+
375+ let bytes_written = match bytes_written {
376+ -1 if vmm_sys_util:: errno:: Error :: last ( ) . errno ( ) == libc:: ENOSPC => 0 ,
377+ written @ 0 .. => written as usize ,
378+ _ => panic ! ( "{:?}" , std:: io:: Error :: last_os_error( ) ) ,
379+ } ;
380+
381+ self . userfault_bitmap
382+ . as_mut ( )
383+ . unwrap ( )
384+ . reset_addr_range ( offset + total_written, bytes_written) ;
385+
386+ total_written += bytes_written;
387+
388+ if bytes_written != len_to_write {
389+ break ;
390+ }
391+ }
392+
393+ total_written
394+ }
296395
396+ fn populate_via_uffdio_copy ( & self , src : * const u8 , dst : u64 , len : usize ) -> bool {
297397 unsafe {
298- match self . uffd . copy ( src as * const _ , dst as * mut _ , len, true ) {
398+ match self . uffd . copy ( src. cast ( ) , dst as * mut _ , len, true ) {
299399 // Make sure the UFFD copied some bytes.
300400 Ok ( value) => assert ! ( value > 0 ) ,
301401 // Catch EAGAIN errors, which occur when a `remove` event lands in the UFFD
@@ -320,6 +420,42 @@ impl UffdHandler {
320420 true
321421 }
322422
423+ fn populate_via_memcpy ( & mut self , src : * const u8 , dst : u64 , offset : usize , len : usize ) -> bool {
424+ let dst_memcpy = unsafe {
425+ self . guest_memfd_addr
426+ . expect ( "no guest_memfd addr" )
427+ . add ( offset)
428+ } ;
429+
430+ unsafe {
431+ std:: ptr:: copy_nonoverlapping ( src, dst_memcpy, len) ;
432+ }
433+
434+ self . userfault_bitmap
435+ . as_mut ( )
436+ . unwrap ( )
437+ . reset_addr_range ( offset, len) ;
438+
439+ uffd_continue ( self . uffd . as_raw_fd ( ) , dst, len as u64 ) . expect ( "uffd_continue" ) ;
440+
441+ true
442+ }
443+
444+ fn populate_from_file (
445+ & mut self ,
446+ region : & GuestRegionUffdMapping ,
447+ dst : u64 ,
448+ len : usize ,
449+ ) -> bool {
450+ let offset = ( region. offset + dst - region. base_host_virt_addr ) as usize ;
451+ let src = unsafe { self . backing_buffer . add ( offset) } ;
452+
453+ match self . guest_memfd {
454+ Some ( _) => self . populate_via_memcpy ( src, dst, offset, len) ,
455+ None => self . populate_via_uffdio_copy ( src, dst, len) ,
456+ }
457+ }
458+
323459 fn zero_out ( & mut self , addr : u64 ) {
324460 let ret = unsafe {
325461 self . uffd
@@ -666,7 +802,7 @@ mod tests {
666802 let ( stream, _) = listener. accept ( ) . expect ( "Cannot listen on UDS socket" ) ;
667803 // Update runtime with actual runtime
668804 let runtime = uninit_runtime. write ( Runtime :: new ( stream, file) ) ;
669- runtime. run ( |_: & mut UffdHandler | { } ) ;
805+ runtime. run ( |_: & mut UffdHandler | { } , |_ : & mut UffdHandler , _ : usize | { } ) ;
670806 } ) ;
671807
672808 // wait for runtime thread to initialize itself
0 commit comments