4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Copyright (c) 2023, Whamcloud DDN Storage Corporation.
26 * Create|Open|Stat|Read ahead.
27 * This program is mainly used to verify that ahead feature works as
28 * expected for batch file accesses.
30 * Author: Qian Yingjin <qian@ddn.com>
43 #include <sys/types.h>
45 #include <linux/unistd.h>
47 #include <sys/ioctl.h>
49 #include <linux/lustre/lustre_user.h>
51 static char *progname;
53 static void usage(void)
55 printf("Usage: %s {--iocall|-c [stat|open|create|read]}\n"
56 "[--start|-s start] [--end|-e end] [--basename NAME]\n"
57 "[--batch_max|-B] {--dir|-d DIR} dentry ...\n"
58 "\t--iocall|-c: I/O syscall in a predictive batch access\n"
59 "\t--start|-s: Start index of file names\n"
60 "\t--end|-e: End index of file names\n"
61 "\t--basename|-b:Base name for file naming format\n"
62 "\t--noadvise|-N:No ahead advise hint IOCTL\n"
63 "\t--batch_max|-B: max batch count for ahead operations\n"
64 "\t--directory|-d: under this directory do ahead operations\n",
69 static char *get_file_name(const char *dirpath, const char *basename, long n)
71 static char filename[PATH_MAX];
74 bytes = snprintf(filename, PATH_MAX - 1, "%s/%s%ld",
75 dirpath, basename, n);
76 if (bytes >= PATH_MAX - 1) {
77 fprintf(stderr, "%s: file name too long\n", progname);
84 static int ll_batch_io_by_name(const char *dirpath, const char *fname,
85 enum lu_access_flags flags, __u64 start,
91 for (i = start; i < end; i++) {
95 filename = get_file_name(dirpath, fname, i);
96 if (flags & ACCESS_FL_STAT) {
97 rc = stat(filename, &st);
101 "%s: stat(%s) failed: rc = %d\n",
102 progname, filename, errno);
111 static int ll_ahead_by_name_index(const char *dirpath, const char *fname,
112 enum lu_access_flags flags, __u64 start,
113 __u64 end, __u32 batch_max)
115 struct llapi_lu_ladvise2 ladvise;
119 dir_fd = open(dirpath, O_DIRECTORY | O_RDONLY);
122 fprintf(stderr, "%s: failed to open dir '%s': rc = %d\n",
123 progname, dirpath, rc);
127 ladvise.lla_advice = LU_LADVISE_AHEAD;
128 ladvise.lla_ahead_mode = LU_AH_NAME_INDEX;
129 ladvise.lla_access_flags = flags;
130 ladvise.lla_start = start;
131 ladvise.lla_end = end;
132 ladvise.lla_batch_max = batch_max;
133 strncpy(ladvise.lla_fname, fname, sizeof(ladvise.lla_fname) - 1);
134 rc = ioctl(dir_fd, LL_IOC_LADVISE2, &ladvise);
136 fprintf(stderr, "%s: failed to ahead for '%s': rc = %d\n",
137 progname, dirpath, rc);
142 rc = ll_batch_io_by_name(dirpath, fname, flags, start, end);
147 int main(int argc, char **argv)
149 struct option long_opts[] = {
150 { .val = 'c', .name = "iocall", .has_arg = required_argument },
151 { .val = 's', .name = "start", .has_arg = required_argument },
152 { .val = 'e', .name = "end", .has_arg = required_argument },
153 { .val = 'b', .name = "basename", .has_arg = required_argument },
154 { .val = 'd', .name = "directory", .has_arg = required_argument },
155 { .val = 'B', .name = "batch_max", .has_arg = required_argument },
156 { .val = 'N', .name = "noadvise", .has_arg = no_argument },
157 { .val = 'h', .name = "help", .has_arg = no_argument },
159 enum lu_access_flags flags = ACCESS_FL_NONE;
160 enum lu_ahead_mode mode = LU_AH_NAME_INDEX;
162 const char *dirpath = NULL;
164 __u64 start_index = 0;
166 bool has_advise = true;
171 progname = basename(argv[0]);
172 while ((c = getopt_long(argc, argv, "c:s:e:b:d:B:Nh",
173 long_opts, NULL)) != -1) {
176 if (strcmp(optarg, "stat") == 0) {
177 flags |= ACCESS_FL_STAT;
178 } else if (strcmp(optarg, "open") == 0) {
179 flags |= ACCESS_FL_OPEN;
180 } else if (strcmp(optarg, "creat") == 0 ||
181 strcmp(optarg, "create") == 0) {
182 flags |= ACCESS_FL_CREAT;
183 } else if (strcmp(optarg, "read") == 0) {
184 flags |= ACCESS_FL_READ;
185 } else if (strcmp(optarg, "write") == 0) {
186 flags |= ACCESS_FL_WRITE;
188 fprintf(stderr, "%s %s: bad access type '%s'\n",
189 progname, argv[0], optarg);
194 start_index = strtoull(optarg, &end, 0);
196 fprintf(stderr, "%s %s: bad start index '%s'\n",
197 progname, argv[0], optarg);
202 end_index = strtoull(optarg, &end, 0);
204 fprintf(stderr, "%s %s: bad start index '%s'\n",
205 progname, argv[0], optarg);
210 batch_max = strtoul(optarg, &end, 0);
212 fprintf(stderr, "%s %s: bad batch count '%s'\n",
213 progname, argv[0], optarg);
227 fprintf(stderr, "%s: unrecognized option '%s'\n",
228 progname, argv[optind - 1]);
235 if (flags == ACCESS_FL_NONE) {
236 fprintf(stderr, "%s: must specify access mode\n", progname);
241 fprintf(stderr, "%s: must specify directory path\n", progname);
245 if (mode == LU_AH_NAME_INDEX) {
247 fprintf(stderr, "%s: must specify base file name\n",
252 if (end_index == 0) {
253 fprintf(stderr, "%s: must specify end index\n",
258 if (flags != ACCESS_FL_STAT) {
259 fprintf(stderr, "%s: only support stat-ahead\n",
265 rc = ll_ahead_by_name_index(dirpath, fname, flags,
266 start_index, end_index,
269 rc = ll_batch_io_by_name(dirpath, fname, flags,
270 start_index, end_index);
273 fprintf(stderr, "%s: unsupported ahead type %d\n",