@@ -24,7 +24,7 @@ use super::{
2424 VIRTIO_BALLOON_S_MEMTOT , VIRTIO_BALLOON_S_MINFLT , VIRTIO_BALLOON_S_SWAP_IN ,
2525 VIRTIO_BALLOON_S_SWAP_OUT ,
2626} ;
27- use crate :: devices:: virtio:: balloon:: BalloonError ;
27+ use crate :: devices:: virtio:: balloon:: { BalloonError , VIRTIO_BALLOON_F_FREE_PAGE_REPORTING } ;
2828use crate :: devices:: virtio:: device:: ActiveState ;
2929use crate :: devices:: virtio:: generated:: virtio_config:: VIRTIO_F_VERSION_1 ;
3030use crate :: devices:: virtio:: generated:: virtio_ids:: VIRTIO_ID_BALLOON ;
@@ -83,6 +83,9 @@ pub struct BalloonConfig {
8383 pub deflate_on_oom : bool ,
8484 /// Interval of time in seconds at which the balloon statistics are updated.
8585 pub stats_polling_interval_s : u16 ,
86+ /// Free page reporting enabled
87+ #[ serde( default ) ]
88+ pub free_page_reporting : bool ,
8689}
8790
8891/// BalloonStats holds statistics returned from the stats_queue.
@@ -189,6 +192,7 @@ impl Balloon {
189192 amount_mib : u32 ,
190193 deflate_on_oom : bool ,
191194 stats_polling_interval_s : u16 ,
195+ free_page_reporting : bool ,
192196 ) -> Result < Balloon , BalloonError > {
193197 let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 ;
194198
@@ -204,16 +208,26 @@ impl Balloon {
204208 EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
205209 EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
206210 EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
211+ EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
207212 ] ;
208213
209214 let mut queues: Vec < Queue > = BALLOON_QUEUE_SIZES . iter ( ) . map ( |& s| Queue :: new ( s) ) . collect ( ) ;
210215
211216 // The VirtIO specification states that the statistics queue should
212217 // not be present at all if the statistics are not enabled.
218+ let mut dropped_queue_count = 0 ;
213219 if stats_polling_interval_s == 0 {
214- let _ = queues. remove ( STATS_INDEX ) ;
220+ dropped_queue_count += 1 ;
221+ }
222+
223+ if free_page_reporting {
224+ avail_features |= 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ;
225+ } else {
226+ dropped_queue_count += 1 ;
215227 }
216228
229+ queues. truncate ( queues. len ( ) - dropped_queue_count) ;
230+
217231 let stats_timer =
218232 TimerFd :: new_custom ( ClockId :: Monotonic , true , true ) . map_err ( BalloonError :: Timer ) ?;
219233
@@ -262,9 +276,16 @@ impl Balloon {
262276 self . trigger_stats_update ( )
263277 }
264278
279+ pub ( crate ) fn process_free_page_reporting_queue_event ( & mut self ) -> Result < ( ) , BalloonError > {
280+ self . queue_evts [ self . free_page_reporting_idx ( ) ]
281+ . read ( )
282+ . map_err ( BalloonError :: EventFd ) ?;
283+ self . process_free_page_reporting_queue ( )
284+ }
285+
265286 pub ( crate ) fn process_inflate ( & mut self ) -> Result < ( ) , BalloonError > {
266287 // This is safe since we checked in the event handler that the device is activated.
267- let mem = & self . device_state . active_state ( ) . unwrap ( ) . mem ;
288+ let mem = & self . device_state . active_state ( ) . ok_or ( BalloonError :: DeviceNotActive ) ? . mem ;
268289 METRICS . inflate_count . inc ( ) ;
269290
270291 let queue = & mut self . queues [ INFLATE_INDEX ] ;
@@ -406,6 +427,41 @@ impl Balloon {
406427 Ok ( ( ) )
407428 }
408429
430+ pub ( crate ) fn process_free_page_reporting_queue (
431+ & mut self ,
432+ ) -> Result < ( ) , BalloonError > {
433+ let mem = & self . device_state . active_state ( ) . unwrap ( ) . mem ;
434+
435+ let idx = self . free_page_reporting_idx ( ) ;
436+ let queue = & mut self . queues [ idx] ;
437+ let mut needs_interrupt = false ;
438+
439+ while let Some ( head) = queue. pop ( ) ? {
440+ let head_index = head. index ;
441+
442+ let mut last_desc = Some ( head) ;
443+ while let Some ( desc) = last_desc {
444+ if let Err ( err) =
445+ mem. discard_range ( desc. addr , desc. len as usize )
446+ {
447+ error ! ( "balloon: failed to remove range: {err:?}" ) ;
448+ }
449+ last_desc = desc. next_descriptor ( ) ;
450+ }
451+
452+ queue. add_used ( head. index , 0 ) ?;
453+ needs_interrupt = true ;
454+ }
455+
456+ queue. advance_used_ring_idx ( ) ;
457+
458+ if needs_interrupt {
459+ self . signal_used_queue ( idx) ?;
460+ }
461+
462+ Ok ( ( ) )
463+ }
464+
409465 pub ( crate ) fn signal_used_queue ( & self , qidx : usize ) -> Result < ( ) , BalloonError > {
410466 self . interrupt_trigger ( )
411467 . trigger ( VirtioInterruptType :: Queue (
@@ -427,6 +483,11 @@ impl Balloon {
427483 return Err ( err) ;
428484 }
429485
486+ if self . free_page_reporting ( ) &&
487+ let Err ( BalloonError :: InvalidAvailIdx ( err) ) = self . process_free_page_reporting_queue ( ) {
488+ return Err ( err) ;
489+ }
490+
430491 Ok ( ( ) )
431492 }
432493
@@ -466,6 +527,20 @@ impl Balloon {
466527 }
467528 }
468529
530+ pub fn free_page_reporting ( & self ) -> bool {
531+ self . avail_features & ( 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ) != 0
532+ }
533+
534+ pub fn free_page_reporting_idx ( & self ) -> usize {
535+ let mut idx = STATS_INDEX ;
536+
537+ if self . stats_polling_interval_s > 0 {
538+ idx += 1 ;
539+ }
540+
541+ idx
542+ }
543+
469544 /// Update the statistics polling interval.
470545 pub fn update_stats_polling_interval ( & mut self , interval_s : u16 ) -> Result < ( ) , BalloonError > {
471546 if self . stats_polling_interval_s == interval_s {
@@ -529,6 +604,7 @@ impl Balloon {
529604 amount_mib : self . size_mb ( ) ,
530605 deflate_on_oom : self . deflate_on_oom ( ) ,
531606 stats_polling_interval_s : self . stats_polling_interval_s ( ) ,
607+ free_page_reporting : self . free_page_reporting ( ) ,
532608 }
533609 }
534610
@@ -737,7 +813,7 @@ pub(crate) mod tests {
737813 // Test all feature combinations.
738814 for deflate_on_oom in [ true , false ] . iter ( ) {
739815 for stats_interval in [ 0 , 1 ] . iter ( ) {
740- let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval) . unwrap ( ) ;
816+ let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval, false ) . unwrap ( ) ;
741817 assert_eq ! ( balloon. device_type( ) , VIRTIO_ID_BALLOON ) ;
742818
743819 let features: u64 = ( 1u64 << VIRTIO_F_VERSION_1 )
@@ -764,12 +840,13 @@ pub(crate) mod tests {
764840
765841 #[ test]
766842 fn test_virtio_read_config ( ) {
767- let balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
843+ let balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
768844
769845 let cfg = BalloonConfig {
770846 amount_mib : 16 ,
771847 deflate_on_oom : true ,
772848 stats_polling_interval_s : 0 ,
849+ free_page_reporting : false ,
773850 } ;
774851 assert_eq ! ( balloon. config( ) , cfg) ;
775852
@@ -798,7 +875,7 @@ pub(crate) mod tests {
798875
799876 #[ test]
800877 fn test_virtio_write_config ( ) {
801- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
878+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
802879
803880 let expected_config_space: [ u8 ; BALLOON_CONFIG_SPACE_SIZE ] =
804881 [ 0x00 , 0x50 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
@@ -824,7 +901,7 @@ pub(crate) mod tests {
824901
825902 #[ test]
826903 fn test_invalid_request ( ) {
827- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
904+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
828905 let mem = default_mem ( ) ;
829906 let interrupt = default_interrupt ( ) ;
830907 // Only initialize the inflate queue to demonstrate invalid request handling.
@@ -885,7 +962,7 @@ pub(crate) mod tests {
885962
886963 #[ test]
887964 fn test_inflate ( ) {
888- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
965+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
889966 let mem = default_mem ( ) ;
890967 let interrupt = default_interrupt ( ) ;
891968 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -957,7 +1034,7 @@ pub(crate) mod tests {
9571034
9581035 #[ test]
9591036 fn test_deflate ( ) {
960- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1037+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
9611038 let mem = default_mem ( ) ;
9621039 let interrupt = default_interrupt ( ) ;
9631040 let defq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1007,7 +1084,7 @@ pub(crate) mod tests {
10071084
10081085 #[ test]
10091086 fn test_stats ( ) {
1010- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1087+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
10111088 let mem = default_mem ( ) ;
10121089 let interrupt = default_interrupt ( ) ;
10131090 let statsq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1099,7 +1176,7 @@ pub(crate) mod tests {
10991176
11001177 #[ test]
11011178 fn test_process_balloon_queues ( ) {
1102- let mut balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
1179+ let mut balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
11031180 let mem = default_mem ( ) ;
11041181 let interrupt = default_interrupt ( ) ;
11051182 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1114,7 +1191,7 @@ pub(crate) mod tests {
11141191
11151192 #[ test]
11161193 fn test_update_stats_interval ( ) {
1117- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1194+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11181195 let mem = default_mem ( ) ;
11191196 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11201197 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1127,7 +1204,7 @@ pub(crate) mod tests {
11271204 ) ;
11281205 balloon. update_stats_polling_interval ( 0 ) . unwrap ( ) ;
11291206
1130- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1207+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
11311208 let mem = default_mem ( ) ;
11321209 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11331210 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1145,14 +1222,14 @@ pub(crate) mod tests {
11451222
11461223 #[ test]
11471224 fn test_cannot_update_inactive_device ( ) {
1148- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1225+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11491226 // Assert that we can't update an inactive device.
11501227 balloon. update_size ( 1 ) . unwrap_err ( ) ;
11511228 }
11521229
11531230 #[ test]
11541231 fn test_num_pages ( ) {
1155- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1232+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11561233 // Switch the state to active.
11571234 balloon. device_state = DeviceState :: Activated ( ActiveState {
11581235 mem : single_region_mem ( 32 << 20 ) ,
0 commit comments