readahead: parse command line arguments

This commit is contained in:
Lennart Poettering 2010-09-25 15:39:38 +02:00
parent 437dca8b2f
commit 8260358d5a
4 changed files with 184 additions and 17 deletions

View File

@ -40,6 +40,7 @@
#include <linux/fiemap.h>
#include <sys/ioctl.h>
#include <sys/vfs.h>
#include <getopt.h>
#include "missing.h"
#include "util.h"
@ -48,20 +49,20 @@
#include "ioprio.h"
#include "readahead-common.h"
#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE)
#define TIMEOUT_USEC (2*USEC_PER_MINUTE)
/* fixme:
*
* - detect ssd on btrfs/lvm...
* - read ahead directories
* - sd_readahead_cancel
* - gzip?
* - remount rw
* - are filenames from anotify normalized regards /../ and // and /./?
* - remount rw?
* - does ioprio_set work with fadvise()?
*/
static unsigned arg_files_max = 16*1024;
static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
static usec_t arg_timeout = 2*USEC_PER_MINUTE;
static int btrfs_defrag(int fd) {
struct btrfs_ioctl_vol_args data;
@ -74,7 +75,7 @@ static int btrfs_defrag(int fd) {
static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
struct stat st;
void *start = MAP_FAILED;
uint8_t vec[MINCORE_VEC_SIZE];
uint8_t *vec;
uint32_t b, c;
size_t l, pages;
bool mapped;
@ -89,7 +90,7 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
goto finish;
}
if ((k = file_verify(fd, fn, &st)) <= 0) {
if ((k = file_verify(fd, fn, arg_file_size_max, &st)) <= 0) {
r = k;
goto finish;
}
@ -104,6 +105,9 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
goto finish;
}
pages = l / PAGE_SIZE;
vec = alloca(pages);
if (mincore(start, l, vec) < 0) {
log_warning("mincore(%s) failed: %m", fn);
r = -errno;
@ -113,7 +117,6 @@ static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
fputs(fn, pack);
fputc('\n', pack);
pages = l / PAGE_SIZE;
mapped = false;
for (c = 0; c < pages; c++) {
bool new_mapped = !!(vec[c] & 1);
@ -248,7 +251,7 @@ static int collect(const char *root) {
goto finish;
}
not_after = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
not_after = now(CLOCK_MONOTONIC) + arg_timeout;
my_pid = getpid();
@ -274,7 +277,7 @@ static int collect(const char *root) {
usec_t t;
int h;
if (hashmap_size(files) > READAHEAD_FILES_MAX) {
if (hashmap_size(files) > arg_files_max) {
log_debug("Reached maximum number of read ahead files, ending collection.");
break;
}
@ -464,18 +467,109 @@ finish:
return r;
}
static int help(void) {
printf("%s [OPTIONS...] [DIRECTORY]\n\n"
"Collect read-ahead data on early boot.\n\n"
" -h --help Show this help\n"
" --max-files=INT Maximum number of files to read ahead\n"
" --max-file-size=BYTES Maximum size of files to read ahead\n"
" --timeout=USEC Maximum time to spend collecting data\n",
program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_FILES_MAX = 0x100,
ARG_FILE_SIZE_MAX,
ARG_TIMEOUT
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "files-max", required_argument, NULL, ARG_FILES_MAX },
{ "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_FILES_MAX:
if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) {
log_error("Failed to parse maximum number of files %s.", optarg);
return -EINVAL;
}
break;
case ARG_FILE_SIZE_MAX: {
unsigned long long ull;
if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
log_error("Failed to parse maximum file size %s.", optarg);
return -EINVAL;
}
arg_file_size_max = (off_t) ull;
break;
}
case ARG_TIMEOUT:
if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
log_error("Failed to parse timeout %s.", optarg);
return -EINVAL;
}
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
if (optind != argc &&
optind != argc-1) {
help();
return -EINVAL;
}
return 1;
}
int main(int argc, char *argv[]) {
int r;
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
log_parse_environment();
log_open();
if ((r = parse_argv(argc, argv)) <= 0)
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
if (!enough_ram()) {
log_info("Disabling readahead collector due to low memory.");
return 0;
}
if (collect(argc >= 2 ? argv[1] : "/") < 0)
if (collect(optind < argc ? argv[optind] : "/") < 0)
return 1;
return 0;

View File

@ -29,7 +29,7 @@
#include "readahead-common.h"
#include "util.h"
int file_verify(int fd, const char *fn, struct stat *st) {
int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
assert(fd >= 0);
assert(fn);
assert(st);
@ -44,7 +44,7 @@ int file_verify(int fd, const char *fn, struct stat *st) {
return 0;
}
if (st->st_size <= 0 || st->st_size > READAHEAD_FILE_SIZE_MAX) {
if (st->st_size <= 0 || st->st_size > file_size_max) {
log_debug("Not preloading file %s with size out of bounds %zi", fn, st->st_size);
return 0;
}

View File

@ -25,9 +25,8 @@
#include <sys/stat.h>
#define READAHEAD_FILE_SIZE_MAX (128*1024*1024)
#define READAHEAD_FILES_MAX (16*1024)
int file_verify(int fd, const char *fn, struct stat *st);
int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st);
int fs_on_ssd(const char *p);

View File

@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include "missing.h"
#include "util.h"
@ -40,6 +41,8 @@
#include "ioprio.h"
#include "readahead-common.h"
static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
static int unpack_file(FILE *pack) {
char fn[PATH_MAX];
int r = 0, fd = -1;
@ -56,7 +59,7 @@ static int unpack_file(FILE *pack) {
if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0)
log_warning("open(%s) failed: %m", fn);
else if (file_verify(fd, fn, &st) <= 0) {
else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
close_nointr_nofail(fd);
fd = -1;
}
@ -210,18 +213,89 @@ finish:
return r;
}
static int help(void) {
printf("%s [OPTIONS...] [DIRECTORY]\n\n"
"Replay collected read-ahead data on early boot.\n\n"
" -h --help Show this help\n"
" --max-file-size=BYTES Maximum size of files to read ahead\n",
program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_FILE_SIZE_MAX
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_FILE_SIZE_MAX: {
unsigned long long ull;
if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
log_error("Failed to parse maximum file size %s.", optarg);
return -EINVAL;
}
arg_file_size_max = (off_t) ull;
break;
}
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
if (optind != argc &&
optind != argc-1) {
help();
return -EINVAL;
}
return 1;
}
int main(int argc, char*argv[]) {
int r;
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
log_parse_environment();
log_open();
if ((r = parse_argv(argc, argv)) <= 0)
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
if (!enough_ram()) {
log_info("Disabling readahead replay due to low memory.");
return 0;
}
if (replay(argc >= 2 ? argv[1] : "/") < 0)
if (replay(optind < argc ? argv[optind] : "/") < 0)
return 1;
return 0;