From: Lei Feng Date: Tue, 27 Apr 2021 10:50:10 +0000 (+0800) Subject: EX-2818 lipe: Parse arguments and config file X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=472694d2b752716568c0add7457269099abe0398;p=fs%2Flustre-release.git EX-2818 lipe: Parse arguments and config file Add the code to parse arguments and config file, and some arguments. Add the framework of wait-scan-free. Change-Id: I792715badcb5a1fb1aa9062f04c2afab3229b747 Signed-off-by: Lei Feng Test-Parameters: trivial Reviewed-on: https://review.whamcloud.com/43194 Tested-by: jenkins Tested-by: Maloo Reviewed-by: John L. Hammond Reviewed-by: Andreas Dilger --- diff --git a/lipe/src/lpcc_purge.c b/lipe/src/lpcc_purge.c index a47c91a..e0bff19 100644 --- a/lipe/src/lpcc_purge.c +++ b/lipe/src/lpcc_purge.c @@ -1,6 +1,9 @@ +#include #include #include #include +#include +#include #include #include #include @@ -8,20 +11,44 @@ #include #include #include -#include +#include #include +#include "debug.h" + -#define LPCC_PURGE_DEFAULT_CONFIG_FILE "/etc/lpcc_purge.conf" -#define LPCC_PURGE_PIDFILE "/var/run/lpcc_purge.pid" +#define DEF_HIGH_USAGE 90 +#define DEF_LOW_USAGE 75 +#define DEF_INTERVAL 5 +#define DEF_SCAN_THREADS 1 + +#define OPT_LOG_LEVEL 4 struct lpcc_purge_options { - char *o_cfg_file; - bool o_exit; + char *o_cache; + char *o_mount; + unsigned int o_rwid; + + double o_high_usage; + double o_low_usage; + + int o_interval; + int o_scan_threads; }; static struct lpcc_purge_options opt = { - .o_cfg_file = LPCC_PURGE_DEFAULT_CONFIG_FILE, - .o_exit = false, + .o_rwid = -1, + .o_high_usage = DEF_HIGH_USAGE, + .o_low_usage = DEF_LOW_USAGE, + .o_interval = DEF_INTERVAL, + .o_scan_threads = DEF_SCAN_THREADS, +}; + +bool exit_flag = false; +struct lpcc_purge_stats { + double s_start_usage; +}; +static struct lpcc_purge_stats stats = { + .s_start_usage = -1, }; static void lpcc_purge_null_handler(int signal) @@ -32,35 +59,277 @@ static void lpcc_purge_sigint_handler(int signal) { psignal(signal, "exiting"); - opt.o_exit = true; + exit_flag = true; } static void usage(void) { printf("Usage: %s [options]\n" - "\t-f, --config-file FILE\n" + "\t-b, --debug, enable debugging output\n" + "\t --log_level={debug|info|normal|warn|error|fatal|off}, set log level (default: info)\n" + "\t-f, --config-file=FILE\n" + "\t-C, --cache=DIR root directory of PCC\n" + "\t-M, --mount=DIR local Lustre client's mountpoint\n" + "\t-A, --rwid=NUM the rwid of PCC\n" + "\t-H, --high_usage=NUM %% of space or inode to start purging (default: %u)\n" + "\t-L, --low_usage=NUM %% of space or inode to stop purging (default: %u)\n" + "\t-i, --interval=NUM, seconds to next check (default: %u)\n" + "\t-t, --scan_threads=NUM scanning threads (default: %u)\n" "\t-h, --help, print this help message\n", - program_invocation_short_name - ); - exit(0); + program_invocation_short_name, + DEF_HIGH_USAGE, + DEF_LOW_USAGE, + DEF_INTERVAL, + DEF_SCAN_THREADS + ); } static struct option long_options[] = { + { "debug", no_argument, NULL, 'b'}, { "config-file", required_argument, NULL, 'f'}, + { "log_level", required_argument, NULL, OPT_LOG_LEVEL}, + { "cache", required_argument, NULL, 'C'}, + { "mount", required_argument, NULL, 'M'}, + { "rwid", required_argument, NULL, 'A'}, + { "high_usage", required_argument, NULL, 'H'}, + { "low_usage", required_argument, NULL, 'L'}, + { "interval", required_argument, NULL, 'i'}, + { "scan_threads", required_argument, NULL, 't'}, { "help", no_argument, NULL, 'h' }, { NULL } }; +static struct option *lpcc_purge_keyword_lookup(const char *keyword) +{ + int i = 0; + + while (long_options[i].name) { + if (strcmp(keyword, long_options[i].name) == 0) + return long_options + i; + i++; + } + return NULL; +} + +static void lpcc_purge_process_opt(int c, char *optarg); + +static void load_config(const char *path) +{ + size_t len = 0; + char *buf = NULL; + + FILE *f = fopen(path, "r"); + if (!f) { + llapi_error(LLAPI_MSG_FATAL, errno, + "cannot open config file '%s'", path); + exit(1); + } + + while (!feof(f)) { + struct option *opt; + char *s, *t; + len = PATH_MAX; + + if (getline(&buf, &len, f) <= 0) + break; + s = buf; + while (isspace(*s)) + s++; + t = strchr(s, '#'); + if (t) + *t = '\0'; + if (*s == '#') + continue; + t = strsep(&s, "=\n"); + if (!t || *t == 0) + continue; + opt = lpcc_purge_keyword_lookup(t); + if (!opt) { + llapi_error(LLAPI_MSG_ERROR, EINVAL, + "unknown tunable '%s'", t); + continue; + } + if (opt->val == 'f') { + /* you shall not pass! */ + continue; + } + + if (opt->has_arg == required_argument || + opt->has_arg == optional_argument) { + optarg = strsep(&s, "\n "); + if (!optarg && + opt->has_arg == required_argument) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "no argument for '%s'", t); + exit(1); + } + } + else { + optarg = NULL; + } + llapi_printf(LLAPI_MSG_DEBUG, "conf: %s %s\n", t, + optarg ? optarg : ""); + lpcc_purge_process_opt(opt->val, optarg); + } + + free(buf); + fclose(f); +} + +static const char *log_level_strs[] = { + [LLAPI_MSG_OFF] = "off", + [LLAPI_MSG_FATAL] = "fatal", + [LLAPI_MSG_ERROR] = "error", + [LLAPI_MSG_WARN] = "warn", + [LLAPI_MSG_NORMAL] = "normal", + [LLAPI_MSG_INFO] = "info", + [LLAPI_MSG_DEBUG] = "debug", +}; + +static enum llapi_message_level parse_log_level(const char *level_str) +{ + enum llapi_message_level level = LLAPI_MSG_INFO; + int i; + + for (i = 0; i < ARRAY_SIZE(log_level_strs); i++) { + if (strcasecmp(log_level_strs[i], level_str) == 0) { + level = i; + break; + } + } + + if (i == sizeof(log_level_strs) / sizeof(char *)) { + llapi_error(LLAPI_MSG_WARN, -EINVAL, + "Unrecognized log level string '%s'", + optarg); + } + + return level; +} + +static void parse_mountpoint(const char *name) +{ + int rc; + struct statfs statbuf; + + rc = statfs(name, &statbuf); + if (rc != 0) { + llapi_error(LLAPI_MSG_FATAL, -rc, + "cannot statfs '%s'", name); + exit(1); + } + if (statbuf.f_type != LL_SUPER_MAGIC) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "'%s' is not a lustre mount point", name); + exit(1); + } +} + +static void parse_cache_dir(const char *name) +{ + struct stat stat1; + int rc; + + rc = stat(name, &stat1); + if (rc) { + llapi_error(LLAPI_MSG_FATAL, errno, + "cannot stat cache dir '%s'", name); + exit(1); + } + + if (!S_ISDIR(stat1.st_mode)) { + llapi_error(LLAPI_MSG_FATAL, errno, + "cache path '%s' is not a directory", name); + exit(1); + } +} + static void lpcc_purge_process_opt(int c, char *optarg) { + long value; + char *endptr = NULL; + enum llapi_message_level log_level; + switch(c) { case 'f': - opt.o_cfg_file = optarg; + load_config(optarg); break; - } + case 'b': + llapi_msg_set_level(LLAPI_MSG_MAX); + break; + case OPT_LOG_LEVEL: + log_level = parse_log_level(optarg); + llapi_printf(LLAPI_MSG_INFO, "Set log level: %s\n", + log_level_strs[log_level]); + llapi_msg_set_level(log_level); - return; + break; + case 'C': + parse_cache_dir(optarg); + opt.o_cache = strdup(optarg); + break; + case 'M': + parse_mountpoint(optarg); + opt.o_mount = strdup(optarg); + break; + case 'A': + value = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || value <= 0) { + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid rwid: '%s'", + optarg); + exit(1); + } + opt.o_rwid = value; + break; + case 'H': + value = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || value < 1 || value > 99) { + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid high watermark: '%s'", + optarg); + exit(1); + } + opt.o_high_usage = value; + break; + case 'L': + value = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || value < 1 || value > 99) { + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid low watermark: '%s'", + optarg); + exit(1); + } + opt.o_low_usage = value; + break; + case 'i': + value = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || value <= 0) { + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid check interval: '%s'", + optarg); + exit(1); + } + opt.o_interval = value; + break; + case 't': + value = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || value <= 0) { + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid scan threads count: '%s'", + optarg); + exit(1); + } + opt.o_scan_threads = value; + break; + default: + llapi_error(LLAPI_MSG_FATAL, -EINVAL, + "invalid argument: '%s'", + optarg); + exit(1); + break; + } } static void lpcc_purge_parse_opts(int argc, char **argv) @@ -68,7 +337,7 @@ static void lpcc_purge_parse_opts(int argc, char **argv) int c; while ((c = getopt_long(argc, argv, - "hf:", + "bf:C:M:A:H:L:i:t:h", long_options, NULL)) != EOF) { switch(c) { @@ -88,73 +357,138 @@ static void lpcc_purge_parse_opts(int argc, char **argv) } } -static int load_config() +void lpcc_purge_verify_opts(void) { - return 0; + if (opt.o_mount == NULL) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "lustre mount point must be specified"); + exit(1); + } + if (opt.o_cache == NULL) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "pcc cache dir must be specified"); + exit(1); + } + if (opt.o_rwid == -1) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "rwid mount point must be specified"); + exit(1); + } + // check freehi > freelo + if (opt.o_high_usage <= opt.o_low_usage) { + llapi_error(LLAPI_MSG_FATAL, EINVAL, + "high usage (%.1f) must be larger than low usage (%.1f)", + opt.o_high_usage, opt.o_low_usage); + exit(1); + } } -static void lpcc_purge_lock_pidfile(void) +/* It seems that lustre/utils/pid_file.c does not exist in RHEL7.x, + * so copy the function here + */ +static int create_pid_file(const char *path) { - char buf[1024]; - int fd, rc, sz; + char buf[3 * sizeof(long long) + 2]; + size_t buf_len; + int fd = -1; + int rc2; - snprintf(buf, sizeof(buf), LPCC_PURGE_PIDFILE); - fd = open(buf, O_RDWR | O_CREAT, 0600); + fd = open(path, O_RDWR|O_CREAT|O_CLOEXEC, 0600); if (fd < 0) { - llapi_error(LLAPI_MSG_FATAL, errno, - "cannot create pidfile"); - exit(1); + fprintf(stderr, "%s: cannot open '%s': %s\n", + program_invocation_short_name, path, strerror(errno)); + return -1; } - rc = flock(fd, LOCK_EX | LOCK_NB); - if (rc < 0) { - sz = read(fd, buf, sizeof(buf)); - /* protect against garbage */ - if (sz > 10) - sz = 0; - if (sz > 0) - buf[sz] = 0; - llapi_error(LLAPI_MSG_FATAL, EBUSY, - "another lpcc_purge is running, locked by %s", - sz > 0 ? buf : "[unknown]"); - exit(1); + + struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + rc2 = fcntl(fd, F_SETLK, &fl); + if (rc2 < 0) { + fprintf(stderr, "%s: cannot lock '%s': %s\n", + program_invocation_short_name, path, strerror(errno)); + goto out; } - rc = ftruncate(fd, 0); - if (rc < 0) { - llapi_error(LLAPI_MSG_FATAL, errno, - "cannot truncate pidfile"); - exit(1); + rc2 = ftruncate(fd, 0); + if (rc2 < 0) { + fprintf(stderr, "%s: cannot truncate '%s': %s\n", + program_invocation_short_name, path, strerror(errno)); + goto out; } - sz = snprintf(buf, sizeof(buf), "%d\n", getpid()); - rc = write(fd, buf, sz); - if (rc < 0 || rc != sz) { + buf_len = snprintf(buf, sizeof(buf), "%lld\n", (long long)getpid()); + rc2 = write(fd, buf, buf_len); + if (rc2 < 0) { + fprintf(stderr, "%s: cannot write '%s': %s\n", + program_invocation_short_name, path, strerror(errno)); + goto out; + } +out: + if (rc2 < 0 && !(fd < 0)) { + close(fd); + fd = -1; + } + + return fd; +} + +static void lpcc_purge_lock_pidfile(void) +{ + char buf[PATH_MAX]; + int fd; + + snprintf(buf, sizeof(buf), "/var/run/lpcc_purge-%d.pid", opt.o_rwid); + fd = create_pid_file(buf); + if (fd < 0) { llapi_error(LLAPI_MSG_FATAL, errno, - "cannot write pidfile"); + "cannot create pidfile '%s'", buf); exit(1); } - + /* we keep the fd open to hold the flock, + it will be closed automatically when the process exits */ } -static void lpcc_purge_unlock_pidfile(void) +static double lpcc_purge_get_fs_usage(const char *fs) { - char buf[1024]; int rc; + struct statfs statfs_buf; - snprintf(buf, sizeof(buf), LPCC_PURGE_PIDFILE); + rc = statfs(opt.o_cache, &statfs_buf); + if (rc) { + llapi_error(LLAPI_MSG_FATAL, errno, "cannot statfs '%s'", fs); + exit(1); + } - rc = unlink(buf); - if (rc < 0) { - llapi_error(LLAPI_MSG_ERROR, errno, - "cannot unlink pidfile"); + return 100.0 * (statfs_buf.f_blocks - statfs_buf.f_bfree) / statfs_buf.f_blocks; +} + +static void lpcc_purge_wait_for_scan(void) +{ + while (!exit_flag) { + double usage = lpcc_purge_get_fs_usage(opt.o_cache); + if (usage >= opt.o_high_usage) { + stats.s_start_usage = usage; + break; + } else { + sleep(opt.o_interval); + } } +} - return; +static void lpcc_purge_scan(void) +{ +} + +static void lpcc_purge_free_space(void) +{ } int main(int argc, char *argv[]) { - int rc = -1; + int rc = 0; llapi_msg_set_level(LLAPI_MSG_INFO); signal(SIGUSR1, &lpcc_purge_null_handler); @@ -165,18 +499,18 @@ int main(int argc, char *argv[]) lpcc_purge_parse_opts(argc, argv); - rc = load_config(); - if (rc) { - goto out; - } + lpcc_purge_verify_opts(); lpcc_purge_lock_pidfile(); - while(!opt.o_exit) { - sleep(10); + while(!exit_flag) { + + lpcc_purge_wait_for_scan(); + + lpcc_purge_scan(); + + lpcc_purge_free_space(); } -out: - lpcc_purge_unlock_pidfile(); return rc == 0 ? 0 : 255; }