Skip to content

Commit ca2ce9c

Browse files
nabijaczlewelibehlendorf
authored andcommitted
zed: use separate reaper thread and collect ZEDLETs asynchronously
Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Ahelenia Ziemiańska <[email protected]> Closes #11807
1 parent 3ef80ee commit ca2ce9c

File tree

5 files changed

+157
-55
lines changed

5 files changed

+157
-55
lines changed

cmd/zed/zed.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ _setup_sig_handlers(void)
6060
zed_log_die("Failed to initialize sigset");
6161

6262
sa.sa_flags = SA_RESTART;
63-
sa.sa_handler = SIG_IGN;
6463

64+
sa.sa_handler = SIG_IGN;
6565
if (sigaction(SIGPIPE, &sa, NULL) < 0)
6666
zed_log_die("Failed to ignore SIGPIPE");
6767

@@ -75,6 +75,10 @@ _setup_sig_handlers(void)
7575
sa.sa_handler = _hup_handler;
7676
if (sigaction(SIGHUP, &sa, NULL) < 0)
7777
zed_log_die("Failed to register SIGHUP handler");
78+
79+
(void) sigaddset(&sa.sa_mask, SIGCHLD);
80+
if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0)
81+
zed_log_die("Failed to block SIGCHLD");
7882
}
7983

8084
/*

cmd/zed/zed_event.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include <ctype.h>
1616
#include <errno.h>
1717
#include <fcntl.h>
18-
#include <libzfs.h> /* FIXME: Replace with libzfs_core. */
18+
#include <libzfs_core.h>
1919
#include <paths.h>
2020
#include <stdarg.h>
2121
#include <stdio.h>
@@ -96,6 +96,8 @@ zed_event_fini(struct zed_conf *zcp)
9696
libzfs_fini(zcp->zfs_hdl);
9797
zcp->zfs_hdl = NULL;
9898
}
99+
100+
zed_exec_fini();
99101
}
100102

101103
/*

cmd/zed/zed_exec.c

Lines changed: 147 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,52 @@
1818
#include <fcntl.h>
1919
#include <stdlib.h>
2020
#include <string.h>
21+
#include <stddef.h>
22+
#include <sys/avl.h>
2123
#include <sys/stat.h>
2224
#include <sys/wait.h>
2325
#include <time.h>
2426
#include <unistd.h>
27+
#include <pthread.h>
2528
#include "zed_exec.h"
2629
#include "zed_file.h"
2730
#include "zed_log.h"
2831
#include "zed_strings.h"
2932

3033
#define ZEVENT_FILENO 3
3134

35+
struct launched_process_node {
36+
avl_node_t node;
37+
pid_t pid;
38+
uint64_t eid;
39+
char *name;
40+
};
41+
42+
static int
43+
_launched_process_node_compare(const void *x1, const void *x2)
44+
{
45+
pid_t p1;
46+
pid_t p2;
47+
48+
assert(x1 != NULL);
49+
assert(x2 != NULL);
50+
51+
p1 = ((const struct launched_process_node *) x1)->pid;
52+
p2 = ((const struct launched_process_node *) x2)->pid;
53+
54+
if (p1 < p2)
55+
return (-1);
56+
else if (p1 == p2)
57+
return (0);
58+
else
59+
return (1);
60+
}
61+
62+
static pthread_t _reap_children_tid = (pthread_t)-1;
63+
static volatile boolean_t _reap_children_stop;
64+
static avl_tree_t _launched_processes;
65+
static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
66+
3267
/*
3368
* Create an environment string array for passing to execve() using the
3469
* NAME=VALUE strings in container [zsp].
@@ -85,8 +120,8 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
85120
int n;
86121
pid_t pid;
87122
int fd;
88-
pid_t wpid;
89-
int status;
123+
struct launched_process_node *node;
124+
sigset_t mask;
90125

91126
assert(dir != NULL);
92127
assert(prog != NULL);
@@ -107,6 +142,9 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
107142
prog, eid, strerror(errno));
108143
return;
109144
} else if (pid == 0) {
145+
(void) sigemptyset(&mask);
146+
(void) sigprocmask(SIG_SETMASK, &mask, NULL);
147+
110148
(void) umask(022);
111149
if ((fd = open("/dev/null", O_RDWR)) != -1) {
112150
(void) dup2(fd, STDIN_FILENO);
@@ -124,57 +162,108 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
124162
zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
125163
prog, eid, pid);
126164

127-
/* FIXME: Timeout rogue child processes with sigalarm? */
128-
129-
/*
130-
* Wait for child process using WNOHANG to limit
131-
* the time spent waiting to 10 seconds (10,000ms).
132-
*/
133-
for (n = 0; n < 1000; n++) {
134-
wpid = waitpid(pid, &status, WNOHANG);
135-
if (wpid == (pid_t)-1) {
136-
if (errno == EINTR)
137-
continue;
138-
zed_log_msg(LOG_WARNING,
139-
"Failed to wait for \"%s\" eid=%llu pid=%d",
140-
prog, eid, pid);
141-
break;
142-
} else if (wpid == 0) {
143-
struct timespec t;
144-
145-
/* child still running */
146-
t.tv_sec = 0;
147-
t.tv_nsec = 10000000; /* 10ms */
148-
(void) nanosleep(&t, NULL);
149-
continue;
150-
}
165+
node = calloc(1, sizeof (*node));
166+
if (node) {
167+
node->pid = pid;
168+
node->eid = eid;
169+
node->name = strdup(prog);
170+
(void) pthread_mutex_lock(&_launched_processes_lock);
171+
avl_add(&_launched_processes, node);
172+
(void) pthread_mutex_unlock(&_launched_processes_lock);
173+
}
174+
}
151175

152-
if (WIFEXITED(status)) {
153-
zed_log_msg(LOG_INFO,
154-
"Finished \"%s\" eid=%llu pid=%d exit=%d",
155-
prog, eid, pid, WEXITSTATUS(status));
156-
} else if (WIFSIGNALED(status)) {
157-
zed_log_msg(LOG_INFO,
158-
"Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
159-
prog, eid, pid, WTERMSIG(status),
160-
strsignal(WTERMSIG(status)));
176+
static void
177+
_nop(int sig)
178+
{}
179+
180+
static void *
181+
_reap_children(void *arg)
182+
{
183+
struct launched_process_node node, *pnode;
184+
pid_t pid;
185+
int status;
186+
struct sigaction sa = {};
187+
188+
(void) sigfillset(&sa.sa_mask);
189+
(void) sigdelset(&sa.sa_mask, SIGCHLD);
190+
(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
191+
192+
(void) sigemptyset(&sa.sa_mask);
193+
sa.sa_handler = _nop;
194+
sa.sa_flags = SA_NOCLDSTOP;
195+
(void) sigaction(SIGCHLD, &sa, NULL);
196+
197+
for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
198+
pid = waitpid(0, &status, 0);
199+
200+
if (pid == (pid_t)-1) {
201+
if (errno == ECHILD)
202+
pause();
203+
else if (errno != EINTR)
204+
zed_log_msg(LOG_WARNING,
205+
"Failed to wait for children: %s",
206+
strerror(errno));
161207
} else {
162-
zed_log_msg(LOG_INFO,
163-
"Finished \"%s\" eid=%llu pid=%d status=0x%X",
164-
prog, eid, (unsigned int) status);
208+
memset(&node, 0, sizeof (node));
209+
node.pid = pid;
210+
(void) pthread_mutex_lock(&_launched_processes_lock);
211+
pnode = avl_find(&_launched_processes, &node, NULL);
212+
if (pnode) {
213+
memcpy(&node, pnode, sizeof (node));
214+
215+
avl_remove(&_launched_processes, pnode);
216+
free(pnode);
217+
}
218+
(void) pthread_mutex_unlock(&_launched_processes_lock);
219+
220+
if (WIFEXITED(status)) {
221+
zed_log_msg(LOG_INFO,
222+
"Finished \"%s\" eid=%llu pid=%d exit=%d",
223+
node.name, node.eid, pid,
224+
WEXITSTATUS(status));
225+
} else if (WIFSIGNALED(status)) {
226+
zed_log_msg(LOG_INFO,
227+
"Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
228+
node.name, node.eid, pid, WTERMSIG(status),
229+
strsignal(WTERMSIG(status)));
230+
} else {
231+
zed_log_msg(LOG_INFO,
232+
"Finished \"%s\" eid=%llu pid=%d "
233+
"status=0x%X",
234+
node.name, node.eid, (unsigned int) status);
235+
}
236+
237+
free(node.name);
165238
}
166-
break;
167239
}
168240

169-
/*
170-
* kill child process after 10 seconds
171-
*/
172-
if (wpid == 0) {
173-
zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d",
174-
prog, pid);
175-
(void) kill(pid, SIGKILL);
176-
(void) waitpid(pid, &status, 0);
241+
return (NULL);
242+
}
243+
244+
void
245+
zed_exec_fini(void)
246+
{
247+
struct launched_process_node *node;
248+
void *ck = NULL;
249+
250+
if (_reap_children_tid == (pthread_t)-1)
251+
return;
252+
253+
_reap_children_stop = B_TRUE;
254+
(void) pthread_kill(_reap_children_tid, SIGCHLD);
255+
(void) pthread_join(_reap_children_tid, NULL);
256+
257+
while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
258+
free(node->name);
259+
free(node);
177260
}
261+
avl_destroy(&_launched_processes);
262+
263+
(void) pthread_mutex_destroy(&_launched_processes_lock);
264+
(void) pthread_mutex_init(&_launched_processes_lock, NULL);
265+
266+
_reap_children_tid = (pthread_t)-1;
178267
}
179268

180269
/*
@@ -206,6 +295,17 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
206295
if (!dir || !zedlets || !envs || zfd < 0)
207296
return (-1);
208297

298+
if (_reap_children_tid == (pthread_t)-1) {
299+
if (pthread_create(&_reap_children_tid, NULL,
300+
_reap_children, NULL) != 0)
301+
return (-1);
302+
pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
303+
304+
avl_create(&_launched_processes, _launched_process_node_compare,
305+
sizeof (struct launched_process_node),
306+
offsetof(struct launched_process_node, node));
307+
}
308+
209309
csp = class_strings;
210310

211311
if (class)

cmd/zed/zed_exec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <stdint.h>
1919
#include "zed_strings.h"
2020

21+
void zed_exec_fini(void);
22+
2123
int zed_exec_process(uint64_t eid, const char *class, const char *subclass,
2224
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs,
2325
int zevent_fd);

man/man8/zed.8.in

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,6 @@ Terminate the daemon.
231231

232232
.SH BUGS
233233
.PP
234-
Events are processed synchronously by a single thread. This can delay the
235-
processing of simultaneous zevents.
236-
.PP
237-
ZEDLETs are killed after a maximum of ten seconds.
238-
This can lead to a violation of a ZEDLET's atomicity assumptions.
239-
.PP
240234
The ownership and permissions of the \fIenabled-zedlets\fR directory (along
241235
with all parent directories) are not checked. If any of these directories
242236
are improperly owned or permissioned, an unprivileged user could insert a

0 commit comments

Comments
 (0)