@@ -211,3 +211,122 @@ def test_alru_dirty_ratio_inertia_no_cleaning_if_dirty_below_threshold(cache_lin
211211 f"No flushing shall occur when dirty < threshold (dirty before={ dirty_before } , "
212212 f"dirty after={ dirty_after } , threshold={ dirty_ratio_threshold } %)"
213213 )
214+
215+
216+ @pytest .mark .require_disk ("cache" , DiskTypeSet ([DiskType .optane , DiskType .nand ]))
217+ @pytest .mark .require_disk ("core" , DiskTypeLowerThan ("cache" ))
218+ @pytest .mark .parametrize ("inertia" , [Size .zero (), Size (600 , Unit .MiB )])
219+ def test_alru_dirty_threshold_cleans_until_threshold_minus_inertia (inertia ):
220+ """
221+ title: Test ALRU threshold cleaning – start > 50%, stop at (threshold - inertia)
222+ description: |
223+ Verify that with ALRU cleaning configured with 50% dirty threshold and specified inertia in MiB,
224+ cleaning starts when dirty exceeds the threshold and stops after reaching (threshold - inertia).
225+ pass_criteria:
226+ - Cleaning starts once dirty exceeds 50%.
227+ - Dirty decreases over time after stopping I/O.
228+ - Cleaning stops at or below (threshold - inertia).
229+ - If dirty does not decrease for 20 consecutive checks, fail the test.
230+ """
231+
232+ target_dirty_ratio_threshold = 50
233+ # 3 GiB cache -> 50% = 1536 MiB; inertia is in MiB
234+ threshold_size = Size (1536 , Unit .MiB )
235+ target_cleaning_threshold = threshold_size - inertia
236+
237+ with TestRun .step ("Prepare 3GiB cache and 10GiB core, disable udev, start cache WB, add core" ):
238+ cache_dev = TestRun .disks ["cache" ]
239+ core_dev = TestRun .disks ["core" ]
240+
241+ cache_dev .create_partitions ([Size (3 , Unit .GiB )])
242+ core_dev .create_partitions ([Size (10 , Unit .GiB )])
243+
244+ with TestRun .step ("Disable udev" ):
245+ Udev .disable ()
246+
247+ with TestRun .step ("Start cache and add core" ):
248+ cache = casadm .start_cache (cache_dev .partitions [0 ], force = True , cache_mode = CacheMode .WB )
249+ core = cache .add_core (core_dev .partitions [0 ])
250+
251+ with TestRun .step ("Set ALRU and disable sequential cutoff" ):
252+ cache .set_seq_cutoff_policy (SeqCutOffPolicy .never )
253+ cache .set_cleaning_policy (CleaningPolicy .alru )
254+
255+ with TestRun .step (
256+ "Configure ALRU: dirty threshold=50%, inertia={inertia}, default wakeup, max staleness_time"
257+ ):
258+ cache .set_params_alru (
259+ FlushParametersAlru (
260+ staleness_time = Time (seconds = 3600 ),
261+ dirty_ratio_threshold = target_dirty_ratio_threshold ,
262+ dirty_ratio_inertia = inertia ,
263+ )
264+ )
265+
266+ with TestRun .step ("Start background fio (4 GiB randwrite)" ):
267+ (
268+ Fio ()
269+ .create_command ()
270+ .io_engine (IoEngine .libaio )
271+ .size (Size (4 , Unit .GiB ))
272+ .block_size (Size (4 , Unit .KiB ))
273+ .target (core )
274+ .direct ()
275+ .read_write (ReadWrite .randwrite )
276+ .run_in_background ()
277+ )
278+
279+ with TestRun .step ("Wait until cache dirty >= 90%, then stop workload" ):
280+ start_ts = time .time ()
281+ while True :
282+ time .sleep (2 )
283+ dirty_pct = cache .get_statistics (percentage_val = True ).usage_stats .dirty
284+ if dirty_pct >= 90 :
285+ break
286+ if time .time () - start_ts > 5 * 60 :
287+ TestRun .LOGGER .exception (
288+ f"Cache dirty level did not reach 90% within 5 minutes "
289+ f"(current dirty={ dirty_pct } %). Aborting test."
290+ )
291+ TestRun .fail (
292+ "TODO: For some reason the TestRun.LOGGER.exception doesn't interrupt the test, "
293+ "but only modifies the test status and continiues the execution. Until fixed, use TestRun.fail"
294+ )
295+
296+ with TestRun .step ("Make sure that the all IO has been completed" ):
297+ kill_all_io ()
298+ sync ()
299+
300+ with TestRun .step (
301+ "Observe cleaning: ensure monotonic decrease and stop at (threshold - inertia)"
302+ ):
303+ # Initial dirty after IO stop
304+ last_dirty = cache .get_statistics ().usage_stats .dirty
305+
306+ not_decreasing = 0
307+ while last_dirty > target_cleaning_threshold :
308+ time .sleep (5 )
309+ dirty_now = cache .get_statistics ().usage_stats .dirty
310+
311+ if dirty_now < last_dirty :
312+ not_decreasing = 0
313+ else :
314+ not_decreasing += 1
315+ if not_decreasing >= 20 :
316+ TestRun .fail (
317+ f"Exception: Dirty amount not decreasing for 20 consecutive checks "
318+ f"(stuck at { dirty_now } , target stop { target_cleaning_threshold } )."
319+ )
320+
321+ last_dirty = dirty_now
322+
323+ with TestRun .step (
324+ "Once the dirty cache lines counter reached (threshold - inertia) level, "
325+ "no further cleaning should take place"
326+ ):
327+ time .sleep (30 )
328+ if last_dirty > target_cleaning_threshold :
329+ TestRun .fail (
330+ f"Cleaning did not stop at expected level "
331+ f"(dirty={ last_dirty } , expected <= { target_cleaning_threshold } )."
332+ )
0 commit comments