@@ -16,13 +16,13 @@ use super::super::queue::Queue;
1616use super :: metrics:: METRICS ;
1717use super :: util:: compact_page_frame_numbers;
1818use super :: {
19- BALLOON_DEV_ID , BALLOON_NUM_QUEUES , BALLOON_QUEUE_SIZES , DEFLATE_INDEX , INFLATE_INDEX ,
20- MAX_PAGE_COMPACT_BUFFER , MAX_PAGES_IN_DESC , MIB_TO_4K_PAGES , STATS_INDEX ,
21- VIRTIO_BALLOON_F_DEFLATE_ON_OOM , VIRTIO_BALLOON_F_STATS_VQ , VIRTIO_BALLOON_PFN_SHIFT ,
22- VIRTIO_BALLOON_S_AVAIL , VIRTIO_BALLOON_S_CACHES , VIRTIO_BALLOON_S_HTLB_PGALLOC ,
23- VIRTIO_BALLOON_S_HTLB_PGFAIL , VIRTIO_BALLOON_S_MAJFLT , VIRTIO_BALLOON_S_MEMFREE ,
24- VIRTIO_BALLOON_S_MEMTOT , VIRTIO_BALLOON_S_MINFLT , VIRTIO_BALLOON_S_SWAP_IN ,
25- VIRTIO_BALLOON_S_SWAP_OUT ,
19+ BALLOON_DEV_ID , BALLOON_MIN_NUM_QUEUES , BALLOON_NUM_QUEUES , BALLOON_QUEUE_SIZE , DEFLATE_INDEX ,
20+ INFLATE_INDEX , MAX_PAGE_COMPACT_BUFFER , MAX_PAGES_IN_DESC , MIB_TO_4K_PAGES , STATS_INDEX ,
21+ VIRTIO_BALLOON_F_DEFLATE_ON_OOM , VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ,
22+ VIRTIO_BALLOON_F_STATS_VQ , VIRTIO_BALLOON_PFN_SHIFT , VIRTIO_BALLOON_S_AVAIL ,
23+ VIRTIO_BALLOON_S_CACHES , VIRTIO_BALLOON_S_HTLB_PGALLOC , VIRTIO_BALLOON_S_HTLB_PGFAIL ,
24+ VIRTIO_BALLOON_S_MAJFLT , VIRTIO_BALLOON_S_MEMFREE , VIRTIO_BALLOON_S_MEMTOT ,
25+ VIRTIO_BALLOON_S_MINFLT , VIRTIO_BALLOON_S_SWAP_IN , VIRTIO_BALLOON_S_SWAP_OUT ,
2626} ;
2727use crate :: devices:: virtio:: balloon:: BalloonError ;
2828use crate :: devices:: virtio:: device:: ActiveState ;
@@ -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,25 @@ 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.
213- if stats_polling_interval_s == 0 {
214- let _ = queues. remove ( STATS_INDEX ) ;
209+ let mut queue_count = BALLOON_MIN_NUM_QUEUES ;
210+ if stats_polling_interval_s > 0 {
211+ queue_count += 1 ;
215212 }
216213
214+ if free_page_reporting {
215+ avail_features |= 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ;
216+ queue_count += 1 ;
217+ }
218+
219+ let queues: Vec < Queue > = ( 0 ..queue_count)
220+ . map ( |_| Queue :: new ( BALLOON_QUEUE_SIZE ) )
221+ . collect ( ) ;
222+ let queue_evts = ( 0 ..queue_count)
223+ . map ( |_| EventFd :: new ( libc:: EFD_NONBLOCK ) . map_err ( BalloonError :: EventFd ) )
224+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
225+
217226 let stats_timer =
218227 TimerFd :: new_custom ( ClockId :: Monotonic , true , true ) . map_err ( BalloonError :: Timer ) ?;
219228
@@ -262,9 +271,20 @@ impl Balloon {
262271 self . trigger_stats_update ( )
263272 }
264273
274+ pub ( crate ) fn process_free_page_reporting_queue_event ( & mut self ) -> Result < ( ) , BalloonError > {
275+ self . queue_evts [ self . free_page_reporting_idx ( ) ]
276+ . read ( )
277+ . map_err ( BalloonError :: EventFd ) ?;
278+ self . process_free_page_reporting_queue ( )
279+ }
280+
265281 pub ( crate ) fn process_inflate ( & mut self ) -> Result < ( ) , BalloonError > {
266282 // 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 ;
283+ let mem = & self
284+ . device_state
285+ . active_state ( )
286+ . ok_or ( BalloonError :: DeviceNotActive ) ?
287+ . mem ;
268288 METRICS . inflate_count . inc ( ) ;
269289
270290 let queue = & mut self . queues [ INFLATE_INDEX ] ;
@@ -406,6 +426,37 @@ impl Balloon {
406426 Ok ( ( ) )
407427 }
408428
429+ pub ( crate ) fn process_free_page_reporting_queue ( & mut self ) -> Result < ( ) , BalloonError > {
430+ let mem = & self . device_state . active_state ( ) . unwrap ( ) . mem ;
431+
432+ let idx = self . free_page_reporting_idx ( ) ;
433+ let queue = & mut self . queues [ idx] ;
434+ let mut needs_interrupt = false ;
435+
436+ while let Some ( head) = queue. pop ( ) ? {
437+ let head_index = head. index ;
438+
439+ let mut last_desc = Some ( head) ;
440+ while let Some ( desc) = last_desc {
441+ if let Err ( err) = mem. discard_range ( desc. addr , desc. len as usize ) {
442+ error ! ( "balloon: failed to remove range: {err:?}" ) ;
443+ }
444+ last_desc = desc. next_descriptor ( ) ;
445+ }
446+
447+ queue. add_used ( head. index , 0 ) ?;
448+ needs_interrupt = true ;
449+ }
450+
451+ queue. advance_used_ring_idx ( ) ;
452+
453+ if needs_interrupt {
454+ self . signal_used_queue ( idx) ?;
455+ }
456+
457+ Ok ( ( ) )
458+ }
459+
409460 pub ( crate ) fn signal_used_queue ( & self , qidx : usize ) -> Result < ( ) , BalloonError > {
410461 self . interrupt_trigger ( )
411462 . trigger ( VirtioInterruptType :: Queue (
@@ -427,6 +478,13 @@ impl Balloon {
427478 return Err ( err) ;
428479 }
429480
481+ if self . free_page_reporting ( )
482+ && let Err ( BalloonError :: InvalidAvailIdx ( err) ) =
483+ self . process_free_page_reporting_queue ( )
484+ {
485+ return Err ( err) ;
486+ }
487+
430488 Ok ( ( ) )
431489 }
432490
@@ -466,6 +524,20 @@ impl Balloon {
466524 }
467525 }
468526
527+ pub fn free_page_reporting ( & self ) -> bool {
528+ self . avail_features & ( 1u64 << VIRTIO_BALLOON_F_FREE_PAGE_REPORTING ) != 0
529+ }
530+
531+ pub fn free_page_reporting_idx ( & self ) -> usize {
532+ let mut idx = STATS_INDEX ;
533+
534+ if self . stats_polling_interval_s > 0 {
535+ idx += 1 ;
536+ }
537+
538+ idx
539+ }
540+
469541 /// Update the statistics polling interval.
470542 pub fn update_stats_polling_interval ( & mut self , interval_s : u16 ) -> Result < ( ) , BalloonError > {
471543 if self . stats_polling_interval_s == interval_s {
@@ -529,6 +601,7 @@ impl Balloon {
529601 amount_mib : self . size_mb ( ) ,
530602 deflate_on_oom : self . deflate_on_oom ( ) ,
531603 stats_polling_interval_s : self . stats_polling_interval_s ( ) ,
604+ free_page_reporting : self . free_page_reporting ( ) ,
532605 }
533606 }
534607
@@ -737,7 +810,7 @@ pub(crate) mod tests {
737810 // Test all feature combinations.
738811 for deflate_on_oom in [ true , false ] . iter ( ) {
739812 for stats_interval in [ 0 , 1 ] . iter ( ) {
740- let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval) . unwrap ( ) ;
813+ let mut balloon = Balloon :: new ( 0 , * deflate_on_oom, * stats_interval, false ) . unwrap ( ) ;
741814 assert_eq ! ( balloon. device_type( ) , VIRTIO_ID_BALLOON ) ;
742815
743816 let features: u64 = ( 1u64 << VIRTIO_F_VERSION_1 )
@@ -764,12 +837,13 @@ pub(crate) mod tests {
764837
765838 #[ test]
766839 fn test_virtio_read_config ( ) {
767- let balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
840+ let balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
768841
769842 let cfg = BalloonConfig {
770843 amount_mib : 16 ,
771844 deflate_on_oom : true ,
772845 stats_polling_interval_s : 0 ,
846+ free_page_reporting : false ,
773847 } ;
774848 assert_eq ! ( balloon. config( ) , cfg) ;
775849
@@ -798,7 +872,7 @@ pub(crate) mod tests {
798872
799873 #[ test]
800874 fn test_virtio_write_config ( ) {
801- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
875+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
802876
803877 let expected_config_space: [ u8 ; BALLOON_CONFIG_SPACE_SIZE ] =
804878 [ 0x00 , 0x50 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] ;
@@ -824,7 +898,7 @@ pub(crate) mod tests {
824898
825899 #[ test]
826900 fn test_invalid_request ( ) {
827- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
901+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
828902 let mem = default_mem ( ) ;
829903 let interrupt = default_interrupt ( ) ;
830904 // Only initialize the inflate queue to demonstrate invalid request handling.
@@ -885,7 +959,7 @@ pub(crate) mod tests {
885959
886960 #[ test]
887961 fn test_inflate ( ) {
888- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
962+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
889963 let mem = default_mem ( ) ;
890964 let interrupt = default_interrupt ( ) ;
891965 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -957,7 +1031,7 @@ pub(crate) mod tests {
9571031
9581032 #[ test]
9591033 fn test_deflate ( ) {
960- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1034+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
9611035 let mem = default_mem ( ) ;
9621036 let interrupt = default_interrupt ( ) ;
9631037 let defq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1007,7 +1081,7 @@ pub(crate) mod tests {
10071081
10081082 #[ test]
10091083 fn test_stats ( ) {
1010- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1084+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
10111085 let mem = default_mem ( ) ;
10121086 let interrupt = default_interrupt ( ) ;
10131087 let statsq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1099,7 +1173,7 @@ pub(crate) mod tests {
10991173
11001174 #[ test]
11011175 fn test_process_balloon_queues ( ) {
1102- let mut balloon = Balloon :: new ( 0x10 , true , 0 ) . unwrap ( ) ;
1176+ let mut balloon = Balloon :: new ( 0x10 , true , 0 , false ) . unwrap ( ) ;
11031177 let mem = default_mem ( ) ;
11041178 let interrupt = default_interrupt ( ) ;
11051179 let infq = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
@@ -1114,7 +1188,7 @@ pub(crate) mod tests {
11141188
11151189 #[ test]
11161190 fn test_update_stats_interval ( ) {
1117- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1191+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11181192 let mem = default_mem ( ) ;
11191193 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11201194 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1127,7 +1201,7 @@ pub(crate) mod tests {
11271201 ) ;
11281202 balloon. update_stats_polling_interval ( 0 ) . unwrap ( ) ;
11291203
1130- let mut balloon = Balloon :: new ( 0 , true , 1 ) . unwrap ( ) ;
1204+ let mut balloon = Balloon :: new ( 0 , true , 1 , false ) . unwrap ( ) ;
11311205 let mem = default_mem ( ) ;
11321206 let q = VirtQueue :: new ( GuestAddress ( 0 ) , & mem, 16 ) ;
11331207 balloon. set_queue ( INFLATE_INDEX , q. create_queue ( ) ) ;
@@ -1145,14 +1219,14 @@ pub(crate) mod tests {
11451219
11461220 #[ test]
11471221 fn test_cannot_update_inactive_device ( ) {
1148- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1222+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11491223 // Assert that we can't update an inactive device.
11501224 balloon. update_size ( 1 ) . unwrap_err ( ) ;
11511225 }
11521226
11531227 #[ test]
11541228 fn test_num_pages ( ) {
1155- let mut balloon = Balloon :: new ( 0 , true , 0 ) . unwrap ( ) ;
1229+ let mut balloon = Balloon :: new ( 0 , true , 0 , false ) . unwrap ( ) ;
11561230 // Switch the state to active.
11571231 balloon. device_state = DeviceState :: Activated ( ActiveState {
11581232 mem : single_region_mem ( 32 << 20 ) ,
0 commit comments