@@ -8,7 +8,7 @@ use lru_mem::{LruCache, MemSize};
88use nvme:: types:: { Chunk , Zone } ;
99use std:: sync:: {
1010 Arc , Mutex ,
11- atomic:: { AtomicBool , Ordering } ,
11+ atomic:: { AtomicBool , AtomicU64 , Ordering } ,
1212} ;
1313use std:: thread:: { self , JoinHandle } ;
1414use std:: time:: Duration ;
@@ -119,7 +119,8 @@ pub struct PromotionalEvictionPolicy {
119119 high_water : Zone ,
120120 low_water : Zone ,
121121 nr_zones : Zone ,
122- nr_chunks_per_zone : Chunk ,
122+ pub nr_chunks_per_zone : Chunk ,
123+ pub zone_chunk_counts : Arc < Vec < AtomicU64 > > ,
123124 lru : LruCache < Zone , ( ) > ,
124125 #[ cfg( feature = "eviction-metrics" ) ]
125126 pub metrics : Option < Arc < crate :: eviction_metrics:: EvictionMetrics > > ,
@@ -133,11 +134,17 @@ impl PromotionalEvictionPolicy {
133134 nr_chunks_per_zone : Chunk ,
134135 ) -> Self {
135136 let lru = LruCache :: new ( usize:: MAX ) ; // Effectively unbounded
137+ let zone_chunk_counts = Arc :: new (
138+ ( 0 ..nr_zones)
139+ . map ( |_| AtomicU64 :: new ( 0 ) )
140+ . collect ( )
141+ ) ;
136142 Self {
137143 high_water,
138144 low_water,
139145 nr_zones,
140146 nr_chunks_per_zone,
147+ zone_chunk_counts,
141148 lru,
142149 #[ cfg( feature = "eviction-metrics" ) ]
143150 metrics : None ,
@@ -157,12 +164,9 @@ impl EvictionPolicy for PromotionalEvictionPolicy {
157164 metrics. record_write ( & chunk) ;
158165 }
159166
160- // assert!(!self.lru.contains(&chunk.zone)); // We cannot assert this because we allow out of order writes
161-
162- // We only want to put it in the LRU once the zone is full
163- if chunk. index == self . nr_chunks_per_zone - 1 {
164- self . lru . insert ( chunk. zone , ( ) ) . ok ( ) ;
165- }
167+ // This is now only called when zone is full (atomic check done outside lock)
168+ // Just insert the zone into the LRU
169+ self . lru . insert ( chunk. zone , ( ) ) . ok ( ) ;
166170 }
167171
168172 fn read_update ( & mut self , chunk : ChunkLocation ) {
@@ -171,9 +175,8 @@ impl EvictionPolicy for PromotionalEvictionPolicy {
171175 metrics. record_read ( & chunk) ;
172176 }
173177
174- // We only want to put it in the LRU once the zone is full
175- // If it has filled before we want to update every time "promoting" it
176- // Following this, only zones that have filled prior are updated
178+ // This is now only called when zone is full (atomic check done outside lock)
179+ // Promote the zone in the LRU if it's already there
177180 if self . lru . contains ( & chunk. zone ) {
178181 self . lru . insert ( chunk. zone , ( ) ) . ok ( ) ;
179182 }
@@ -558,7 +561,10 @@ mod tests {
558561
559562 // zone=[_,_,_,_], lru=()
560563 let mut order: VecDeque < Zone > = VecDeque :: new ( ) ;
561- policy. write_update ( ChunkLocation :: new ( 3 , 0 ) ) ;
564+ let count = policy. zone_chunk_counts [ 3 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
565+ if count + 1 == policy. nr_chunks_per_zone {
566+ policy. write_update ( ChunkLocation :: new ( 3 , 0 ) ) ;
567+ }
562568 compare_order ( & mut policy. lru , & order) ;
563569 let et = policy. get_evict_targets ( false ) ;
564570 let expect_none: Vec < Zone > = vec ! [ ] ;
@@ -569,7 +575,10 @@ mod tests {
569575 ) ;
570576
571577 // zone=[_,_,_,_], lru=()
572- policy. write_update ( ChunkLocation :: new ( 3 , 1 ) ) ;
578+ let count = policy. zone_chunk_counts [ 3 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
579+ if count + 1 == policy. nr_chunks_per_zone {
580+ policy. write_update ( ChunkLocation :: new ( 3 , 1 ) ) ;
581+ }
573582 // zone=[_,_,_,3], lru=(3)
574583 order. push_back ( 3 ) ;
575584 compare_order ( & mut policy. lru , & order) ;
@@ -580,12 +589,18 @@ mod tests {
580589 expect_none, et
581590 ) ;
582591
583- policy. write_update ( ChunkLocation :: new ( 1 , 0 ) ) ;
592+ let count = policy. zone_chunk_counts [ 1 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
593+ if count + 1 == policy. nr_chunks_per_zone {
594+ policy. write_update ( ChunkLocation :: new ( 1 , 0 ) ) ;
595+ }
584596 // There should be no change
585597 // zone=[_,_,_,3], lru=(3)
586598 compare_order ( & mut policy. lru , & order) ;
587599
588- policy. write_update ( ChunkLocation :: new ( 1 , 1 ) ) ;
600+ let count = policy. zone_chunk_counts [ 1 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
601+ if count + 1 == policy. nr_chunks_per_zone {
602+ policy. write_update ( ChunkLocation :: new ( 1 , 1 ) ) ;
603+ }
589604 // zone=[_,1,_,3], lru=(3, 1)
590605 order. push_front ( 1 ) ;
591606 compare_order ( & mut policy. lru , & order) ;
@@ -595,15 +610,22 @@ mod tests {
595610 "Expected = {:?}, but got {:?}" ,
596611 expect_none, et
597612 ) ;
598-
599- policy. write_update ( ChunkLocation :: new ( 2 , 0 ) ) ;
600- policy. write_update ( ChunkLocation :: new ( 2 , 1 ) ) ;
613+ let count = policy. zone_chunk_counts [ 2 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
614+ if count + 1 == policy. nr_chunks_per_zone {
615+ policy. write_update ( ChunkLocation :: new ( 2 , 0 ) ) ;
616+ }
617+ let count = policy. zone_chunk_counts [ 2 ] . fetch_add ( 1 , Ordering :: Relaxed ) ;
618+ if count + 1 == policy. nr_chunks_per_zone {
619+ policy. write_update ( ChunkLocation :: new ( 2 , 1 ) ) ;
620+ }
601621 order. push_front ( 2 ) ;
602622 // zone=[_,1,2,3], lru=(3, 1, 2)
603623 compare_order ( & mut policy. lru , & order) ;
604624
605625 // Should update in place, and adjust order
606- policy. read_update ( ChunkLocation :: new ( 3 , 1 ) ) ;
626+ if policy. zone_chunk_counts [ 3 ] . load ( Ordering :: Relaxed ) >= policy. nr_chunks_per_zone {
627+ policy. read_update ( ChunkLocation :: new ( 3 , 1 ) ) ;
628+ }
607629 let c = order. pop_back ( ) . unwrap ( ) ;
608630 order. push_front ( c) ;
609631 // zone=[_,1,2,3], lru=(1, 2, 3)
0 commit comments