diff mbox series

btrfs-progs: fi defrag: allow passing compression levels

Message ID 20250304172712.573328-1-neelx@suse.com (mailing list archive)
State New
Headers show
Series btrfs-progs: fi defrag: allow passing compression levels | expand

Commit Message

Daniel Vacek March 4, 2025, 5:27 p.m. UTC
zlib and zstd compression methods support using compression levels.
Enable defrag to pass them to kernel.

Signed-off-by: Daniel Vacek <neelx@suse.com>
---
 Documentation/ch-compression.rst | 10 +++---
 cmds/filesystem.c                | 55 ++++++++++++++++++++++++++++++--
 kernel-shared/uapi/btrfs.h       | 10 +++++-
 libbtrfs/ioctl.h                 |  1 +
 libbtrfsutil/btrfs.h             | 12 +++++--
 5 files changed, 77 insertions(+), 11 deletions(-)

Comments

Qu Wenruo March 4, 2025, 9:40 p.m. UTC | #1
在 2025/3/5 03:57, Daniel Vacek 写道:
> zlib and zstd compression methods support using compression levels.
> Enable defrag to pass them to kernel.
>
> Signed-off-by: Daniel Vacek <neelx@suse.com>
> ---
>   Documentation/ch-compression.rst | 10 +++---
>   cmds/filesystem.c                | 55 ++++++++++++++++++++++++++++++--
>   kernel-shared/uapi/btrfs.h       | 10 +++++-
>   libbtrfs/ioctl.h                 |  1 +
>   libbtrfsutil/btrfs.h             | 12 +++++--
>   5 files changed, 77 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/ch-compression.rst b/Documentation/ch-compression.rst
> index a9ec8f1e..f7cdda86 100644
> --- a/Documentation/ch-compression.rst
> +++ b/Documentation/ch-compression.rst
> @@ -25,8 +25,8 @@ LZO
>           * good backward compatibility
>   ZSTD
>           * compression comparable to ZLIB with higher compression/decompression speeds and different ratio
> -        * levels: 1 to 15, mapped directly (higher levels are not available)
> -        * since 4.14, levels since 5.1
> +        * levels: -15 to 15, mapped directly, default is 3
> +        * since 4.14, levels 1 to 15 since 5.1, -15 to -1 since 6.15

I believe the doc updates can be a separate patch since it's not related
to the new defrag ioctl flag.

>
>   The differences depend on the actual data set and cannot be expressed by a
>   single number or recommendation. Higher levels consume more CPU time and may
> @@ -78,7 +78,7 @@ Compression levels
>
>   The level support of ZLIB has been added in v4.14, LZO does not support levels
>   (the kernel implementation provides only one), ZSTD level support has been added
> -in v5.1.
> +in v5.1 and negative levels since v6.15.
>
>   There are 9 levels of ZLIB supported (1 to 9), mapping 1:1 from the mount option
>   to the algorithm defined level. The default is level 3, which provides the
> @@ -86,8 +86,8 @@ reasonably good compression ratio and is still reasonably fast. The difference
>   in compression gain of levels 7, 8 and 9 is comparable but the higher levels
>   take longer.
>
> -The ZSTD support includes levels 1 to 15, a subset of full range of what ZSTD
> -provides. Levels 1-3 are real-time, 4-8 slower with improved compression and
> +The ZSTD support includes levels -15 to 15, a subset of full range of what ZSTD
> +provides. Levels -15-3 are real-time, 4-8 slower with improved compression and
>   9-15 try even harder though the resulting size may not be significantly improved.
>
>   Level 0 always maps to the default. The compression level does not affect
> diff --git a/cmds/filesystem.c b/cmds/filesystem.c
> index d2605bda..f3f93ff7 100644
> --- a/cmds/filesystem.c
> +++ b/cmds/filesystem.c
> @@ -962,6 +962,7 @@ static const char * const cmd_filesystem_defrag_usage[] = {
>   	"",
>   	OPTLINE("-r", "defragment files recursively"),
>   	OPTLINE("-c[zlib,lzo,zstd]", "compress the file while defragmenting, optional parameter (no space in between)"),
> +	OPTLINE("-L|--level level", "use given compression level if enabled (zlib supports levels 1-9, zstd -15-15, and 0 selects the default level)"),
>   	OPTLINE("-f", "flush data to disk immediately after defragmenting"),
>   	OPTLINE("-s start", "defragment only from byte onward"),
>   	OPTLINE("-l len", "defragment only up to len bytes"),
> @@ -1066,6 +1067,7 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
>   	bool recursive = false;
>   	int ret = 0;
>   	int compress_type = BTRFS_COMPRESS_NONE;
> +	int compress_level = 0;
>
>   	/*
>   	 * Kernel 4.19+ supports defragmention of files open read-only,
> @@ -1095,18 +1097,18 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
>   	if (bconf.verbose != BTRFS_BCONF_UNSET)
>   		bconf.verbose++;
>
> -	defrag_global_errors = 0;
>   	defrag_global_errors = 0;
>   	optind = 0;
>   	while(1) {
>   		enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST };
>   		static const struct option long_options[] = {
> +			{ "level", required_argument, NULL, 'L' },
>   			{ "step", required_argument, NULL, GETOPT_VAL_STEP },
>   			{ NULL, 0, NULL, 0 }
>   		};
>   		int c;
>
> -		c = getopt_long(argc, argv, "vrc::fs:l:t:", long_options, NULL);
> +		c = getopt_long(argc, argv, "vrc::L:fs:l:t:", long_options, NULL);
>   		if (c < 0)
>   			break;
>
> @@ -1116,6 +1118,18 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
>   			if (optarg)
>   				compress_type = parse_compress_type_arg(optarg);
>   			break;
> +		case 'L':
> +			/*
> +			 * Do not enforce any limits here, kernel will do itself
> +			 * based on what's supported by the running version.
> +			 * Just clip to the s8 type of the API.
> +			 */
> +			compress_level = atoi(optarg);
> +			if (compress_level < -128)
> +				compress_level = -128;
> +			else if (compress_level > 127)
> +				compress_level = 127;
> +			break;
>   		case 'f':
>   			flush = true;
>   			break;
> @@ -1165,7 +1179,12 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
>   	defrag_global_range.extent_thresh = (u32)thresh;
>   	if (compress_type) {
>   		defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
> -		defrag_global_range.compress_type = compress_type;
> +		if (compress_level) {
> +			defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL;

Since it's a newer flag, the ioctl() may fail with -EOPNOTSUPP on older
kernels, in that case it's better to fall back to flags without the new
COMPRESS_LEVEL one, and try it again. (With some warning showing that
the kernel doesn't support the new flag).

Thanks,
Qu

> +			defrag_global_range.compress.type = compress_type;
> +			defrag_global_range.compress.level= compress_level;
> +		} else
> +			defrag_global_range.compress_type = compress_type;
>   	}
>   	if (flush)
>   		defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
> diff --git a/kernel-shared/uapi/btrfs.h b/kernel-shared/uapi/btrfs.h
> index 6649436c..d2609777 100644
> --- a/kernel-shared/uapi/btrfs.h
> +++ b/kernel-shared/uapi/btrfs.h
> @@ -645,7 +645,9 @@ _static_assert(sizeof(struct btrfs_ioctl_clone_range_args) == 32);
>    */
>   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>   #define BTRFS_DEFRAG_RANGE_START_IO 2
> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>   #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP	(BTRFS_DEFRAG_RANGE_COMPRESS |		\
> +					 BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |	\
>   					 BTRFS_DEFRAG_RANGE_START_IO)
>
>   struct btrfs_ioctl_defrag_range_args {
> @@ -673,7 +675,13 @@ struct btrfs_ioctl_defrag_range_args {
>   	 * for this defrag operation.  If unspecified, zlib will
>   	 * be used
>   	 */
> -	__u32 compress_type;
> +	union {
> +		__u32 compress_type;
> +		struct {
> +			__u8 type;
> +			__s8 level;
> +		} compress;
> +	};
>
>   	/* spare for later */
>   	__u32 unused[4];
> diff --git a/libbtrfs/ioctl.h b/libbtrfs/ioctl.h
> index 7b53a531..08681f2e 100644
> --- a/libbtrfs/ioctl.h
> +++ b/libbtrfs/ioctl.h
> @@ -398,6 +398,7 @@ struct btrfs_ioctl_clone_range_args {
>   /* flags for the defrag range ioctl */
>   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>   #define BTRFS_DEFRAG_RANGE_START_IO 2
> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>
>   #define BTRFS_SAME_DATA_DIFFERS	1
>   /* For extent-same ioctl */
> diff --git a/libbtrfsutil/btrfs.h b/libbtrfsutil/btrfs.h
> index 8e5681c7..ebc9fc2a 100644
> --- a/libbtrfsutil/btrfs.h
> +++ b/libbtrfsutil/btrfs.h
> @@ -608,7 +608,9 @@ struct btrfs_ioctl_clone_range_args {
>    */
>   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>   #define BTRFS_DEFRAG_RANGE_START_IO 2
> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>   #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP	(BTRFS_DEFRAG_RANGE_COMPRESS |		\
> +					 BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |	\
>   					 BTRFS_DEFRAG_RANGE_START_IO)
>
>   struct btrfs_ioctl_defrag_range_args {
> @@ -636,7 +638,13 @@ struct btrfs_ioctl_defrag_range_args {
>   	 * for this defrag operation.  If unspecified, zlib will
>   	 * be used
>   	 */
> -	__u32 compress_type;
> +	union {
> +		__u32 compress_type;
> +		struct {
> +			__u8 type;
> +			__s8 level;
> +		} compress;
> +	};
>
>   	/* spare for later */
>   	__u32 unused[4];
Daniel Vacek March 5, 2025, 7:16 a.m. UTC | #2
On Tue, 4 Mar 2025 at 22:40, Qu Wenruo <quwenruo.btrfs@gmx.com> wrote:
>
>
>
> 在 2025/3/5 03:57, Daniel Vacek 写道:
> > zlib and zstd compression methods support using compression levels.
> > Enable defrag to pass them to kernel.
> >
> > Signed-off-by: Daniel Vacek <neelx@suse.com>
> > ---
> >   Documentation/ch-compression.rst | 10 +++---
> >   cmds/filesystem.c                | 55 ++++++++++++++++++++++++++++++--
> >   kernel-shared/uapi/btrfs.h       | 10 +++++-
> >   libbtrfs/ioctl.h                 |  1 +
> >   libbtrfsutil/btrfs.h             | 12 +++++--
> >   5 files changed, 77 insertions(+), 11 deletions(-)
> >
> > diff --git a/Documentation/ch-compression.rst b/Documentation/ch-compression.rst
> > index a9ec8f1e..f7cdda86 100644
> > --- a/Documentation/ch-compression.rst
> > +++ b/Documentation/ch-compression.rst
> > @@ -25,8 +25,8 @@ LZO
> >           * good backward compatibility
> >   ZSTD
> >           * compression comparable to ZLIB with higher compression/decompression speeds and different ratio
> > -        * levels: 1 to 15, mapped directly (higher levels are not available)
> > -        * since 4.14, levels since 5.1
> > +        * levels: -15 to 15, mapped directly, default is 3
> > +        * since 4.14, levels 1 to 15 since 5.1, -15 to -1 since 6.15
>
> I believe the doc updates can be a separate patch since it's not related
> to the new defrag ioctl flag.

Good point. Thanks.

> >
> >   The differences depend on the actual data set and cannot be expressed by a
> >   single number or recommendation. Higher levels consume more CPU time and may
> > @@ -78,7 +78,7 @@ Compression levels
> >
> >   The level support of ZLIB has been added in v4.14, LZO does not support levels
> >   (the kernel implementation provides only one), ZSTD level support has been added
> > -in v5.1.
> > +in v5.1 and negative levels since v6.15.
> >
> >   There are 9 levels of ZLIB supported (1 to 9), mapping 1:1 from the mount option
> >   to the algorithm defined level. The default is level 3, which provides the
> > @@ -86,8 +86,8 @@ reasonably good compression ratio and is still reasonably fast. The difference
> >   in compression gain of levels 7, 8 and 9 is comparable but the higher levels
> >   take longer.
> >
> > -The ZSTD support includes levels 1 to 15, a subset of full range of what ZSTD
> > -provides. Levels 1-3 are real-time, 4-8 slower with improved compression and
> > +The ZSTD support includes levels -15 to 15, a subset of full range of what ZSTD
> > +provides. Levels -15-3 are real-time, 4-8 slower with improved compression and
> >   9-15 try even harder though the resulting size may not be significantly improved.
> >
> >   Level 0 always maps to the default. The compression level does not affect
> > diff --git a/cmds/filesystem.c b/cmds/filesystem.c
> > index d2605bda..f3f93ff7 100644
> > --- a/cmds/filesystem.c
> > +++ b/cmds/filesystem.c
> > @@ -962,6 +962,7 @@ static const char * const cmd_filesystem_defrag_usage[] = {
> >       "",
> >       OPTLINE("-r", "defragment files recursively"),
> >       OPTLINE("-c[zlib,lzo,zstd]", "compress the file while defragmenting, optional parameter (no space in between)"),
> > +     OPTLINE("-L|--level level", "use given compression level if enabled (zlib supports levels 1-9, zstd -15-15, and 0 selects the default level)"),
> >       OPTLINE("-f", "flush data to disk immediately after defragmenting"),
> >       OPTLINE("-s start", "defragment only from byte onward"),
> >       OPTLINE("-l len", "defragment only up to len bytes"),
> > @@ -1066,6 +1067,7 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
> >       bool recursive = false;
> >       int ret = 0;
> >       int compress_type = BTRFS_COMPRESS_NONE;
> > +     int compress_level = 0;
> >
> >       /*
> >        * Kernel 4.19+ supports defragmention of files open read-only,
> > @@ -1095,18 +1097,18 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
> >       if (bconf.verbose != BTRFS_BCONF_UNSET)
> >               bconf.verbose++;
> >
> > -     defrag_global_errors = 0;
> >       defrag_global_errors = 0;
> >       optind = 0;
> >       while(1) {
> >               enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST };
> >               static const struct option long_options[] = {
> > +                     { "level", required_argument, NULL, 'L' },
> >                       { "step", required_argument, NULL, GETOPT_VAL_STEP },
> >                       { NULL, 0, NULL, 0 }
> >               };
> >               int c;
> >
> > -             c = getopt_long(argc, argv, "vrc::fs:l:t:", long_options, NULL);
> > +             c = getopt_long(argc, argv, "vrc::L:fs:l:t:", long_options, NULL);
> >               if (c < 0)
> >                       break;
> >
> > @@ -1116,6 +1118,18 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
> >                       if (optarg)
> >                               compress_type = parse_compress_type_arg(optarg);
> >                       break;
> > +             case 'L':
> > +                     /*
> > +                      * Do not enforce any limits here, kernel will do itself
> > +                      * based on what's supported by the running version.
> > +                      * Just clip to the s8 type of the API.
> > +                      */
> > +                     compress_level = atoi(optarg);
> > +                     if (compress_level < -128)
> > +                             compress_level = -128;
> > +                     else if (compress_level > 127)
> > +                             compress_level = 127;
> > +                     break;
> >               case 'f':
> >                       flush = true;
> >                       break;
> > @@ -1165,7 +1179,12 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
> >       defrag_global_range.extent_thresh = (u32)thresh;
> >       if (compress_type) {
> >               defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
> > -             defrag_global_range.compress_type = compress_type;
> > +             if (compress_level) {
> > +                     defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL;
>
> Since it's a newer flag, the ioctl() may fail with -EOPNOTSUPP on older
> kernels, in that case it's better to fall back to flags without the new
> COMPRESS_LEVEL one, and try it again. (With some warning showing that
> the kernel doesn't support the new flag).

Well, we don't know if the user really wants to proceed without the
specified level. This way the defrag fails and the user can rerun
without that option if he's fine using the default level.

Ie. Do we really want to do something different than what was asked on
the command line? Let me know and I'll change that but this looks
rigorous to me.

> Thanks,
> Qu
>
> > +                     defrag_global_range.compress.type = compress_type;
> > +                     defrag_global_range.compress.level= compress_level;
> > +             } else
> > +                     defrag_global_range.compress_type = compress_type;
> >       }
> >       if (flush)
> >               defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
> > diff --git a/kernel-shared/uapi/btrfs.h b/kernel-shared/uapi/btrfs.h
> > index 6649436c..d2609777 100644
> > --- a/kernel-shared/uapi/btrfs.h
> > +++ b/kernel-shared/uapi/btrfs.h
> > @@ -645,7 +645,9 @@ _static_assert(sizeof(struct btrfs_ioctl_clone_range_args) == 32);
> >    */
> >   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
> >   #define BTRFS_DEFRAG_RANGE_START_IO 2
> > +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
> >   #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP       (BTRFS_DEFRAG_RANGE_COMPRESS |          \
> > +                                      BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |    \
> >                                        BTRFS_DEFRAG_RANGE_START_IO)
> >
> >   struct btrfs_ioctl_defrag_range_args {
> > @@ -673,7 +675,13 @@ struct btrfs_ioctl_defrag_range_args {
> >        * for this defrag operation.  If unspecified, zlib will
> >        * be used
> >        */
> > -     __u32 compress_type;
> > +     union {
> > +             __u32 compress_type;
> > +             struct {
> > +                     __u8 type;
> > +                     __s8 level;
> > +             } compress;
> > +     };
> >
> >       /* spare for later */
> >       __u32 unused[4];
> > diff --git a/libbtrfs/ioctl.h b/libbtrfs/ioctl.h
> > index 7b53a531..08681f2e 100644
> > --- a/libbtrfs/ioctl.h
> > +++ b/libbtrfs/ioctl.h
> > @@ -398,6 +398,7 @@ struct btrfs_ioctl_clone_range_args {
> >   /* flags for the defrag range ioctl */
> >   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
> >   #define BTRFS_DEFRAG_RANGE_START_IO 2
> > +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
> >
> >   #define BTRFS_SAME_DATA_DIFFERS     1
> >   /* For extent-same ioctl */
> > diff --git a/libbtrfsutil/btrfs.h b/libbtrfsutil/btrfs.h
> > index 8e5681c7..ebc9fc2a 100644
> > --- a/libbtrfsutil/btrfs.h
> > +++ b/libbtrfsutil/btrfs.h
> > @@ -608,7 +608,9 @@ struct btrfs_ioctl_clone_range_args {
> >    */
> >   #define BTRFS_DEFRAG_RANGE_COMPRESS 1
> >   #define BTRFS_DEFRAG_RANGE_START_IO 2
> > +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
> >   #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP       (BTRFS_DEFRAG_RANGE_COMPRESS |          \
> > +                                      BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |    \
> >                                        BTRFS_DEFRAG_RANGE_START_IO)
> >
> >   struct btrfs_ioctl_defrag_range_args {
> > @@ -636,7 +638,13 @@ struct btrfs_ioctl_defrag_range_args {
> >        * for this defrag operation.  If unspecified, zlib will
> >        * be used
> >        */
> > -     __u32 compress_type;
> > +     union {
> > +             __u32 compress_type;
> > +             struct {
> > +                     __u8 type;
> > +                     __s8 level;
> > +             } compress;
> > +     };
> >
> >       /* spare for later */
> >       __u32 unused[4];
>
Qu Wenruo March 5, 2025, 7:46 a.m. UTC | #3
在 2025/3/5 17:46, Daniel Vacek 写道:
> On Tue, 4 Mar 2025 at 22:40, Qu Wenruo <quwenruo.btrfs@gmx.com> wrote:
[...]
>>>                defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
>>> -             defrag_global_range.compress_type = compress_type;
>>> +             if (compress_level) {
>>> +                     defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL;
>>
>> Since it's a newer flag, the ioctl() may fail with -EOPNOTSUPP on older
>> kernels, in that case it's better to fall back to flags without the new
>> COMPRESS_LEVEL one, and try it again. (With some warning showing that
>> the kernel doesn't support the new flag).
> 
> Well, we don't know if the user really wants to proceed without the
> specified level. This way the defrag fails and the user can rerun
> without that option if he's fine using the default level.
> 
> Ie. Do we really want to do something different than what was asked on
> the command line? Let me know and I'll change that but this looks
> rigorous to me.

Right, since the level can only be enabled by explicitly specifying the 
--level option, we should not fallback.

With the doc update split out:

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu

> 
>> Thanks,
>> Qu
>>
>>> +                     defrag_global_range.compress.type = compress_type;
>>> +                     defrag_global_range.compress.level= compress_level;
>>> +             } else
>>> +                     defrag_global_range.compress_type = compress_type;
>>>        }
>>>        if (flush)
>>>                defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
>>> diff --git a/kernel-shared/uapi/btrfs.h b/kernel-shared/uapi/btrfs.h
>>> index 6649436c..d2609777 100644
>>> --- a/kernel-shared/uapi/btrfs.h
>>> +++ b/kernel-shared/uapi/btrfs.h
>>> @@ -645,7 +645,9 @@ _static_assert(sizeof(struct btrfs_ioctl_clone_range_args) == 32);
>>>     */
>>>    #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>>>    #define BTRFS_DEFRAG_RANGE_START_IO 2
>>> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>>>    #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP       (BTRFS_DEFRAG_RANGE_COMPRESS |          \
>>> +                                      BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |    \
>>>                                         BTRFS_DEFRAG_RANGE_START_IO)
>>>
>>>    struct btrfs_ioctl_defrag_range_args {
>>> @@ -673,7 +675,13 @@ struct btrfs_ioctl_defrag_range_args {
>>>         * for this defrag operation.  If unspecified, zlib will
>>>         * be used
>>>         */
>>> -     __u32 compress_type;
>>> +     union {
>>> +             __u32 compress_type;
>>> +             struct {
>>> +                     __u8 type;
>>> +                     __s8 level;
>>> +             } compress;
>>> +     };
>>>
>>>        /* spare for later */
>>>        __u32 unused[4];
>>> diff --git a/libbtrfs/ioctl.h b/libbtrfs/ioctl.h
>>> index 7b53a531..08681f2e 100644
>>> --- a/libbtrfs/ioctl.h
>>> +++ b/libbtrfs/ioctl.h
>>> @@ -398,6 +398,7 @@ struct btrfs_ioctl_clone_range_args {
>>>    /* flags for the defrag range ioctl */
>>>    #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>>>    #define BTRFS_DEFRAG_RANGE_START_IO 2
>>> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>>>
>>>    #define BTRFS_SAME_DATA_DIFFERS     1
>>>    /* For extent-same ioctl */
>>> diff --git a/libbtrfsutil/btrfs.h b/libbtrfsutil/btrfs.h
>>> index 8e5681c7..ebc9fc2a 100644
>>> --- a/libbtrfsutil/btrfs.h
>>> +++ b/libbtrfsutil/btrfs.h
>>> @@ -608,7 +608,9 @@ struct btrfs_ioctl_clone_range_args {
>>>     */
>>>    #define BTRFS_DEFRAG_RANGE_COMPRESS 1
>>>    #define BTRFS_DEFRAG_RANGE_START_IO 2
>>> +#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
>>>    #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP       (BTRFS_DEFRAG_RANGE_COMPRESS |          \
>>> +                                      BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |    \
>>>                                         BTRFS_DEFRAG_RANGE_START_IO)
>>>
>>>    struct btrfs_ioctl_defrag_range_args {
>>> @@ -636,7 +638,13 @@ struct btrfs_ioctl_defrag_range_args {
>>>         * for this defrag operation.  If unspecified, zlib will
>>>         * be used
>>>         */
>>> -     __u32 compress_type;
>>> +     union {
>>> +             __u32 compress_type;
>>> +             struct {
>>> +                     __u8 type;
>>> +                     __s8 level;
>>> +             } compress;
>>> +     };
>>>
>>>        /* spare for later */
>>>        __u32 unused[4];
>>
>
David Sterba March 5, 2025, 10:24 a.m. UTC | #4
On Wed, Mar 05, 2025 at 08:10:24AM +1030, Qu Wenruo wrote:
> 
> 
> 在 2025/3/5 03:57, Daniel Vacek 写道:
> > zlib and zstd compression methods support using compression levels.
> > Enable defrag to pass them to kernel.
> >
> > Signed-off-by: Daniel Vacek <neelx@suse.com>
> > ---
> >   Documentation/ch-compression.rst | 10 +++---
> >   cmds/filesystem.c                | 55 ++++++++++++++++++++++++++++++--
> >   kernel-shared/uapi/btrfs.h       | 10 +++++-
> >   libbtrfs/ioctl.h                 |  1 +
> >   libbtrfsutil/btrfs.h             | 12 +++++--
> >   5 files changed, 77 insertions(+), 11 deletions(-)
> >
> > diff --git a/Documentation/ch-compression.rst b/Documentation/ch-compression.rst
> > index a9ec8f1e..f7cdda86 100644
> > --- a/Documentation/ch-compression.rst
> > +++ b/Documentation/ch-compression.rst
> > @@ -25,8 +25,8 @@ LZO
> >           * good backward compatibility
> >   ZSTD
> >           * compression comparable to ZLIB with higher compression/decompression speeds and different ratio
> > -        * levels: 1 to 15, mapped directly (higher levels are not available)
> > -        * since 4.14, levels since 5.1
> > +        * levels: -15 to 15, mapped directly, default is 3
> > +        * since 4.14, levels 1 to 15 since 5.1, -15 to -1 since 6.15
> 
> I believe the doc updates can be a separate patch since it's not related
> to the new defrag ioctl flag.

In progs I don't mind either way, included in the functional change or
separate.
Daniel Vacek March 5, 2025, 10:47 a.m. UTC | #5
On Wed, 5 Mar 2025 at 11:25, David Sterba <dsterba@suse.cz> wrote:
>
> On Wed, Mar 05, 2025 at 08:10:24AM +1030, Qu Wenruo wrote:
> >
> >
> > 在 2025/3/5 03:57, Daniel Vacek 写道:
> > > zlib and zstd compression methods support using compression levels.
> > > Enable defrag to pass them to kernel.
> > >
> > > Signed-off-by: Daniel Vacek <neelx@suse.com>
> > > ---
> > >   Documentation/ch-compression.rst | 10 +++---
> > >   cmds/filesystem.c                | 55 ++++++++++++++++++++++++++++++--
> > >   kernel-shared/uapi/btrfs.h       | 10 +++++-
> > >   libbtrfs/ioctl.h                 |  1 +
> > >   libbtrfsutil/btrfs.h             | 12 +++++--
> > >   5 files changed, 77 insertions(+), 11 deletions(-)
> > >
> > > diff --git a/Documentation/ch-compression.rst b/Documentation/ch-compression.rst
> > > index a9ec8f1e..f7cdda86 100644
> > > --- a/Documentation/ch-compression.rst
> > > +++ b/Documentation/ch-compression.rst
> > > @@ -25,8 +25,8 @@ LZO
> > >           * good backward compatibility
> > >   ZSTD
> > >           * compression comparable to ZLIB with higher compression/decompression speeds and different ratio
> > > -        * levels: 1 to 15, mapped directly (higher levels are not available)
> > > -        * since 4.14, levels since 5.1
> > > +        * levels: -15 to 15, mapped directly, default is 3
> > > +        * since 4.14, levels 1 to 15 since 5.1, -15 to -1 since 6.15
> >
> > I believe the doc updates can be a separate patch since it's not related
> > to the new defrag ioctl flag.
>
> In progs I don't mind either way, included in the functional change or
> separate.

Next time I'll split them. But if you're happy with it as it is,
there's no need for v2 I guess.
diff mbox series

Patch

diff --git a/Documentation/ch-compression.rst b/Documentation/ch-compression.rst
index a9ec8f1e..f7cdda86 100644
--- a/Documentation/ch-compression.rst
+++ b/Documentation/ch-compression.rst
@@ -25,8 +25,8 @@  LZO
         * good backward compatibility
 ZSTD
         * compression comparable to ZLIB with higher compression/decompression speeds and different ratio
-        * levels: 1 to 15, mapped directly (higher levels are not available)
-        * since 4.14, levels since 5.1
+        * levels: -15 to 15, mapped directly, default is 3
+        * since 4.14, levels 1 to 15 since 5.1, -15 to -1 since 6.15
 
 The differences depend on the actual data set and cannot be expressed by a
 single number or recommendation. Higher levels consume more CPU time and may
@@ -78,7 +78,7 @@  Compression levels
 
 The level support of ZLIB has been added in v4.14, LZO does not support levels
 (the kernel implementation provides only one), ZSTD level support has been added
-in v5.1.
+in v5.1 and negative levels since v6.15.
 
 There are 9 levels of ZLIB supported (1 to 9), mapping 1:1 from the mount option
 to the algorithm defined level. The default is level 3, which provides the
@@ -86,8 +86,8 @@  reasonably good compression ratio and is still reasonably fast. The difference
 in compression gain of levels 7, 8 and 9 is comparable but the higher levels
 take longer.
 
-The ZSTD support includes levels 1 to 15, a subset of full range of what ZSTD
-provides. Levels 1-3 are real-time, 4-8 slower with improved compression and
+The ZSTD support includes levels -15 to 15, a subset of full range of what ZSTD
+provides. Levels -15-3 are real-time, 4-8 slower with improved compression and
 9-15 try even harder though the resulting size may not be significantly improved.
 
 Level 0 always maps to the default. The compression level does not affect
diff --git a/cmds/filesystem.c b/cmds/filesystem.c
index d2605bda..f3f93ff7 100644
--- a/cmds/filesystem.c
+++ b/cmds/filesystem.c
@@ -962,6 +962,7 @@  static const char * const cmd_filesystem_defrag_usage[] = {
 	"",
 	OPTLINE("-r", "defragment files recursively"),
 	OPTLINE("-c[zlib,lzo,zstd]", "compress the file while defragmenting, optional parameter (no space in between)"),
+	OPTLINE("-L|--level level", "use given compression level if enabled (zlib supports levels 1-9, zstd -15-15, and 0 selects the default level)"),
 	OPTLINE("-f", "flush data to disk immediately after defragmenting"),
 	OPTLINE("-s start", "defragment only from byte onward"),
 	OPTLINE("-l len", "defragment only up to len bytes"),
@@ -1066,6 +1067,7 @@  static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
 	bool recursive = false;
 	int ret = 0;
 	int compress_type = BTRFS_COMPRESS_NONE;
+	int compress_level = 0;
 
 	/*
 	 * Kernel 4.19+ supports defragmention of files open read-only,
@@ -1095,18 +1097,18 @@  static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
 	if (bconf.verbose != BTRFS_BCONF_UNSET)
 		bconf.verbose++;
 
-	defrag_global_errors = 0;
 	defrag_global_errors = 0;
 	optind = 0;
 	while(1) {
 		enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST };
 		static const struct option long_options[] = {
+			{ "level", required_argument, NULL, 'L' },
 			{ "step", required_argument, NULL, GETOPT_VAL_STEP },
 			{ NULL, 0, NULL, 0 }
 		};
 		int c;
 
-		c = getopt_long(argc, argv, "vrc::fs:l:t:", long_options, NULL);
+		c = getopt_long(argc, argv, "vrc::L:fs:l:t:", long_options, NULL);
 		if (c < 0)
 			break;
 
@@ -1116,6 +1118,18 @@  static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
 			if (optarg)
 				compress_type = parse_compress_type_arg(optarg);
 			break;
+		case 'L':
+			/*
+			 * Do not enforce any limits here, kernel will do itself
+			 * based on what's supported by the running version.
+			 * Just clip to the s8 type of the API.
+			 */
+			compress_level = atoi(optarg);
+			if (compress_level < -128)
+				compress_level = -128;
+			else if (compress_level > 127)
+				compress_level = 127;
+			break;
 		case 'f':
 			flush = true;
 			break;
@@ -1165,7 +1179,12 @@  static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
 	defrag_global_range.extent_thresh = (u32)thresh;
 	if (compress_type) {
 		defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS;
-		defrag_global_range.compress_type = compress_type;
+		if (compress_level) {
+			defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL;
+			defrag_global_range.compress.type = compress_type;
+			defrag_global_range.compress.level= compress_level;
+		} else
+			defrag_global_range.compress_type = compress_type;
 	}
 	if (flush)
 		defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO;
diff --git a/kernel-shared/uapi/btrfs.h b/kernel-shared/uapi/btrfs.h
index 6649436c..d2609777 100644
--- a/kernel-shared/uapi/btrfs.h
+++ b/kernel-shared/uapi/btrfs.h
@@ -645,7 +645,9 @@  _static_assert(sizeof(struct btrfs_ioctl_clone_range_args) == 32);
  */
 #define BTRFS_DEFRAG_RANGE_COMPRESS 1
 #define BTRFS_DEFRAG_RANGE_START_IO 2
+#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
 #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP	(BTRFS_DEFRAG_RANGE_COMPRESS |		\
+					 BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |	\
 					 BTRFS_DEFRAG_RANGE_START_IO)
 
 struct btrfs_ioctl_defrag_range_args {
@@ -673,7 +675,13 @@  struct btrfs_ioctl_defrag_range_args {
 	 * for this defrag operation.  If unspecified, zlib will
 	 * be used
 	 */
-	__u32 compress_type;
+	union {
+		__u32 compress_type;
+		struct {
+			__u8 type;
+			__s8 level;
+		} compress;
+	};
 
 	/* spare for later */
 	__u32 unused[4];
diff --git a/libbtrfs/ioctl.h b/libbtrfs/ioctl.h
index 7b53a531..08681f2e 100644
--- a/libbtrfs/ioctl.h
+++ b/libbtrfs/ioctl.h
@@ -398,6 +398,7 @@  struct btrfs_ioctl_clone_range_args {
 /* flags for the defrag range ioctl */
 #define BTRFS_DEFRAG_RANGE_COMPRESS 1
 #define BTRFS_DEFRAG_RANGE_START_IO 2
+#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
 
 #define BTRFS_SAME_DATA_DIFFERS	1
 /* For extent-same ioctl */
diff --git a/libbtrfsutil/btrfs.h b/libbtrfsutil/btrfs.h
index 8e5681c7..ebc9fc2a 100644
--- a/libbtrfsutil/btrfs.h
+++ b/libbtrfsutil/btrfs.h
@@ -608,7 +608,9 @@  struct btrfs_ioctl_clone_range_args {
  */
 #define BTRFS_DEFRAG_RANGE_COMPRESS 1
 #define BTRFS_DEFRAG_RANGE_START_IO 2
+#define BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL 4
 #define BTRFS_DEFRAG_RANGE_FLAGS_SUPP	(BTRFS_DEFRAG_RANGE_COMPRESS |		\
+					 BTRFS_DEFRAG_RANGE_COMPRESS_LEVEL |	\
 					 BTRFS_DEFRAG_RANGE_START_IO)
 
 struct btrfs_ioctl_defrag_range_args {
@@ -636,7 +638,13 @@  struct btrfs_ioctl_defrag_range_args {
 	 * for this defrag operation.  If unspecified, zlib will
 	 * be used
 	 */
-	__u32 compress_type;
+	union {
+		__u32 compress_type;
+		struct {
+			__u8 type;
+			__s8 level;
+		} compress;
+	};
 
 	/* spare for later */
 	__u32 unused[4];