From: Darrick J. Wong <djwong@xxxxxxxxxx> Teach this stress tool to be able to use the file content exchange ioctl. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- ltp/fsstress.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/ltp/fsstress.c b/ltp/fsstress.c index 10608fb554..0fba3d92a0 100644 --- a/ltp/fsstress.c +++ b/ltp/fsstress.c @@ -143,6 +143,7 @@ typedef enum { OP_URING_WRITE, OP_WRITE, OP_WRITEV, + OP_XCHGRANGE, OP_LAST } opty_t; @@ -272,6 +273,8 @@ void uring_read_f(opnum_t, long); void uring_write_f(opnum_t, long); void write_f(opnum_t, long); void writev_f(opnum_t, long); +void xchgrange_f(opnum_t, long); + char *xattr_flag_to_string(int); struct opdesc ops[OP_LAST] = { @@ -340,6 +343,7 @@ struct opdesc ops[OP_LAST] = { [OP_URING_WRITE] = {"uring_write", uring_write_f, 1, 1 }, [OP_WRITE] = {"write", write_f, 4, 1 }, [OP_WRITEV] = {"writev", writev_f, 4, 1 }, + [OP_XCHGRANGE] = {"xchgrange", xchgrange_f, 4, 1 }, }, *ops_end; flist_t flist[FT_nft] = { @@ -2494,6 +2498,170 @@ chown_f(opnum_t opno, long r) free_pathname(&f); } +/* exchange some arbitrary range of f1 to f2...fn. */ +void +xchgrange_f( + opnum_t opno, + long r) +{ +#ifdef FIEXCHANGE_RANGE + struct file_xchg_range fxr = { 0 }; + static __u64 swap_flags = 0; + struct pathname fpath1; + struct pathname fpath2; + struct stat64 stat1; + struct stat64 stat2; + char inoinfo1[1024]; + char inoinfo2[1024]; + off64_t lr; + off64_t off1; + off64_t off2; + off64_t max_off2; + size_t len; + int v1; + int v2; + int fd1; + int fd2; + int ret; + int tries = 0; + int e; + + /* Load paths */ + init_pathname(&fpath1); + if (!get_fname(FT_REGm, r, &fpath1, NULL, NULL, &v1)) { + if (v1) + printf("%d/%lld: xchgrange read - no filename\n", + procid, opno); + goto out_fpath1; + } + + init_pathname(&fpath2); + if (!get_fname(FT_REGm, random(), &fpath2, NULL, NULL, &v2)) { + if (v2) + printf("%d/%lld: xchgrange write - no filename\n", + procid, opno); + goto out_fpath2; + } + + /* Open files */ + fd1 = open_path(&fpath1, O_RDONLY); + e = fd1 < 0 ? errno : 0; + check_cwd(); + if (fd1 < 0) { + if (v1) + printf("%d/%lld: xchgrange read - open %s failed %d\n", + procid, opno, fpath1.path, e); + goto out_fpath2; + } + + fd2 = open_path(&fpath2, O_WRONLY); + e = fd2 < 0 ? errno : 0; + check_cwd(); + if (fd2 < 0) { + if (v2) + printf("%d/%lld: xchgrange write - open %s failed %d\n", + procid, opno, fpath2.path, e); + goto out_fd1; + } + + /* Get file stats */ + if (fstat64(fd1, &stat1) < 0) { + if (v1) + printf("%d/%lld: xchgrange read - fstat64 %s failed %d\n", + procid, opno, fpath1.path, errno); + goto out_fd2; + } + inode_info(inoinfo1, sizeof(inoinfo1), &stat1, v1); + + if (fstat64(fd2, &stat2) < 0) { + if (v2) + printf("%d/%lld: xchgrange write - fstat64 %s failed %d\n", + procid, opno, fpath2.path, errno); + goto out_fd2; + } + inode_info(inoinfo2, sizeof(inoinfo2), &stat2, v2); + + if (stat1.st_size < (stat1.st_blksize * 2) || + stat2.st_size < (stat2.st_blksize * 2)) { + if (v2) + printf("%d/%lld: xchgrange - files are too small\n", + procid, opno); + goto out_fd2; + } + + /* Never let us swap more than 1/4 of the files. */ + len = (random() % FILELEN_MAX) + 1; + if (len > stat1.st_size / 4) + len = stat1.st_size / 4; + if (len > stat2.st_size / 4) + len = stat2.st_size / 4; + len = rounddown_64(len, stat1.st_blksize); + if (len == 0) + len = stat1.st_blksize; + + /* Calculate offsets */ + lr = ((int64_t)random() << 32) + random(); + if (stat1.st_size == len) + off1 = 0; + else + off1 = (off64_t)(lr % MIN(stat1.st_size - len, MAXFSIZE)); + off1 %= maxfsize; + off1 = rounddown_64(off1, stat1.st_blksize); + + /* + * If srcfile == destfile, randomly generate destination ranges + * until we find one that doesn't overlap the source range. + */ + max_off2 = MIN(stat2.st_size - len, MAXFSIZE); + do { + lr = ((int64_t)random() << 32) + random(); + if (stat2.st_size == len) + off2 = 0; + else + off2 = (off64_t)(lr % max_off2); + off2 %= maxfsize; + off2 = rounddown_64(off2, stat2.st_blksize); + } while (stat1.st_ino == stat2.st_ino && + llabs(off2 - off1) < len && + tries++ < 10); + + /* Swap data blocks */ + fxr.file1_fd = fd1; + fxr.file1_offset = off1; + fxr.length = len; + fxr.file2_offset = off2; + fxr.flags = swap_flags; + +retry: + ret = ioctl(fd2, FIEXCHANGE_RANGE, &fxr); + e = ret < 0 ? errno : 0; + if (e == EOPNOTSUPP && !(swap_flags & FILE_XCHG_RANGE_NONATOMIC)) { + swap_flags = FILE_XCHG_RANGE_NONATOMIC; + fxr.flags |= swap_flags; + goto retry; + } + if (v1 || v2) { + printf("%d/%lld: xchgrange %s%s [%lld,%lld] -> %s%s [%lld,%lld]", + procid, opno, + fpath1.path, inoinfo1, (long long)off1, (long long)len, + fpath2.path, inoinfo2, (long long)off2, (long long)len); + + if (ret < 0) + printf(" error %d", e); + printf("\n"); + } + +out_fd2: + close(fd2); +out_fd1: + close(fd1); +out_fpath2: + free_pathname(&fpath2); +out_fpath1: + free_pathname(&fpath1); +#endif +} + /* reflink some arbitrary range of f1 to f2. */ void clonerange_f(