Viewing: aheadmany.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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--noadvise|-N:No ahead advise hint IOCTL\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_batch_io_by_name(const char *dirpath, const char *fname,
enum lu_access_flags flags, __u64 start,
__u64 end)
{
int rc = 0;
int i;
for (i = start; i < end; i++) {
char *filename;
struct stat st;
filename = get_file_name(dirpath, fname, i);
if (flags & ACCESS_FL_STAT) {
rc = stat(filename, &st);
if (rc < 0) {
rc = -errno;
fprintf(stderr,
"%s: stat(%s) failed: rc = %d\n",
progname, filename, errno);
break;
}
}
}
return rc;
}
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;
int dir_fd;
int rc;
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;
}
rc = ll_batch_io_by_name(dirpath, fname, flags, start, end);
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 = 'N', .name = "noadvise", .has_arg = no_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;
bool has_advise = true;
char *end;
int rc = 0;
int c;
progname = basename(argv[0]);
while ((c = getopt_long(argc, argv, "c:s:e:b:d:B:Nh",
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 'N':
has_advise = false;
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;
}
if (has_advise)
rc = ll_ahead_by_name_index(dirpath, fname, flags,
start_index, end_index,
batch_max);
else
rc = ll_batch_io_by_name(dirpath, fname, flags,
start_index, end_index);
} else {
rc = -EOPNOTSUPP;
fprintf(stderr, "%s: unsupported ahead type %d\n",
progname, mode);
}
return rc;
}