zed: allow limiting concurrent jobs
200ms time-out is relatively long, but if we already hit the cap, then we'll likely be able to spawn multiple new jobs when we wake up Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz> Closes #11807
This commit is contained in:
parent
dbf8baf8dd
commit
fa991f2a47
|
@ -51,6 +51,7 @@ zed_conf_create(void)
|
||||||
zcp->state_fd = -1; /* opened via zed_conf_open_state() */
|
zcp->state_fd = -1; /* opened via zed_conf_open_state() */
|
||||||
zcp->zfs_hdl = NULL; /* opened via zed_event_init() */
|
zcp->zfs_hdl = NULL; /* opened via zed_event_init() */
|
||||||
zcp->zevent_fd = -1; /* opened via zed_event_init() */
|
zcp->zevent_fd = -1; /* opened via zed_event_init() */
|
||||||
|
zcp->max_jobs = 16;
|
||||||
|
|
||||||
if (!(zcp->conf_file = strdup(ZED_CONF_FILE)))
|
if (!(zcp->conf_file = strdup(ZED_CONF_FILE)))
|
||||||
goto nomem;
|
goto nomem;
|
||||||
|
@ -172,6 +173,8 @@ _zed_conf_display_help(const char *prog, int got_err)
|
||||||
"Write daemon's PID to FILE.", ZED_PID_FILE);
|
"Write daemon's PID to FILE.", ZED_PID_FILE);
|
||||||
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-s FILE",
|
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-s FILE",
|
||||||
"Write daemon's state to FILE.", ZED_STATE_FILE);
|
"Write daemon's state to FILE.", ZED_STATE_FILE);
|
||||||
|
fprintf(fp, "%*c%*s %s [%d]\n", w1, 0x20, -w2, "-j JOBS",
|
||||||
|
"Start at most JOBS at once.", 16);
|
||||||
fprintf(fp, "\n");
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
|
exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||||
|
@ -251,8 +254,9 @@ _zed_conf_parse_path(char **resultp, const char *path)
|
||||||
void
|
void
|
||||||
zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char * const opts = ":hLVc:d:p:P:s:vfFMZI";
|
const char * const opts = ":hLVc:d:p:P:s:vfFMZIj:";
|
||||||
int opt;
|
int opt;
|
||||||
|
unsigned long raw;
|
||||||
|
|
||||||
if (!zcp || !argv || !argv[0])
|
if (!zcp || !argv || !argv[0])
|
||||||
zed_log_die("Failed to parse options: Internal error");
|
zed_log_die("Failed to parse options: Internal error");
|
||||||
|
@ -303,6 +307,17 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||||
case 'Z':
|
case 'Z':
|
||||||
zcp->do_zero = 1;
|
zcp->do_zero = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'j':
|
||||||
|
errno = 0;
|
||||||
|
raw = strtoul(optarg, NULL, 0);
|
||||||
|
if (errno == ERANGE || raw > INT16_MAX) {
|
||||||
|
zed_log_die("%lu is too many jobs", raw);
|
||||||
|
} if (raw == 0) {
|
||||||
|
zed_log_die("0 jobs makes no sense");
|
||||||
|
} else {
|
||||||
|
zcp->max_jobs = raw;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
if (optopt == '?')
|
if (optopt == '?')
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct zed_conf {
|
||||||
libzfs_handle_t *zfs_hdl; /* handle to libzfs */
|
libzfs_handle_t *zfs_hdl; /* handle to libzfs */
|
||||||
int zevent_fd; /* fd for access to zevents */
|
int zevent_fd; /* fd for access to zevents */
|
||||||
char *path; /* custom $PATH for zedlets to use */
|
char *path; /* custom $PATH for zedlets to use */
|
||||||
|
int16_t max_jobs; /* max zedlets to run at one time */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct zed_conf *zed_conf_create(void);
|
struct zed_conf *zed_conf_create(void);
|
||||||
|
|
|
@ -955,8 +955,7 @@ zed_event_service(struct zed_conf *zcp)
|
||||||
|
|
||||||
_zed_event_add_time_strings(eid, zsp, etime);
|
_zed_event_add_time_strings(eid, zsp, etime);
|
||||||
|
|
||||||
zed_exec_process(eid, class, subclass,
|
zed_exec_process(eid, class, subclass, zcp, zsp);
|
||||||
zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd);
|
|
||||||
|
|
||||||
zed_conf_write_state(zcp, eid, etime);
|
zed_conf_write_state(zcp, eid, etime);
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ static pthread_t _reap_children_tid = (pthread_t)-1;
|
||||||
static volatile boolean_t _reap_children_stop;
|
static volatile boolean_t _reap_children_stop;
|
||||||
static avl_tree_t _launched_processes;
|
static avl_tree_t _launched_processes;
|
||||||
static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static int16_t _launched_processes_limit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an environment string array for passing to execve() using the
|
* Create an environment string array for passing to execve() using the
|
||||||
|
@ -122,12 +123,18 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
|
||||||
int fd;
|
int fd;
|
||||||
struct launched_process_node *node;
|
struct launched_process_node *node;
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
|
struct timespec launch_timeout =
|
||||||
|
{ .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
|
||||||
|
|
||||||
assert(dir != NULL);
|
assert(dir != NULL);
|
||||||
assert(prog != NULL);
|
assert(prog != NULL);
|
||||||
assert(env != NULL);
|
assert(env != NULL);
|
||||||
assert(zfd >= 0);
|
assert(zfd >= 0);
|
||||||
|
|
||||||
|
while (__atomic_load_n(&_launched_processes_limit,
|
||||||
|
__ATOMIC_SEQ_CST) <= 0)
|
||||||
|
(void) nanosleep(&launch_timeout, NULL);
|
||||||
|
|
||||||
n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
|
n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
|
||||||
if ((n < 0) || (n >= sizeof (path))) {
|
if ((n < 0) || (n >= sizeof (path))) {
|
||||||
zed_log_msg(LOG_WARNING,
|
zed_log_msg(LOG_WARNING,
|
||||||
|
@ -159,6 +166,7 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
|
||||||
|
|
||||||
/* parent process */
|
/* parent process */
|
||||||
|
|
||||||
|
__atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
|
||||||
zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
|
zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
|
||||||
prog, eid, pid);
|
prog, eid, pid);
|
||||||
|
|
||||||
|
@ -216,6 +224,8 @@ _reap_children(void *arg)
|
||||||
free(pnode);
|
free(pnode);
|
||||||
}
|
}
|
||||||
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
||||||
|
__atomic_add_fetch(&_launched_processes_limit, 1,
|
||||||
|
__ATOMIC_SEQ_CST);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(status)) {
|
||||||
zed_log_msg(LOG_INFO,
|
zed_log_msg(LOG_INFO,
|
||||||
|
@ -270,20 +280,21 @@ zed_exec_fini(void)
|
||||||
* Process the event [eid] by synchronously invoking all zedlets with a
|
* Process the event [eid] by synchronously invoking all zedlets with a
|
||||||
* matching class prefix.
|
* matching class prefix.
|
||||||
*
|
*
|
||||||
* Each executable in [zedlets] from the directory [dir] is matched against
|
* Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
|
||||||
* the event's [class], [subclass], and the "all" class (which matches
|
* is matched against the event's [class], [subclass], and the "all" class
|
||||||
* all events). Every zedlet with a matching class prefix is invoked.
|
* (which matches all events).
|
||||||
|
* Every zedlet with a matching class prefix is invoked.
|
||||||
* The NAME=VALUE strings in [envs] will be passed to the zedlet as
|
* The NAME=VALUE strings in [envs] will be passed to the zedlet as
|
||||||
* environment variables.
|
* environment variables.
|
||||||
*
|
*
|
||||||
* The file descriptor [zfd] is the zevent_fd used to track the
|
* The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
|
||||||
* current cursor location within the zevent nvlist.
|
* current cursor location within the zevent nvlist.
|
||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||||
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd)
|
struct zed_conf *zcp, zed_strings_t *envs)
|
||||||
{
|
{
|
||||||
const char *class_strings[4];
|
const char *class_strings[4];
|
||||||
const char *allclass = "all";
|
const char *allclass = "all";
|
||||||
|
@ -292,10 +303,12 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||||
char **e;
|
char **e;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (!dir || !zedlets || !envs || zfd < 0)
|
if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
if (_reap_children_tid == (pthread_t)-1) {
|
if (_reap_children_tid == (pthread_t)-1) {
|
||||||
|
_launched_processes_limit = zcp->max_jobs;
|
||||||
|
|
||||||
if (pthread_create(&_reap_children_tid, NULL,
|
if (pthread_create(&_reap_children_tid, NULL,
|
||||||
_reap_children, NULL) != 0)
|
_reap_children, NULL) != 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
@ -321,11 +334,13 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||||
|
|
||||||
e = _zed_exec_create_env(envs);
|
e = _zed_exec_create_env(envs);
|
||||||
|
|
||||||
for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) {
|
for (z = zed_strings_first(zcp->zedlets); z;
|
||||||
|
z = zed_strings_next(zcp->zedlets)) {
|
||||||
for (csp = class_strings; *csp; csp++) {
|
for (csp = class_strings; *csp; csp++) {
|
||||||
n = strlen(*csp);
|
n = strlen(*csp);
|
||||||
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
|
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
|
||||||
_zed_exec_fork_child(eid, dir, z, e, zfd);
|
_zed_exec_fork_child(eid, zcp->zedlet_dir,
|
||||||
|
z, e, zcp->zevent_fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(e);
|
free(e);
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "zed_strings.h"
|
#include "zed_strings.h"
|
||||||
|
#include "zed_conf.h"
|
||||||
|
|
||||||
void zed_exec_fini(void);
|
void zed_exec_fini(void);
|
||||||
|
|
||||||
int zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
int zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||||
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs,
|
struct zed_conf *zcp, zed_strings_t *envs);
|
||||||
int zevent_fd);
|
|
||||||
|
|
||||||
#endif /* !ZED_EXEC_H */
|
#endif /* !ZED_EXEC_H */
|
||||||
|
|
|
@ -29,6 +29,7 @@ ZED \- ZFS Event Daemon
|
||||||
[\fB\-p\fR \fIpidfile\fR]
|
[\fB\-p\fR \fIpidfile\fR]
|
||||||
[\fB\-P\fR \fIpath\fR]
|
[\fB\-P\fR \fIpath\fR]
|
||||||
[\fB\-s\fR \fIstatefile\fR]
|
[\fB\-s\fR \fIstatefile\fR]
|
||||||
|
[\fB\-j\fR \fIjobs\fR]
|
||||||
[\fB\-v\fR]
|
[\fB\-v\fR]
|
||||||
[\fB\-V\fR]
|
[\fB\-V\fR]
|
||||||
[\fB\-Z\fR]
|
[\fB\-Z\fR]
|
||||||
|
@ -95,6 +96,11 @@ it in production!
|
||||||
.TP
|
.TP
|
||||||
.BI \-s\ statefile
|
.BI \-s\ statefile
|
||||||
Write the daemon's state to the specified file.
|
Write the daemon's state to the specified file.
|
||||||
|
.TP
|
||||||
|
.BI \-j\ jobs
|
||||||
|
Allow at most \fIjobs\fR ZEDLETs to run concurrently,
|
||||||
|
delaying execution of new ones until they finish.
|
||||||
|
Defaults to 16.
|
||||||
.SH ZEVENTS
|
.SH ZEVENTS
|
||||||
.PP
|
.PP
|
||||||
A zevent is comprised of a list of nvpairs (name/value pairs). Each zevent
|
A zevent is comprised of a list of nvpairs (name/value pairs). Each zevent
|
||||||
|
|
|
@ -3705,8 +3705,8 @@ function zed_start
|
||||||
# run ZED in the background and redirect foreground logging
|
# run ZED in the background and redirect foreground logging
|
||||||
# output to $ZED_LOG.
|
# output to $ZED_LOG.
|
||||||
log_must truncate -s 0 $ZED_DEBUG_LOG
|
log_must truncate -s 0 $ZED_DEBUG_LOG
|
||||||
log_must eval "zed -vF -d $ZEDLET_DIR -p $ZEDLET_DIR/zed.pid -P $PATH" \
|
log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \
|
||||||
"-s $ZEDLET_DIR/state 2>$ZED_LOG &"
|
"-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -3722,14 +3722,13 @@ function zed_stop
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_note "Stopping ZED"
|
log_note "Stopping ZED"
|
||||||
if [[ -f ${ZEDLET_DIR}/zed.pid ]]; then
|
while true; do
|
||||||
zedpid=$(<${ZEDLET_DIR}/zed.pid)
|
zedpids="$(pgrep -x zed)"
|
||||||
kill $zedpid
|
[ "$?" -ne 0 ] && break
|
||||||
while ps -p $zedpid > /dev/null; do
|
|
||||||
|
log_must kill $zedpids
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
rm -f ${ZEDLET_DIR}/zed.pid
|
|
||||||
fi
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ log_assert "Verify zpool sub-commands generate expected events"
|
||||||
log_onexit cleanup
|
log_onexit cleanup
|
||||||
|
|
||||||
log_must zpool events -c
|
log_must zpool events -c
|
||||||
|
log_must zed_stop
|
||||||
log_must zed_start
|
log_must zed_start
|
||||||
|
|
||||||
# Backup our zed.rc
|
# Backup our zed.rc
|
||||||
|
|
Loading…
Reference in New Issue