From: "Darrick J. Wong" <djwong@kernel.org>
To: Matthias Goergens <matthias.goergens@gmail.com>
Cc: linux-fsdevel@vger.kernel.org, viro@zeniv.linux.org.uk,
brauner@kernel.org, jack@suse.cz, linux-kernel@vger.kernel.org,
ansgar.loesser@kom.tu-darmstadt.de, david@fromorbit.com,
amir73il@gmail.com
Subject: Re: [PATCH v2] vfs: add FILE_DEDUPE_RANGE_REPORT_PROGRESS flag to FIDEDUPERANGE
Date: Fri, 14 Aug 2026 09:16:57 -0700 [thread overview]
Message-ID: <20260814161657.GN3560084@frogsfrogsfrogs> (raw)
In-Reply-To: <20260814082326.3756669-2-matthias.goergens@gmail.com>
On Fri, Aug 14, 2026 at 04:23:26PM +0800, Matthias Goergens wrote:
> FIDEDUPERANGE reports the requested length in bytes_deduped even when
> the filesystem shortens a destination range and deduplicates fewer
> bytes. This predates the VFS hoisting of the ioctl (the btrfs ioctl
> behaved the same way), and changing the default would change an ABI
> that deployed consumers such as duperemove depend on: they advance
> their offsets by bytes_deduped and expect the historical semantics.
>
> Add a flag to opt into the truthful behaviour: with
> FILE_DEDUPE_RANGE_REPORT_PROGRESS set, bytes_deduped reports the bytes
> actually deduplicated, and a non-zero request shortened to zero fails
> per destination with -EINVAL instead of reporting success with no
> progress. Unknown flag bits are rejected.
>
> The flag leaves every existing caller's behaviour unchanged and lets
> new callers request accurate progress reporting.
>
> Suggested-by: Darrick J. Wong <djwong@kernel.org>
> Link: https://lore.kernel.org/linux-fsdevel/20260805071414.3414870-1-matthias.goergens@gmail.com/
> Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
> ---
> fs/remap_range.c | 7 ++++++-
> include/uapi/linux/fs.h | 4 +++-
> 2 files changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/fs/remap_range.c b/fs/remap_range.c
> index 26afbbbfb10c2..16192ec19bb6d 100644
> --- a/fs/remap_range.c
> +++ b/fs/remap_range.c
> @@ -503,7 +503,7 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
> if (!(file->f_mode & FMODE_READ))
> return -EINVAL;
>
> - if (same->reserved1 || same->reserved2)
> + if (same->reserved1 || (same->flags & ~FILE_DEDUPE_RANGE_REPORT_PROGRESS))
> return -EINVAL;
>
> off = same->src_offset;
> @@ -555,6 +555,11 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
> info->status = FILE_DEDUPE_RANGE_DIFFERS;
> else if (deduped < 0)
> info->status = deduped;
> + else if ((same->flags & FILE_DEDUPE_RANGE_REPORT_PROGRESS) &&
> + !deduped && len)
> + info->status = -EINVAL;
Thinking about this (deduped == 0 && len > 0) case some more -- the
filesystem didn't return EBADE (aka FILE_DEDUPE_RANGE_DIFFERS), which
means that len wasn't long enough for the filesystem to do any
interesting work. For example, if you try to dedupe two 65 byte files
when the blocksize is 4k, the generic_remap_check* functions can change
65 to 0, and that's what we get here.
Seeing as we've now a new flag to play with, do we really need to return
EINVAL here? Only new code will use the new flag, so we don't have to
worry about old code; and the flag has already established that
info->bytes_deduped is the quantity of deduplication achieved, *not* the
amount by which to bump the source/dest offset for the next call. So
passing out zero here should be ok.
Hmm.
Waitaminute, we have *two* output fields -- status and bytes_deduped.
There are three different cases that I can see:
1) If an operational error occurs, we set info->status to the negative
error number and bytes_deduped is 0.
2) If the file ranges starting at src_offset/dest_offset can be
deduplicated, we set info->status to FILE_DEDUPE_RANGE_SAME and set
info->bytes_deduped to the number of bytes that were deduplicated.
3) If the file ranges cannot be deduplicated, we set info->status to
FILE_DEDUPE_RANGE_DIFFERS and ... do nothing with info->bytes_deduped,
so it remains 0.
In cases 2 and 3, userspace is likely to want to try again with any
remaining file range.
For case 2 this is trivial: add bytes_deduped to src_offset and
dest_offset; subtract it from src_length; and call the kernel again.
In other words, the kernel replies "ok I dedupe'd 72k of data" and the
program advances the file offsets by 72k and asks the kernel to try
again.
For case 3 there isn't currently any obvious way for the ioctl code to
communicate how far userspace should advance the file offsets before
calling again. Most likely (since only blocks can be remapped) that
quantity is i_blocksize, but then userspace has to figure out what that
is -- is it statvfs' f_blocksize? Or statx' stx_blksize?
What if we just did that work for userspace?
if (deduped == -EBADE) {
info->status = FILE_DEDUPE_RANGE_DIFFERS;
if (same->flags & FILE_DEDUPE_RANGE_REPORT_PROGRESS)
info->bytes_deduped = i_blocksize(src);
} else if (deduped < 0) {
info->status = deduped;
} else if (same->flags & FILE_DEDUPE_RANGE_REPORT_PROGRESS)
info->bytes_deduped = deduped;
} else {
info->bytes_deduped = len;
}
And then the manpage can say:
FILE_DEDUPE_RANGE_REPORT_PROGRESS
If this flag is set, the output value of status and
bytes_deduped are redefined as follows:
If an operational error occurred, status contains the negative
error number. bytes_deduped field is set to zero.
If status is FILE_DEDUPE_RANGE_DIFFERS, this is the number of
bytes that were examined but could not be deduplicated.
If status is FILE_DEDUPE_RANGE_SAME, this is the number of bytes
that were successfully deduplicated at the start of the provided
file ranges.
The intent here is that a userspace program could use
bytes_deduplicated to advance the input file ranges in
preparation for another kernel call. In both cases, a zero
value for bytes_deduped means that no further work is possible.
What do you think of that?
> + else if (same->flags & FILE_DEDUPE_RANGE_REPORT_PROGRESS)
> + info->bytes_deduped = deduped;
> else
> info->bytes_deduped = len;
>
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index bd87262f2e349..abee40359bfd5 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -179,12 +179,14 @@ struct file_dedupe_range_info {
> };
>
> /* from struct btrfs_ioctl_file_extent_same_args */
> +#define FILE_DEDUPE_RANGE_REPORT_PROGRESS 0x1
Nitpicking: This should be (1U << 0), not 0x1, because 0x1 is treated as
a signed int and the flags field is unsigned.
--D
> +
> struct file_dedupe_range {
> __u64 src_offset; /* in - start of extent in source */
> __u64 src_length; /* in - length of extent */
> __u16 dest_count; /* in - total elements in info array */
> __u16 reserved1; /* must be zero */
> - __u32 reserved2; /* must be zero */
> + __u32 flags; /* FILE_DEDUPE_RANGE_* flags; was reserved2 */
> struct file_dedupe_range_info info[];
> };
>
> --
> 2.55.0
>
>
next prev parent reply other threads:[~2026-08-14 16:16 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 7:14 [PATCH 0/2] vfs: report truthful FIDEDUPERANGE progress safely Matthias Goergens
2026-08-05 7:14 ` [PATCH 1/2] vfs: fail dedupe requests that cannot make progress Matthias Goergens
2026-08-05 7:14 ` [PATCH 2/2] vfs: report the amount of bytes actually deduplicated Matthias Goergens
2026-08-12 7:46 ` [PATCH 0/2] vfs: report truthful FIDEDUPERANGE progress safely Christian Brauner
2026-08-13 15:33 ` Amir Goldstein
2026-08-13 20:09 ` Darrick J. Wong
2026-08-14 8:23 ` [PATCH v2 0/1] vfs: add FILE_DEDUPE_RANGE_REPORT_PROGRESS flag to FIDEDUPERANGE Matthias Goergens
2026-08-14 8:23 ` [PATCH v2] " Matthias Goergens
2026-08-14 16:16 ` Darrick J. Wong [this message]
2026-08-17 11:56 ` [PATCH v3 0/1] " Matthias Goergens
2026-08-17 11:56 ` [PATCH v3] " Matthias Goergens
2026-08-18 8:50 ` Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260814161657.GN3560084@frogsfrogsfrogs \
--to=djwong@kernel.org \
--cc=amir73il@gmail.com \
--cc=ansgar.loesser@kom.tu-darmstadt.de \
--cc=brauner@kernel.org \
--cc=david@fromorbit.com \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=matthias.goergens@gmail.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®