From 5317f8a523ac32b616713396f60eda4995dea85e Mon Sep 17 00:00:00 2001 From: Qian Yingjin Date: Fri, 21 Jul 2023 06:21:11 -0400 Subject: [PATCH] LU-14361 statahead: Add test for statahead advise This patch adds a test program "aheadmany" and sanity/test_123g to verfiy that statahead advise works as expected. Signed-off-by: Qian Yingjin Change-Id: I751313ecb790c66f70a09bf8e9d13846b3c0032d Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/51730 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin Reviewed-by: Lai Siyao Reviewed-by: Andreas Dilger --- lustre/tests/Makefile.am | 2 +- lustre/tests/aheadmany.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++ lustre/tests/sanity.sh | 29 ++++++ 3 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 lustre/tests/aheadmany.c diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index ad340b1..6a1a4c4 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -75,7 +75,7 @@ THETESTS += create_foreign_file parse_foreign_file THETESTS += create_foreign_dir parse_foreign_dir THETESTS += check_fallocate splice-test lseek_test expand_truncate_test THETESTS += foreign_symlink_striping lov_getstripe_old io_uring_probe -THETESTS += fadvise_dontneed_helper llapi_root_test +THETESTS += fadvise_dontneed_helper llapi_root_test aheadmany if LIBAIO THETESTS += aiocp diff --git a/lustre/tests/aheadmany.c b/lustre/tests/aheadmany.c new file mode 100644 index 0000000..d145858 --- /dev/null +++ b/lustre/tests/aheadmany.c @@ -0,0 +1,255 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.gnu.org/licenses/gpl-2.0.html + * + * GPL HEADER END + */ +/* + * Copyright (c) 2023, Whamcloud DDN Storage Corporation. + */ +/* + * Create|Open|Stat|Read ahead. + * This program is mainly used to verify that ahead feature works as + * expected for batch file accesses. + * + * Author: Qian Yingjin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char *progname; + +static void usage(void) +{ + printf("Usage: %s {--iocall|-c [stat|open|create|read]}\n" + "[--start|-s start] [--end|-e end] [--basename NAME]\n" + "[--batch_max|-B] {--dir|-d DIR} dentry ...\n" + "\t--iocall|-c: I/O syscall in a predictive batch access\n" + "\t--start|-s: Start index of file names\n" + "\t--end|-e: End index of file names\n" + "\t--basename|-b:Base name for file naming format\n" + "\t--batch_max|-B: max batch count for ahead operations\n" + "\t--directory|-d: under this directory do ahead operations\n", + progname); + exit(0); +} + +static char *get_file_name(const char *dirpath, const char *basename, long n) +{ + static char filename[PATH_MAX]; + int bytes; + + bytes = snprintf(filename, PATH_MAX - 1, "%s/%s%ld", + dirpath, basename, n); + if (bytes >= PATH_MAX - 1) { + fprintf(stderr, "%s: file name too long\n", progname); + exit(EXIT_FAILURE); + } + + return filename; +} + +static int ll_ahead_by_name_index(const char *dirpath, const char *fname, + enum lu_access_flags flags, __u64 start, + __u64 end, __u32 batch_max) +{ + struct llapi_lu_ladvise2 ladvise; + struct stat st; + int dir_fd; + int rc; + int i; + + dir_fd = open(dirpath, O_DIRECTORY | O_RDONLY); + if (dir_fd < 0) { + rc = -errno; + fprintf(stderr, "%s: failed to open dir '%s': rc = %d\n", + progname, dirpath, rc); + return rc; + } + + ladvise.lla_advice = LU_LADVISE_AHEAD; + ladvise.lla_ahead_mode = LU_AH_NAME_INDEX; + ladvise.lla_access_flags = flags; + ladvise.lla_start = start; + ladvise.lla_end = end; + ladvise.lla_batch_max = batch_max; + strncpy(ladvise.lla_fname, fname, sizeof(ladvise.lla_fname) - 1); + rc = ioctl(dir_fd, LL_IOC_LADVISE2, &ladvise); + if (rc < 0) { + fprintf(stderr, "%s: failed to ahead for '%s': rc = %d\n", + progname, dirpath, rc); + close(dir_fd); + return rc; + } + + for (i = start; i < end; i++) { + char *filename; + + filename = get_file_name(dirpath, fname, i); + rc = stat(filename, &st); + if (rc < 0) { + rc = -errno; + fprintf(stderr, "%s: stat(%s) failed: rc = %d\n", + progname, filename, errno); + break; + } + } + + close(dir_fd); + return rc; +} + +int main(int argc, char **argv) +{ + struct option long_opts[] = { + { .val = 'c', .name = "iocall", .has_arg = required_argument }, + { .val = 's', .name = "start", .has_arg = required_argument }, + { .val = 'e', .name = "end", .has_arg = required_argument }, + { .val = 'b', .name = "basename", .has_arg = required_argument }, + { .val = 'd', .name = "directory", .has_arg = required_argument }, + { .val = 'B', .name = "batch_max", .has_arg = required_argument }, + { .val = 'h', .name = "help", .has_arg = no_argument }, + { .name = NULL } }; + enum lu_access_flags flags = ACCESS_FL_NONE; + enum lu_ahead_mode mode = LU_AH_NAME_INDEX; + __u32 batch_max = 0; + const char *dirpath = NULL; + char *fname = NULL; + __u64 start_index = 0; + __u64 end_index = 0; + char *end; + int rc = 0; + int c; + + progname = basename(argv[0]); + while ((c = getopt_long(argc, argv, "c:s:e:b:d:B:h", + long_opts, NULL)) != -1) { + switch (c) { + case 'c': + if (strcmp(optarg, "stat") == 0) { + flags |= ACCESS_FL_STAT; + } else if (strcmp(optarg, "open") == 0) { + flags |= ACCESS_FL_OPEN; + } else if (strcmp(optarg, "creat") == 0 || + strcmp(optarg, "create") == 0) { + flags |= ACCESS_FL_CREAT; + } else if (strcmp(optarg, "read") == 0) { + flags |= ACCESS_FL_READ; + } else if (strcmp(optarg, "write") == 0) { + flags |= ACCESS_FL_WRITE; + } else { + fprintf(stderr, "%s %s: bad access type '%s'\n", + progname, argv[0], optarg); + return -EINVAL; + } + break; + case 's': + start_index = strtoull(optarg, &end, 0); + if (*end) { + fprintf(stderr, "%s %s: bad start index '%s'\n", + progname, argv[0], optarg); + return -EINVAL; + } + break; + case 'e': + end_index = strtoull(optarg, &end, 0); + if (*end) { + fprintf(stderr, "%s %s: bad start index '%s'\n", + progname, argv[0], optarg); + return -EINVAL; + } + break; + case 'B': + batch_max = strtoul(optarg, &end, 0); + if (*end) { + fprintf(stderr, "%s %s: bad batch count '%s'\n", + progname, argv[0], optarg); + return -EINVAL; + } + break; + case 'b': + fname = optarg; + break; + case 'd': + dirpath = optarg; + break; + default: + fprintf(stderr, "%s: unrecognized option '%s'\n", + progname, argv[optind - 1]); + return -EOPNOTSUPP; + case 'h': + usage(); + } + } + + if (flags == ACCESS_FL_NONE) { + fprintf(stderr, "%s: must specify access mode\n", progname); + return -EINVAL; + } + + if (!dirpath) { + fprintf(stderr, "%s: must specify directory path\n", progname); + return -EINVAL; + } + + if (mode == LU_AH_NAME_INDEX) { + if (!fname) { + fprintf(stderr, "%s: must specify base file name\n", + progname); + return -EINVAL; + } + + if (end_index == 0) { + fprintf(stderr, "%s: must specify end index\n", + progname); + return -EINVAL; + } + + if (flags != ACCESS_FL_STAT) { + fprintf(stderr, "%s: only support stat-ahead\n", + progname); + return -EINVAL; + } + + rc = ll_ahead_by_name_index(dirpath, fname, flags, start_index, + end_index, batch_max); + } else { + rc = -EOPNOTSUPP; + fprintf(stderr, "%s: unsupported ahead type %d\n", + progname, mode); + } + + return rc; +} + diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 0fda525..5ef65dc 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -14452,6 +14452,35 @@ test_123f() { } run_test 123f "Retry mechanism with large wide striping files" +test_123g() { + local dir=$DIR/$tdir + local num=1000 + + mkdir $dir || error "failed to mkdir $dir" + createmany -o $dir/$tfile $num || error "failed creatmany files" + cancel_lru_locks mdc + cancel_lru_locks osc + + $LCTL set_param llite.*.statahead_stats=clear + $LCTL set_param mdc.*.batch_stats=clear + aheadmany -c stat -s 0 -e $num -b $tfile -d $dir || + error "aheadmany $dir with $tfile failed" + wait_update_facet client "pgrep ll_sa" "" 35 || + error "ll_sa thread is still running" + $LCTL get_param -n llite.*.statahead_stats + $LCTL get_param -n mdc.*.batch_stats + + local count + + count=$($LCTL get_param -n llite.*.statahead_stats | + awk '/hit.total:/ {print $2}') + echo "Hit total: $count" + # Hit ratio should be >= 75% + (( $count > num * 75 / 100)) || + error "hit total $count is be > 75% of $num" +} +run_test 123g "Test for stat-ahead advise" + test_124a() { [ $PARALLEL == "yes" ] && skip "skip parallel run" $LCTL get_param -n mdc.*.connect_flags | grep -q lru_resize || -- 1.8.3.1