@@ -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.
@@ -169,7 +172,7 @@ pub struct Balloon {
169172
170173 // Transport related fields.
171174 pub ( crate ) queues : Vec < Queue > ,
172- pub ( crate ) queue_evts : [ EventFd ; BALLOON_NUM_QUEUES ] ,
175+ pub ( crate ) queue_evts : Vec < EventFd > ,
173176 pub ( crate ) device_state : DeviceState ,
174177
175178 // Implementation specific fields.
@@ -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
@@ -200,20 +204,26 @@ impl Balloon {
200204 avail_features |= 1u64 << VIRTIO_BALLOON_F_STATS_VQ ;
201205 }
202206
203- let queue_evts = [
204- EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
205- EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
206- EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) ?,
207- ] ;
208-
209- let mut queues: Vec < Queue > = BALLOON_QUEUE_SIZES . iter ( ) . map ( |& s| Queue :: new ( s) ) . collect ( ) ;
210-
211207 // The VirtIO specification states that the statistics queue should
212208 // not be present at all if the statistics are not enabled.
209+ let mut queue_count = BALLOON_QUEUE_SIZES . len ( ) ;
213210 if stats_polling_interval_s == 0 {
214- let _ = queues. remove ( STATS_INDEX ) ;
211+ queue_count -= 1 ;
212+ }
213+
214+ if free_page_reporting {
215+ avail_features |= 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ;
216+ } else {
217+ queue_count -= 1 ;
215218 }
216219
220+ let queues: Vec < Queue > = ( 0 ..queue_count)
221+ . map ( |s| Queue :: new ( BALLOON_QUEUE_SIZES [ s] ) )
222+ . collect ( ) ;
223+ let queue_evts = ( 0 ..queue_count)
224+ . map ( |_| EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) )
225+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
226+
217227 let stats_timer =
218228 TimerFd :: new_custom ( ClockId :: Monotonic , true , true ) . map_err ( BalloonError :: Timer ) ?;
219229
@@ -262,9 +272,20 @@ impl Balloon {
262272 self . trigger_stats_update ( )
263273 }
264274
275+ pub ( crate ) fn process_free_page_reporting_queue_event ( & mut self ) -> Result < ( ) , BalloonError > {
276+ self . queue_evts [ self . free_page_reporting_idx ( ) ]
277+ . read ( )
278+ . map_err ( BalloonError :: EventFd ) ?;
279+ self . process_free_page_reporting_queue ( )
280+ }
281+
265282 pub ( crate ) fn process_inflate ( & mut self ) -> Result < ( ) , BalloonError > {
266283 // 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 ;
284+ let mem = & self
285+ . device_state
286+ . active_state ( )
287+ . ok_or ( BalloonError :: DeviceNotActive ) ?
288+ . mem ;
268289 METRICS . inflate_count . inc ( ) ;
269290
270291 let queue = & mut self . queues [ INFLATE_INDEX ] ;
@@ -406,6 +427,37 @@ impl Balloon {
406427 Ok ( ( ) )
407428 }
408429
430+ pub ( crate ) fn process_free_page_reporting_queue ( & mut self ) -> Result < ( ) , BalloonError > {
431+ let mem = & self . device_state . active_state ( ) . unwrap ( ) . mem ;
432+
433+ let idx = self . free_page_reporting_idx ( ) ;
434+ let queue = & mut self . queues [ idx] ;
435+ let mut needs_interrupt = false ;
436+
437+ while let Some ( head) = queue. pop ( ) ? {
438+ let head_index = head. index ;
439+
440+ let mut last_desc = Some ( head) ;
441+ while let Some ( desc) = last_desc {
442+ if let Err ( err) = mem. discard_range ( desc. addr , desc. len as usize ) {
443+ error ! ( "balloon: failed to remove range: {err:?}" ) ;
444+ }
445+ last_desc = desc. next_descriptor ( ) ;
446+ }
447+
448+ queue. add_used ( head. index , 0 ) ?;
449+ needs_interrupt = true ;
450+ }
451+
452+ queue. advance_used_ring_idx ( ) ;
453+
454+ if needs_interrupt {
455+ self . signal_used_queue ( idx) ?;
456+ }
457+
458+ Ok ( ( ) )
459+ }
460+
409461 pub ( crate ) fn signal_used_queue ( & self , qidx : usize ) -> Result < ( ) , BalloonError > {
410462 self . interrupt_trigger ( )
411463 . trigger ( VirtioInterruptType :: Queue (
@@ -427,6 +479,13 @@ impl Balloon {
427479 return Err ( err) ;
428480 }
429481
482+ if self . free_page_reporting ( )
483+ && let Err ( BalloonError :: InvalidAvailIdx ( err) ) =
484+ self . process_free_page_reporting_queue ( )
485+ {
486+ return Err ( err) ;
487+ }
488+
430489 Ok ( ( ) )
431490 }
432491
@@ -466,6 +525,20 @@ impl Balloon {
466525 }
467526 }
468527
528+ pub fn free_page_reporting ( & self ) -> bool {
529+ self . avail_features & ( 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ) != 0
530+ }
531+
532+ pub fn free_page_reporting_idx ( & self ) -> usize {
533+ let mut idx = STATS_INDEX ;
534+
535+ if self . stats_polling_interval_s > 0 {
536+ idx += 1 ;
537+ }
538+
539+ idx
540+ }
541+
469542 /// Update the statistics polling interval.
470543 pub fn update_stats_polling_interval ( & mut self , interval_s : u16 ) -> Result < ( ) , BalloonError > {
471544 if self . stats_polling_interval_s == interval_s {
@@ -529,6 +602,7 @@ impl Balloon {
529602 amount_mib : self . size_mb ( ) ,
530603 deflate_on_oom : self . deflate_on_oom ( ) ,
531604 stats_polling_interval_s : self . stats_polling_interval_s ( ) ,
605+ free_page_reporting : self . free_page_reporting ( ) ,
532606 }
533607 }
534608
@@ -737,7 +811,7 @@ pub(crate) mod tests {
737811 // Test all feature combinations.
738812 for deflate_on_oom in [ true , false ] . iter ( ) {
739813 for stats_interval in [ 0 , 1 ] . iter ( ) {
740- let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval) . unwrap ( ) ;
814+ let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval, false ) . unwrap ( ) ;
741815 assert_eq ! ( balloon. device_type( ) , VIRTIO_ID_BALLOON ) ;
742816
743817 let features: u64 = ( 1u64 << VIRTIO_F_VERSION_1 )
@@ -764,12 +838,13 @@ pub(crate) mod tests {
764838
765839 #[ test]
766840 fn test_virtio_read_config ( ) {
767- let balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
841+ let balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
768842
769843 let cfg = BalloonConfig {
770844 amount_mib : 16 ,
771845 deflate_on_oom : true ,
772846 stats_polling_interval_s : 0 ,
847+ free_page_reporting : false ,
773848 } ;
774849 assert_eq ! ( balloon. config( ) , cfg) ;
775850
@@ -798,7 +873,7 @@ pub(crate) mod tests {
798873
799874 #[ test]
800875 fn test_virtio_write_config ( ) {
801- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
876+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
802877
803878 let expected_config_space: [ u8 ; BALLOON_CONFIG_SPACE_SIZE ] =
804879 [ 0x00 , 0x50 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
@@ -824,7 +899,7 @@ pub(crate) mod tests {
824899
825900 #[ test]
826901 fn test_invalid_request ( ) {
827- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
902+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
828903 let mem = default_mem ( ) ;
829904 let interrupt = default_interrupt ( ) ;
830905 // Only initialize the inflate queue to demonstrate invalid request handling.
@@ -885,7 +960,7 @@ pub(crate) mod tests {
885960
886961 #[ test]
887962 fn test_inflate ( ) {
888- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
963+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
889964 let mem = default_mem ( ) ;
890965 let interrupt = default_interrupt ( ) ;
891966 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -957,7 +1032,7 @@ pub(crate) mod tests {
9571032
9581033 #[ test]
9591034 fn test_deflate ( ) {
960- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1035+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
9611036 let mem = default_mem ( ) ;
9621037 let interrupt = default_interrupt ( ) ;
9631038 let defq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1007,7 +1082,7 @@ pub(crate) mod tests {
10071082
10081083 #[ test]
10091084 fn test_stats ( ) {
1010- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1085+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
10111086 let mem = default_mem ( ) ;
10121087 let interrupt = default_interrupt ( ) ;
10131088 let statsq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1099,7 +1174,7 @@ pub(crate) mod tests {
10991174
11001175 #[ test]
11011176 fn test_process_balloon_queues ( ) {
1102- let mut balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
1177+ let mut balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
11031178 let mem = default_mem ( ) ;
11041179 let interrupt = default_interrupt ( ) ;
11051180 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1114,7 +1189,7 @@ pub(crate) mod tests {
11141189
11151190 #[ test]
11161191 fn test_update_stats_interval ( ) {
1117- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1192+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11181193 let mem = default_mem ( ) ;
11191194 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11201195 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1127,7 +1202,7 @@ pub(crate) mod tests {
11271202 ) ;
11281203 balloon. update_stats_polling_interval ( 0 ) . unwrap ( ) ;
11291204
1130- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1205+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
11311206 let mem = default_mem ( ) ;
11321207 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11331208 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1145,14 +1220,14 @@ pub(crate) mod tests {
11451220
11461221 #[ test]
11471222 fn test_cannot_update_inactive_device ( ) {
1148- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1223+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11491224 // Assert that we can't update an inactive device.
11501225 balloon. update_size ( 1 ) . unwrap_err ( ) ;
11511226 }
11521227
11531228 #[ test]
11541229 fn test_num_pages ( ) {
1155- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1230+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11561231 // Switch the state to active.
11571232 balloon. device_state = DeviceState :: Activated ( ActiveState {
11581233 mem : single_region_mem ( 32 << 20 ) ,
0 commit comments