Skip to content

Commit 33ec84b

Browse files
committed
zed: Add synchronous zedlets
Historically, ZED has blindly spawned off zedlets in parallel and never worried about their completion order. This means that you can potentially have zedlets for event number 2 starting before zedlets for event number 1 had finished. Most of the time this is fine, and it actually helps a lot when the system is getting spammed with hundreds of events. However, there are times when you want your zedlets to be executed in sequence with the event ID. That is where synchronous zedlets come in. ZED will wait for all previously spawned zedlets to finish before running a synchronous zedlet. Synchronous zedlets are guaranteed to be the only zedlet running. No other zedlets may run in parallel with a synchronous zedlet. Users should be careful to only use synchronous zedlets when needed, since they decrease parallelism. To make a zedlet synchronous, simply add a "-sync-" immediately following the event name in the zedlet's file name: EVENT_NAME-sync-ZEDLETNAME.sh For example, if you wanted a synchronous statechange script: statechange-sync-myzedlet.sh Signed-off-by: Tony Hutter <[email protected]>
1 parent b2284ae commit 33ec84b

File tree

11 files changed

+277
-22
lines changed

11 files changed

+277
-22
lines changed

cmd/zed/zed_exec.c

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -196,38 +196,33 @@ _nop(int sig)
196196
(void) sig;
197197
}
198198

199-
static void *
200-
_reap_children(void *arg)
199+
static void
200+
wait_for_children(boolean_t do_pause, boolean_t wait)
201201
{
202-
(void) arg;
203-
struct launched_process_node node, *pnode;
204202
pid_t pid;
205-
int status;
206203
struct rusage usage;
207-
struct sigaction sa = {};
208-
209-
(void) sigfillset(&sa.sa_mask);
210-
(void) sigdelset(&sa.sa_mask, SIGCHLD);
211-
(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
212-
213-
(void) sigemptyset(&sa.sa_mask);
214-
sa.sa_handler = _nop;
215-
sa.sa_flags = SA_NOCLDSTOP;
216-
(void) sigaction(SIGCHLD, &sa, NULL);
204+
int status;
205+
struct launched_process_node node, *pnode;
217206

218207
for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
219208
(void) pthread_mutex_lock(&_launched_processes_lock);
220-
pid = wait4(0, &status, WNOHANG, &usage);
221-
209+
pid = wait4(0, &status, wait ? 0 : WNOHANG, &usage);
222210
if (pid == 0 || pid == (pid_t)-1) {
223211
(void) pthread_mutex_unlock(&_launched_processes_lock);
224-
if (pid == 0 || errno == ECHILD)
225-
pause();
226-
else if (errno != EINTR)
212+
if ((pid == 0) || (errno == ECHILD)) {
213+
if (do_pause)
214+
pause();
215+
} else if (errno != EINTR)
227216
zed_log_msg(LOG_WARNING,
228217
"Failed to wait for children: %s",
229218
strerror(errno));
219+
zed_log_msg(LOG_INFO, "badpid exit %d", pid);
220+
if (!do_pause)
221+
return;
222+
230223
} else {
224+
zed_log_msg(LOG_INFO, "normal pid %d", pid);
225+
231226
memset(&node, 0, sizeof (node));
232227
node.pid = pid;
233228
pnode = avl_find(&_launched_processes, &node, NULL);
@@ -278,6 +273,25 @@ _reap_children(void *arg)
278273
}
279274
}
280275

276+
}
277+
278+
static void *
279+
_reap_children(void *arg)
280+
{
281+
(void) arg;
282+
struct sigaction sa = {};
283+
284+
(void) sigfillset(&sa.sa_mask);
285+
(void) sigdelset(&sa.sa_mask, SIGCHLD);
286+
(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
287+
288+
(void) sigemptyset(&sa.sa_mask);
289+
sa.sa_handler = _nop;
290+
sa.sa_flags = SA_NOCLDSTOP;
291+
(void) sigaction(SIGCHLD, &sa, NULL);
292+
293+
wait_for_children(B_TRUE, B_FALSE);
294+
281295
return (NULL);
282296
}
283297

@@ -306,6 +320,45 @@ zed_exec_fini(void)
306320
_reap_children_tid = (pthread_t)-1;
307321
}
308322

323+
/*
324+
* Check if the zedlet name indicates if it is a synchronous zedlet
325+
*
326+
* Synchronous zedlets have a "-sync-" immediately following the event name in
327+
* their zedlet filename, like:
328+
*
329+
* EVENT_NAME-sync-ZEDLETNAME.sh
330+
*
331+
* For example, if you wanted a synchronous statechange script:
332+
*
333+
* statechange-sync-myzedlet.sh
334+
*
335+
* Synchronous zedlets are guaranteed to be the only zedlet running. No other
336+
* zedlets may run in parallel with a synchronous zedlet. A synchronously
337+
* zedlet will wait for all previously spawned zedlets to finish before running.
338+
* Users should be careful to only use synchronous zedlets when needed, since
339+
* they decrease parallelism.
340+
*/
341+
static boolean_t
342+
zedlet_is_sync(const char *zedlet, const char *event)
343+
{
344+
const char *sync_str = "-sync-";
345+
size_t sync_str_len;
346+
size_t zedlet_len;
347+
size_t event_len;
348+
349+
sync_str_len = strlen(sync_str);
350+
zedlet_len = strlen(zedlet);
351+
event_len = strlen(event);
352+
353+
if (event_len + sync_str_len >= zedlet_len)
354+
return (B_FALSE);
355+
356+
if (strncmp(&zedlet[event_len], sync_str, sync_str_len) == 0)
357+
return (B_TRUE);
358+
359+
return (B_FALSE);
360+
}
361+
309362
/*
310363
* Process the event [eid] by synchronously invoking all zedlets with a
311364
* matching class prefix.
@@ -368,9 +421,28 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
368421
z = zed_strings_next(zcp->zedlets)) {
369422
for (csp = class_strings; *csp; csp++) {
370423
n = strlen(*csp);
371-
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
424+
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) {
425+
boolean_t is_sync = zedlet_is_sync(z, *csp);
426+
427+
if (is_sync) {
428+
/*
429+
* Wait for previous zedlets to
430+
* finish
431+
*/
432+
wait_for_children(B_FALSE, B_TRUE);
433+
}
434+
372435
_zed_exec_fork_child(eid, zcp->zedlet_dir,
373436
z, e, zcp->zevent_fd, zcp->do_foreground);
437+
438+
if (is_sync) {
439+
/*
440+
* Wait for sync zedlet we just launched
441+
* to finish.
442+
*/
443+
wait_for_children(B_FALSE, B_TRUE);
444+
}
445+
}
374446
}
375447
}
376448
free(e);

man/man8/zed.8.in

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ Multiple ZEDLETs may be invoked for a given zevent.
158158
ZEDLETs are executables invoked by the ZED in response to a given zevent.
159159
They should be written under the presumption they can be invoked concurrently,
160160
and they should use appropriate locking to access any shared resources.
161+
The one exception to this are "synchronous zedlets", which are described later
162+
in this page.
161163
Common variables used by ZEDLETs can be stored in the default rc file which
162164
is sourced by scripts; these variables should be prefixed with
163165
.Sy ZED_ .
@@ -233,6 +235,36 @@ and
233235
.Sy ZPOOL .
234236
These variables may be overridden in the rc file.
235237
.
238+
.Sh Synchronous ZEDLETS
239+
ZED's normal behavior is to spawn off zedlets in parallel and ignore their
240+
completion order.
241+
This means that ZED can potentially
242+
have zedlets for event ID number 2 starting before zedlets for event ID number
243+
1 have finished.
244+
Most of the time this is fine, and it actually helps when the system is getting
245+
hammered with hundreds of events.
246+
.Pp
247+
However, there are times when you want your zedlets to be executed in sequence
248+
with the event ID.
249+
That is where synchronous zedlets come in.
250+
.Pp
251+
ZED will wait for all previously spawned zedlets to finish before running
252+
a synchronous zedlet.
253+
Synchronous zedlets are guaranteed to be the only
254+
zedlet running.
255+
No other zedlets may run in parallel with a synchronous zedlet.
256+
Users should be careful to only use synchronous zedlets when needed, since
257+
they decrease parallelism.
258+
.Pp
259+
To make a zedlet synchronous, simply add a "-sync-" immediately following the
260+
event name in the zedlet's file name:
261+
.Pp
262+
.Sy EVENT_NAME-sync-ZEDLETNAME.sh
263+
.Pp
264+
For example, if you wanted a synchronous statechange script:
265+
.Pp
266+
.Sy statechange-sync-myzedlet.sh
267+
.
236268
.Sh FILES
237269
.Bl -tag -width "-c"
238270
.It Pa @sysconfdir@/zfs/zed.d

tests/runfiles/linux.run

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ tags = ['functional', 'direct']
109109
[tests/functional/events:Linux]
110110
tests = ['events_001_pos', 'events_002_pos', 'zed_rc_filter', 'zed_fd_spill',
111111
'zed_cksum_reported', 'zed_cksum_config', 'zed_io_config',
112-
'zed_slow_io', 'zed_slow_io_many_vdevs', 'zed_diagnose_multiple']
112+
'zed_slow_io', 'zed_slow_io_many_vdevs', 'zed_diagnose_multiple',
113+
'zed_synchronous_zedlet']
113114
tags = ['functional', 'events']
114115

115116
[tests/functional/fadvise:Linux]

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
15181518
functional/events/zed_rc_filter.ksh \
15191519
functional/events/zed_slow_io.ksh \
15201520
functional/events/zed_slow_io_many_vdevs.ksh \
1521+
functional/events/zed_synchronous_zedlet.ksh \
15211522
functional/exec/cleanup.ksh \
15221523
functional/exec/exec_001_pos.ksh \
15231524
functional/exec/exec_002_neg.ksh \

0 commit comments

Comments
 (0)