@@ -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,20 @@ 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
289+ . device_state
290+ . active_state ( )
291+ . ok_or ( BalloonError :: DeviceNotActive ) ?
292+ . mem ;
268293 METRICS . inflate_count . inc ( ) ;
269294
270295 let queue = & mut self . queues [ INFLATE_INDEX ] ;
@@ -406,6 +431,37 @@ impl Balloon {
406431 Ok ( ( ) )
407432 }
408433
434+ pub ( crate ) fn process_free_page_reporting_queue ( & mut self ) -> Result < ( ) , BalloonError > {
435+ let mem = & self . device_state . active_state ( ) . unwrap ( ) . mem ;
436+
437+ let idx = self . free_page_reporting_idx ( ) ;
438+ let queue = & mut self . queues [ idx] ;
439+ let mut needs_interrupt = false ;
440+
441+ while let Some ( head) = queue. pop ( ) ? {
442+ let head_index = head. index ;
443+
444+ let mut last_desc = Some ( head) ;
445+ while let Some ( desc) = last_desc {
446+ if let Err ( err) = mem. discard_range ( desc. addr , desc. len as usize ) {
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,13 @@ impl Balloon {
427483 return Err ( err) ;
428484 }
429485
486+ if self . free_page_reporting ( )
487+ && let Err ( BalloonError :: InvalidAvailIdx ( err) ) =
488+ self . process_free_page_reporting_queue ( )
489+ {
490+ return Err ( err) ;
491+ }
492+
430493 Ok ( ( ) )
431494 }
432495
@@ -466,6 +529,20 @@ impl Balloon {
466529 }
467530 }
468531
532+ pub fn free_page_reporting ( & self ) -> bool {
533+ self . avail_features & ( 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ) != 0
534+ }
535+
536+ pub fn free_page_reporting_idx ( & self ) -> usize {
537+ let mut idx = STATS_INDEX ;
538+
539+ if self . stats_polling_interval_s > 0 {
540+ idx += 1 ;
541+ }
542+
543+ idx
544+ }
545+
469546 /// Update the statistics polling interval.
470547 pub fn update_stats_polling_interval ( & mut self , interval_s : u16 ) -> Result < ( ) , BalloonError > {
471548 if self . stats_polling_interval_s == interval_s {
@@ -529,6 +606,7 @@ impl Balloon {
529606 amount_mib : self . size_mb ( ) ,
530607 deflate_on_oom : self . deflate_on_oom ( ) ,
531608 stats_polling_interval_s : self . stats_polling_interval_s ( ) ,
609+ free_page_reporting : self . free_page_reporting ( ) ,
532610 }
533611 }
534612
@@ -737,7 +815,7 @@ pub(crate) mod tests {
737815 // Test all feature combinations.
738816 for deflate_on_oom in [ true , false ] . iter ( ) {
739817 for stats_interval in [ 0 , 1 ] . iter ( ) {
740- let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval) . unwrap ( ) ;
818+ let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval, false ) . unwrap ( ) ;
741819 assert_eq ! ( balloon. device_type( ) , VIRTIO_ID_BALLOON ) ;
742820
743821 let features: u64 = ( 1u64 << VIRTIO_F_VERSION_1 )
@@ -764,12 +842,13 @@ pub(crate) mod tests {
764842
765843 #[ test]
766844 fn test_virtio_read_config ( ) {
767- let balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
845+ let balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
768846
769847 let cfg = BalloonConfig {
770848 amount_mib : 16 ,
771849 deflate_on_oom : true ,
772850 stats_polling_interval_s : 0 ,
851+ free_page_reporting : false ,
773852 } ;
774853 assert_eq ! ( balloon. config( ) , cfg) ;
775854
@@ -798,7 +877,7 @@ pub(crate) mod tests {
798877
799878 #[ test]
800879 fn test_virtio_write_config ( ) {
801- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
880+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
802881
803882 let expected_config_space: [ u8 ; BALLOON_CONFIG_SPACE_SIZE ] =
804883 [ 0x00 , 0x50 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
@@ -824,7 +903,7 @@ pub(crate) mod tests {
824903
825904 #[ test]
826905 fn test_invalid_request ( ) {
827- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
906+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
828907 let mem = default_mem ( ) ;
829908 let interrupt = default_interrupt ( ) ;
830909 // Only initialize the inflate queue to demonstrate invalid request handling.
@@ -885,7 +964,7 @@ pub(crate) mod tests {
885964
886965 #[ test]
887966 fn test_inflate ( ) {
888- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
967+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
889968 let mem = default_mem ( ) ;
890969 let interrupt = default_interrupt ( ) ;
891970 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -957,7 +1036,7 @@ pub(crate) mod tests {
9571036
9581037 #[ test]
9591038 fn test_deflate ( ) {
960- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1039+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
9611040 let mem = default_mem ( ) ;
9621041 let interrupt = default_interrupt ( ) ;
9631042 let defq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1007,7 +1086,7 @@ pub(crate) mod tests {
10071086
10081087 #[ test]
10091088 fn test_stats ( ) {
1010- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1089+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
10111090 let mem = default_mem ( ) ;
10121091 let interrupt = default_interrupt ( ) ;
10131092 let statsq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1099,7 +1178,7 @@ pub(crate) mod tests {
10991178
11001179 #[ test]
11011180 fn test_process_balloon_queues ( ) {
1102- let mut balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
1181+ let mut balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
11031182 let mem = default_mem ( ) ;
11041183 let interrupt = default_interrupt ( ) ;
11051184 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1114,7 +1193,7 @@ pub(crate) mod tests {
11141193
11151194 #[ test]
11161195 fn test_update_stats_interval ( ) {
1117- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1196+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11181197 let mem = default_mem ( ) ;
11191198 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11201199 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1127,7 +1206,7 @@ pub(crate) mod tests {
11271206 ) ;
11281207 balloon. update_stats_polling_interval ( 0 ) . unwrap ( ) ;
11291208
1130- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1209+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
11311210 let mem = default_mem ( ) ;
11321211 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11331212 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1145,14 +1224,14 @@ pub(crate) mod tests {
11451224
11461225 #[ test]
11471226 fn test_cannot_update_inactive_device ( ) {
1148- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1227+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11491228 // Assert that we can't update an inactive device.
11501229 balloon. update_size ( 1 ) . unwrap_err ( ) ;
11511230 }
11521231
11531232 #[ test]
11541233 fn test_num_pages ( ) {
1155- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1234+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11561235 // Switch the state to active.
11571236 balloon. device_state = DeviceState :: Activated ( ActiveState {
11581237 mem : single_region_mem ( 32 << 20 ) ,
0 commit comments