From 5900ca1f47088cf8134a88118feaacf0da12db7c Mon Sep 17 00:00:00 2001 From: Matthias Goergens Date: Thu, 30 Jul 2026 14:11:49 +0800 Subject: [PATCH] dedupe: complete a request that deduped nothing, rather than re-queueing it process_dedupes() advances req_loff and req_total by info->bytes_deduped and re-queues the request whenever the ioctl succeeded and req_total has not yet reached orig_len. If a successful FIDEDUPERANGE ever reports zero bytes deduped, that re-queues a request with identical parameters, and the loop in dedupe_extents() never terminates. The kernel cannot produce that today: vfs_dedupe_file_range() reports the requested length rather than the amount actually deduped, so bytes_deduped is non-zero whenever status is 0. That is itself a bug -- generic_remap_check_len() silently shortens an unaligned length away from EOF, and the loop records "len" where "deduped" holds the real figure. It was fixed in 4a57a8400075 and reverted the next day in b926f2adb044, partly out of concern for how userspace would react to a truthful zero. So this guard is unreachable at present and is deliberately defensive. It costs one condition, and it means duperemove is not the reason the kernel side stays unfixed. req_total already holds what was really deduped, so completing the request reports the correct figure to the caller rather than looping. --- dedupe.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dedupe.c b/dedupe.c index 0ade18eeeccf..a2e117691afe 100644 --- a/dedupe.c +++ b/dedupe.c @@ -279,7 +279,14 @@ static void process_dedupes(struct dedupe_ctxt *ctxt, req->req_loff += info->bytes_deduped; req->req_total += info->bytes_deduped; - if (info->status || req->req_total >= ctxt->orig_len) { + /* + * A successful ioctl that deduped nothing has made no + * progress, so re-queueing would resubmit an identical + * request forever. Treat it as complete instead: req_total + * already reflects what was really deduped. + */ + if (info->status || req->req_total >= ctxt->orig_len || + !info->bytes_deduped) { /* * Only bother taking the final status (the * rest will be 0)