Whamcloud - gitweb
LU-14361 statahead: Add test for statahead advise 30/51730/9
authorQian Yingjin <qian@ddn.com>
Fri, 21 Jul 2023 10:21:11 +0000 (06:21 -0400)
committerOleg Drokin <green@whamcloud.com>
Fri, 3 Nov 2023 04:05:01 +0000 (04:05 +0000)
This patch adds a test program "aheadmany" and sanity/test_123g to
verfiy that statahead advise works as expected.

Signed-off-by: Qian Yingjin <qian@ddn.com>
Change-Id: I751313ecb790c66f70a09bf8e9d13846b3c0032d
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/51730
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Lai Siyao <lai.siyao@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
lustre/tests/Makefile.am
lustre/tests/aheadmany.c [new file with mode: 0644]
lustre/tests/sanity.sh

index ad340b1..6a1a4c4 100644 (file)
@@ -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 (file)
index 0000000..d145858
--- /dev/null
@@ -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 <qian@ddn.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdbool.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/unistd.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+
+#include <linux/lustre/lustre_user.h>
+
+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;
+}
+
index 0fda525..5ef65dc 100755 (executable)
@@ -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 ||