36
36
#include "kernel-lib/list.h"
37
37
#include "kernel-lib/sizes.h"
38
38
#include "kernel-lib/list_sort.h"
39
+ #include "kernel-lib/overflow.h"
39
40
#include "kernel-shared/ctree.h"
40
41
#include "kernel-shared/compression.h"
41
42
#include "kernel-shared/volumes.h"
@@ -903,6 +904,7 @@ static const char * const cmd_filesystem_defrag_usage[] = {
903
904
OPTLINE ("-s start" , "defragment only from byte onward" ),
904
905
OPTLINE ("-l len" , "defragment only up to len bytes" ),
905
906
OPTLINE ("-t size" , "target extent size hint (default: 32M)" ),
907
+ OPTLINE ("--step SIZE" , "process the range in given steps, flush after each one" ),
906
908
OPTLINE ("-v" , "deprecated, alias for global -v option" ),
907
909
HELPINFO_INSERT_GLOBALS ,
908
910
HELPINFO_INSERT_VERBOSE ,
@@ -916,6 +918,48 @@ static const char * const cmd_filesystem_defrag_usage[] = {
916
918
917
919
static struct btrfs_ioctl_defrag_range_args defrag_global_range ;
918
920
static int defrag_global_errors ;
921
+ static u64 defrag_global_step ;
922
+
923
+ static int defrag_range_in_steps (int fd , const struct stat * st ) {
924
+ int ret = 0 ;
925
+ u64 end ;
926
+ struct btrfs_ioctl_defrag_range_args range ;
927
+
928
+ if (defrag_global_step == 0 )
929
+ return ioctl (fd , BTRFS_IOC_DEFRAG_RANGE , & defrag_global_range );
930
+
931
+ /*
932
+ * If start is set but length is not within or beyond the u64 range,
933
+ * assume it's the rest of the range.
934
+ */
935
+ if (check_add_overflow (defrag_global_range .start , defrag_global_range .len , & end ))
936
+ end = (u64 )- 1 ;
937
+
938
+ range = defrag_global_range ;
939
+ range .flags |= BTRFS_DEFRAG_RANGE_START_IO ;
940
+ while (range .start < end ) {
941
+ u64 start ;
942
+
943
+ range .len = defrag_global_step ;
944
+ pr_verbose (LOG_VERBOSE , "defrag range step: start=%llu len=%llu step=%llu\n" ,
945
+ range .start , range .len , defrag_global_step );
946
+ ret = ioctl (fd , BTRFS_IOC_DEFRAG_RANGE , & range );
947
+ if (ret < 0 )
948
+ return ret ;
949
+ if (check_add_overflow (range .start , defrag_global_step , & start ))
950
+ break ;
951
+ range .start = start ;
952
+ /*
953
+ * Avoid -EINVAL when starting the next ioctl, this can still
954
+ * happen if the file size changes since the time of stat().
955
+ */
956
+ if (start >= (u64 )st -> st_size )
957
+ break ;
958
+ }
959
+
960
+ return ret ;
961
+ }
962
+
919
963
static int defrag_callback (const char * fpath , const struct stat * sb ,
920
964
int typeflag , struct FTW * ftwbuf )
921
965
{
@@ -928,7 +972,7 @@ static int defrag_callback(const char *fpath, const struct stat *sb,
928
972
if (fd < 0 ) {
929
973
goto error ;
930
974
}
931
- ret = ioctl (fd , BTRFS_IOC_DEFRAG_RANGE , & defrag_global_range );
975
+ ret = defrag_range_in_steps (fd , sb );
932
976
close (fd );
933
977
if (ret && errno == ENOTTY ) {
934
978
error (
@@ -994,7 +1038,14 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
994
1038
defrag_global_errors = 0 ;
995
1039
optind = 0 ;
996
1040
while (1 ) {
997
- int c = getopt (argc , argv , "vrc::fs:l:t:" );
1041
+ enum { GETOPT_VAL_STEP = GETOPT_VAL_FIRST };
1042
+ static const struct option long_options [] = {
1043
+ { "step" , required_argument , NULL , GETOPT_VAL_STEP },
1044
+ { NULL , 0 , NULL , 0 }
1045
+ };
1046
+ int c ;
1047
+
1048
+ c = getopt_long (argc , argv , "vrc::fs:l:t:" , long_options , NULL );
998
1049
if (c < 0 )
999
1050
break ;
1000
1051
@@ -1031,6 +1082,14 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
1031
1082
case 'r' :
1032
1083
recursive = true;
1033
1084
break ;
1085
+ case GETOPT_VAL_STEP :
1086
+ defrag_global_step = parse_size_from_string (optarg );
1087
+ if (defrag_global_step < SZ_256K ) {
1088
+ warning ("step %llu too small, adjusting to 256KiB\n" ,
1089
+ defrag_global_step );
1090
+ defrag_global_step = SZ_256K ;
1091
+ }
1092
+ break ;
1034
1093
default :
1035
1094
usage_unknown_option (cmd , argv );
1036
1095
}
@@ -1112,8 +1171,7 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
1112
1171
ret = 0 ;
1113
1172
} else {
1114
1173
pr_verbose (LOG_INFO , "%s\n" , argv [i ]);
1115
- ret = ioctl (fd , BTRFS_IOC_DEFRAG_RANGE ,
1116
- & defrag_global_range );
1174
+ ret = defrag_range_in_steps (fd , & st );
1117
1175
defrag_err = errno ;
1118
1176
if (ret && defrag_err == ENOTTY ) {
1119
1177
error (
0 commit comments