From 1c2ae0cd9474cbfa782bb94fa9ecd92436310198 Mon Sep 17 00:00:00 2001 From: fangxiuning Date: Wed, 15 Dec 2021 12:19:40 +0800 Subject: [PATCH] modify --- ...k-new-epoll-events-when-a-child-even.patch | 310 ++++++++++++++++++ systemd.spec | 9 +- 2 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 backport-sd-event-re-check-new-epoll-events-when-a-child-even.patch diff --git a/backport-sd-event-re-check-new-epoll-events-when-a-child-even.patch b/backport-sd-event-re-check-new-epoll-events-when-a-child-even.patch new file mode 100644 index 0000000..0938048 --- /dev/null +++ b/backport-sd-event-re-check-new-epoll-events-when-a-child-even.patch @@ -0,0 +1,310 @@ +From efd3be9de1dc07ec743912f3c166bbf17dbb20f5 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 8 Mar 2021 15:39:53 +0900 +Subject: [PATCH] sd-event: re-check new epoll events when a child event is + queued + +Previously, when a process outputs something and exit just after +epoll_wait() but before process_child(), then the IO event is ignored +even if the IO event has higher priority. See #18190. + +This can be solved by checking epoll event again after process_child(). + +However, there exists a possibility that another process outputs and +exits just after process_child() but before the second epoll_wait(). +When the IO event has lower priority than the child event, still IO +event is processed. + +So, this makes new epoll events and child events are checked in a loop +until no new event is detected. To prevent an infinite loop, the number +of maximum trial is set to 10. + +Fixes #18190. +--- + src/libsystemd/sd-event/sd-event.c | 141 +++++++++++++++++++++++++++---------- + 1 file changed, 103 insertions(+), 38 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 7dd43f2..5ced128 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -942,7 +942,7 @@ static int source_set_pending(sd_event_source *s, bool b) { + } + } + +- return 0; ++ return 1; + } + + static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { +@@ -2868,11 +2868,19 @@ static int process_timer( + return 0; + } + +-static int process_child(sd_event *e) { ++static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) { ++ int64_t min_priority = threshold; ++ bool something_new = false; + sd_event_source *s; + int r; + + assert(e); ++ assert(ret_min_priority); ++ ++ if (!e->need_process_child) { ++ *ret_min_priority = min_priority; ++ return 0; ++ } + + e->need_process_child = false; + +@@ -2897,6 +2905,9 @@ static int process_child(sd_event *e) { + HASHMAP_FOREACH(s, e->child_sources) { + assert(s->type == SOURCE_CHILD); + ++ if (s->priority > threshold) ++ continue; ++ + if (s->pending) + continue; + +@@ -2934,10 +2945,15 @@ static int process_child(sd_event *e) { + r = source_set_pending(s, true); + if (r < 0) + return r; ++ if (r > 0) { ++ something_new = true; ++ min_priority = MIN(min_priority, s->priority); ++ } + } + } + +- return 0; ++ *ret_min_priority = min_priority; ++ return something_new; + } + + static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) { +@@ -2967,13 +2983,13 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) { + return source_set_pending(s, true); + } + +-static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { +- bool read_one = false; ++static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, int64_t *min_priority) { + int r; + + assert(e); + assert(d); + assert_return(events == EPOLLIN, -EIO); ++ assert(min_priority); + + /* If there's a signal queued on this priority and SIGCHLD is + on this priority too, then make sure to recheck the +@@ -2999,7 +3015,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { + n = read(d->fd, &si, sizeof(si)); + if (n < 0) { + if (IN_SET(errno, EAGAIN, EINTR)) +- return read_one; ++ return 0; + + return -errno; + } +@@ -3009,8 +3025,6 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { + + assert(SIGNAL_VALID(si.ssi_signo)); + +- read_one = true; +- + if (e->signal_sources) + s = e->signal_sources[si.ssi_signo]; + if (!s) +@@ -3024,12 +3038,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { + r = source_set_pending(s, true); + if (r < 0) + return r; ++ if (r > 0 && *min_priority >= s->priority) { ++ *min_priority = s->priority; ++ return 1; /* an event source with smaller priority is queued. */ ++ } + +- return 1; ++ return 0; + } + } + +-static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents) { ++static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents, int64_t threshold) { + ssize_t n; + + assert(e); +@@ -3045,6 +3063,9 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t + if (d->buffer_filled > 0) + return 0; + ++ if (d->priority > threshold) ++ return 0; ++ + n = read(d->fd, &d->buffer, sizeof(d->buffer)); + if (n < 0) { + if (IN_SET(errno, EAGAIN, EINTR)) +@@ -3507,20 +3528,14 @@ pending: + return r; + } + +-_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { ++static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) { ++ int64_t min_priority = threshold; ++ bool something_new = false; + size_t event_queue_max; + int r, m, i; + +- assert_return(e, -EINVAL); +- assert_return(e = event_resolve(e), -ENOPKG); +- assert_return(!event_pid_changed(e), -ECHILD); +- assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); +- assert_return(e->state == SD_EVENT_ARMED, -EBUSY); +- +- if (e->exit_requested) { +- e->state = SD_EVENT_PENDING; +- return 1; +- } ++ assert(e); ++ assert(ret_min_priority); + + event_queue_max = MAX(e->n_sources, 1u); + if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, event_queue_max)) +@@ -3533,16 +3548,12 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + m = epoll_wait(e->epoll_fd, e->event_queue, event_queue_max, + timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC)); + if (m < 0) { +- if (errno == EINTR) { +- e->state = SD_EVENT_PENDING; +- return 1; +- } +- +- r = -errno; +- goto finish; ++ return m; + } + +- triple_timestamp_get(&e->timestamp); ++ /* Set timestamp only when this is called first time. */ ++ if (threshold == INT64_MAX) ++ triple_timestamp_get(&e->timestamp); + + for (i = 0; i < m; i++) { + +@@ -3558,6 +3569,11 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + + assert(s); + ++ if (s->priority > threshold) ++ continue; ++ ++ min_priority = MIN(min_priority, s->priority); ++ + switch (s->type) { + + case SOURCE_IO: +@@ -3585,11 +3601,11 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + } + + case WAKEUP_SIGNAL_DATA: +- r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events); ++ r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events, &min_priority); + break; + + case WAKEUP_INOTIFY_DATA: +- r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events); ++ r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events, threshold); + break; + + default: +@@ -3597,7 +3613,63 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + } + } + if (r < 0) ++ return r; ++ if (r > 0) ++ something_new = true; ++ } ++ ++ *ret_min_priority = min_priority; ++ return something_new; ++} ++ ++_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { ++ int r; ++ ++ assert_return(e, -EINVAL); ++ assert_return(e = event_resolve(e), -ENOPKG); ++ assert_return(!event_pid_changed(e), -ECHILD); ++ assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); ++ assert_return(e->state == SD_EVENT_ARMED, -EBUSY); ++ ++ if (e->exit_requested) { ++ e->state = SD_EVENT_PENDING; ++ return 1; ++ } ++ ++ for (int64_t threshold = INT64_MAX; ; threshold--) { ++ int64_t epoll_min_priority, child_min_priority; ++ ++ /* There may be a possibility that new epoll (especially IO) and child events are ++ * triggered just after process_epoll() call but before process_child(), and the new IO ++ * events may have higher priority than the child events. To salvage these events, ++ * let's call epoll_wait() again, but accepts only events with higher priority than the ++ * previous. See issue https://github.com/systemd/systemd/issues/18190 and comments ++ * https://github.com/systemd/systemd/pull/18750#issuecomment-785801085 ++ * https://github.com/systemd/systemd/pull/18922#issuecomment-792825226 */ ++ ++ r = process_epoll(e, timeout, threshold, &epoll_min_priority); ++ if (r == -EINTR) { ++ e->state = SD_EVENT_PENDING; ++ return 1; ++ } ++ if (r < 0) ++ goto finish; ++ if (r == 0 && threshold < INT64_MAX) ++ /* No new epoll event. */ ++ break; ++ ++ r = process_child(e, threshold, &child_min_priority); ++ if (r < 0) + goto finish; ++ if (r == 0) ++ /* No new child event. */ ++ break; ++ ++ threshold = MIN(epoll_min_priority, child_min_priority); ++ if (threshold == INT64_MIN) ++ break; ++ ++ timeout = 0; + } + + r = process_watchdog(e); +@@ -3624,19 +3696,12 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + if (r < 0) + goto finish; + +- if (e->need_process_child) { +- r = process_child(e); +- if (r < 0) +- goto finish; +- } +- + r = process_inotify(e); + if (r < 0) + goto finish; + + if (event_next_pending(e)) { + e->state = SD_EVENT_PENDING; +- + return 1; + } + +-- +1.8.3.1 + diff --git a/systemd.spec b/systemd.spec index 38e435b..cfc580f 100644 --- a/systemd.spec +++ b/systemd.spec @@ -20,7 +20,7 @@ Name: systemd Url: https://www.freedesktop.org/wiki/Software/systemd Version: 246 -Release: 16 +Release: 17 License: MIT and LGPLv2+ and GPLv2+ Summary: System and Service Manager @@ -75,6 +75,7 @@ Patch0021: scope-on-unified-make-sure-to-unwatch-all-PIDs-once-.patch Patch6001: backport-RFC-Make-user-instance-aware-of-delegated-cgroup-controllers.patch Patch6002: backport-core-Make-user-instance-aware-of-delegated-cgroup.patch +Patch6003: backport-sd-event-re-check-new-epoll-events-when-a-child-even.patch BuildRequires: gcc, gcc-c++ BuildRequires: libcap-devel, libmount-devel, pam-devel, libselinux-devel @@ -1491,6 +1492,12 @@ fi %exclude /usr/share/man/man3/* %changelog +* Wed Dec 15 2021 fangxiuning - 246-17 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: sd-event: re-check new epoll events when a child event is queued + * Wed Mar 3 2021 shenyangyang - 246-16 - Type:bugfix - ID:NA -- Gitee