Merge pull request #19959 from yuwata/udev-reduce-loop

udev: reduce number of times checking event dependency
This commit is contained in:
Lennart Poettering
2021-08-10 13:28:48 +02:00
committed by GitHub
10 changed files with 417 additions and 373 deletions

View File

@@ -142,11 +142,8 @@
#define LIST_FOREACH_SAFE(name,i,n,head) \
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
#define LIST_FOREACH_BEFORE(name,i,p) \
for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
#define LIST_FOREACH_AFTER(name,i,p) \
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
#define LIST_FOREACH_BACKWARDS(name,i,p) \
for ((i) = (p); (i); (i) = (i)->name##_prev)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \

View File

@@ -771,11 +771,11 @@ static Unit *device_following(Unit *u) {
return NULL;
/* Make everybody follow the unit that's named after the sysfs path */
LIST_FOREACH_AFTER(same_sysfs, other, d)
LIST_FOREACH(same_sysfs, other, d->same_sysfs_next)
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
LIST_FOREACH_BEFORE(same_sysfs, other, d) {
LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
@@ -802,13 +802,13 @@ static int device_following_set(Unit *u, Set **_set) {
if (!set)
return -ENOMEM;
LIST_FOREACH_AFTER(same_sysfs, other, d) {
LIST_FOREACH(same_sysfs, other, d->same_sysfs_next) {
r = set_put(set, other);
if (r < 0)
return r;
}
LIST_FOREACH_BEFORE(same_sysfs, other, d) {
LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
r = set_put(set, other);
if (r < 0)
return r;

View File

@@ -1323,11 +1323,11 @@ static Unit *swap_following(Unit *u) {
if (streq_ptr(s->what, s->devnode))
return NULL;
LIST_FOREACH_AFTER(same_devnode, other, s)
LIST_FOREACH(same_devnode, other, s->same_devnode_next)
if (streq_ptr(other->what, other->devnode))
return UNIT(other);
LIST_FOREACH_BEFORE(same_devnode, other, s) {
LIST_FOREACH_BACKWARDS(same_devnode, other, s->same_devnode_prev) {
if (streq_ptr(other->what, other->devnode))
return UNIT(other);

View File

@@ -23,14 +23,14 @@
/* wire protocol magic must match */
#define UDEV_CTRL_MAGIC 0xdead1dea
struct udev_ctrl_msg_wire {
typedef struct UdevCtrlMessageWire {
char version[16];
unsigned magic;
enum udev_ctrl_msg_type type;
union udev_ctrl_msg_value value;
};
UdevCtrlMessageType type;
UdevCtrlMessageValue value;
} UdevCtrlMessageWire;
struct udev_ctrl {
struct UdevCtrl {
unsigned n_ref;
int sock;
int sock_connect;
@@ -47,9 +47,9 @@ struct udev_ctrl {
void *userdata;
};
int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
_cleanup_close_ int sock = -1;
struct udev_ctrl *uctrl;
UdevCtrl *uctrl;
assert(ret);
@@ -59,11 +59,11 @@ int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
return log_error_errno(errno, "Failed to create socket: %m");
}
uctrl = new(struct udev_ctrl, 1);
uctrl = new(UdevCtrl, 1);
if (!uctrl)
return -ENOMEM;
*uctrl = (struct udev_ctrl) {
*uctrl = (UdevCtrl) {
.n_ref = 1,
.sock = fd >= 0 ? fd : TAKE_FD(sock),
.sock_connect = -1,
@@ -81,7 +81,7 @@ int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
return 0;
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
int r;
assert(uctrl);
@@ -107,7 +107,7 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
return 0;
}
static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) {
static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
if (!uctrl)
return;
@@ -115,7 +115,7 @@ static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) {
uctrl->sock_connect = safe_close(uctrl->sock_connect);
}
static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
assert(uctrl);
udev_ctrl_disconnect(uctrl);
@@ -127,9 +127,9 @@ static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
return mfree(uctrl);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free);
DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
int udev_ctrl_cleanup(UdevCtrl *uctrl) {
if (!uctrl)
return 0;
if (uctrl->cleanup_socket)
@@ -137,7 +137,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
return 0;
}
int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) {
int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
int r;
assert_return(uctrl, -EINVAL);
@@ -154,25 +154,25 @@ int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) {
return 0;
}
sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl) {
sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
assert(uctrl);
return uctrl->event_source;
}
static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl *uctrl) {
static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
udev_ctrl_disconnect(uctrl);
udev_ctrl_unref(uctrl);
(void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
/* We don't return NULL here because uctrl is not freed */
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct udev_ctrl*, udev_ctrl_disconnect_and_listen_again, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL;
struct udev_ctrl_msg_wire msg_wire;
struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire));
_cleanup_(udev_ctrl_disconnect_and_listen_againp) UdevCtrl *uctrl = NULL;
UdevCtrlMessageWire msg_wire;
struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(UdevCtrlMessageWire));
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
struct msghdr smsg = {
.msg_iov = &iov,
@@ -235,7 +235,7 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32
}
static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
struct udev_ctrl *uctrl = userdata;
UdevCtrl *uctrl = userdata;
_cleanup_close_ int sock = -1;
struct ucred ucred;
int r;
@@ -282,7 +282,7 @@ static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
int r;
assert(uctrl);
@@ -309,8 +309,8 @@ int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void
return 0;
}
int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) {
struct udev_ctrl_msg_wire ctrl_msg_wire = {
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf) {
UdevCtrlMessageWire ctrl_msg_wire = {
.version = "udev-" STRINGIFY(PROJECT_VERSION),
.magic = UDEV_CTRL_MAGIC,
.type = type,
@@ -339,7 +339,7 @@ int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int in
return 0;
}
int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) {
int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
_cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
int r;

View File

@@ -6,9 +6,9 @@
#include "macro.h"
#include "time-util.h"
struct udev_ctrl;
typedef struct UdevCtrl UdevCtrl;
enum udev_ctrl_msg_type {
typedef enum UdevCtrlMessageType {
_UDEV_CTRL_END_MESSAGES,
UDEV_CTRL_SET_LOG_LEVEL,
UDEV_CTRL_STOP_EXEC_QUEUE,
@@ -18,62 +18,62 @@ enum udev_ctrl_msg_type {
UDEV_CTRL_SET_CHILDREN_MAX,
UDEV_CTRL_PING,
UDEV_CTRL_EXIT,
};
} UdevCtrlMessageType;
union udev_ctrl_msg_value {
typedef union UdevCtrlMessageValue {
int intval;
char buf[256];
};
} UdevCtrlMessageValue;
typedef int (*udev_ctrl_handler_t)(struct udev_ctrl *udev_ctrl, enum udev_ctrl_msg_type type,
const union udev_ctrl_msg_value *value, void *userdata);
typedef int (*udev_ctrl_handler_t)(UdevCtrl *udev_ctrl, UdevCtrlMessageType type,
const UdevCtrlMessageValue *value, void *userdata);
int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd);
static inline int udev_ctrl_new(struct udev_ctrl **ret) {
int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd);
static inline int udev_ctrl_new(UdevCtrl **ret) {
return udev_ctrl_new_from_fd(ret, -1);
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event);
int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl);
int udev_ctrl_enable_receiving(UdevCtrl *uctrl);
UdevCtrl *udev_ctrl_ref(UdevCtrl *uctrl);
UdevCtrl *udev_ctrl_unref(UdevCtrl *uctrl);
int udev_ctrl_cleanup(UdevCtrl *uctrl);
int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event);
int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl);
int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout);
int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout);
int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf);
static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) {
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf);
static inline int udev_ctrl_send_set_log_level(UdevCtrl *uctrl, int priority) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
}
static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) {
static inline int udev_ctrl_send_stop_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
}
static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) {
static inline int udev_ctrl_send_start_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
}
static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) {
static inline int udev_ctrl_send_reload(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL);
}
static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) {
static inline int udev_ctrl_send_set_env(UdevCtrl *uctrl, const char *key) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
}
static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) {
static inline int udev_ctrl_send_set_children_max(UdevCtrl *uctrl, int count) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
}
static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) {
static inline int udev_ctrl_send_ping(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL);
}
static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) {
static inline int udev_ctrl_send_exit(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevCtrl*, udev_ctrl_unref);

View File

@@ -1154,7 +1154,7 @@ static void rule_resolve_goto(UdevRuleFile *rule_file) {
if (!FLAGS_SET(line->type, LINE_HAS_GOTO))
continue;
LIST_FOREACH_AFTER(rule_lines, i, line)
LIST_FOREACH(rule_lines, i, line->rule_lines_next)
if (streq_ptr(i->label, line->goto_label)) {
line->goto_line = i;
break;

View File

@@ -48,7 +48,7 @@ static int help(void) {
}
int control_main(int argc, char *argv[], void *userdata) {
_cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
_cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
usec_t timeout = 60 * USEC_PER_SEC;
int c, r;

View File

@@ -176,7 +176,7 @@ int settle_main(int argc, char *argv[], void *userdata) {
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
_cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
_cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
if (udev_ctrl_new(&uctrl) >= 0) {
r = udev_ctrl_send_ping(uctrl);

View File

@@ -421,7 +421,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
}
if (ping) {
_cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
_cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
r = udev_ctrl_new(&uctrl);
if (r < 0)

View File

@@ -77,10 +77,13 @@ static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
static int arg_timeout_signal = SIGKILL;
static bool arg_blockdev_read_only = false;
typedef struct Event Event;
typedef struct Worker Worker;
typedef struct Manager {
sd_event *event;
Hashmap *workers;
LIST_HEAD(struct event, events);
LIST_HEAD(Event, events);
const char *cgroup;
pid_t pid; /* the process that originally allocated the manager object */
int log_level;
@@ -91,7 +94,7 @@ typedef struct Manager {
sd_netlink *rtnl;
sd_device_monitor *monitor;
struct udev_ctrl *ctrl;
UdevCtrl *ctrl;
int worker_watch[2];
/* used by udev-watch */
@@ -106,54 +109,52 @@ typedef struct Manager {
bool exit;
} Manager;
enum event_state {
typedef enum EventState {
EVENT_UNDEF,
EVENT_QUEUED,
EVENT_RUNNING,
};
} EventState;
struct event {
typedef struct Event {
Manager *manager;
struct worker *worker;
enum event_state state;
Worker *worker;
EventState state;
sd_device *dev;
sd_device *dev_kernel; /* clone of originally received device */
uint64_t seqnum;
uint64_t delaying_seqnum;
uint64_t blocker_seqnum;
sd_event_source *timeout_warning_event;
sd_event_source *timeout_event;
LIST_FIELDS(struct event, event);
};
LIST_FIELDS(Event, event);
} Event;
static void event_queue_cleanup(Manager *manager, enum event_state type);
enum worker_state {
typedef enum WorkerState {
WORKER_UNDEF,
WORKER_RUNNING,
WORKER_IDLE,
WORKER_KILLED,
WORKER_KILLING,
};
} WorkerState;
struct worker {
typedef struct Worker {
Manager *manager;
pid_t pid;
sd_device_monitor *monitor;
enum worker_state state;
struct event *event;
};
WorkerState state;
Event *event;
} Worker;
/* passed from worker to main process */
struct worker_message {
};
typedef struct WorkerMessage {
} WorkerMessage;
static void event_free(struct event *event) {
static Event *event_free(Event *event) {
if (!event)
return;
return NULL;
assert(event->manager);
@@ -170,13 +171,24 @@ static void event_free(struct event *event) {
/* only clean up the queue from the process that created it */
if (LIST_IS_EMPTY(event->manager->events) &&
event->manager->pid == getpid_cached())
if (unlink("/run/udev/queue") < 0)
log_warning_errno(errno, "Failed to unlink /run/udev/queue: %m");
if (unlink("/run/udev/queue") < 0 && errno != ENOENT)
log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m");
free(event);
return mfree(event);
}
static struct worker* worker_free(struct worker *worker) {
static void event_queue_cleanup(Manager *manager, EventState match_state) {
Event *event, *tmp;
LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
if (match_state != EVENT_UNDEF && match_state != event->state)
continue;
event_free(event);
}
}
static Worker *worker_free(Worker *worker) {
if (!worker)
return NULL;
@@ -189,89 +201,8 @@ static struct worker* worker_free(struct worker *worker) {
return mfree(worker);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct worker *, worker_free);
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, struct worker, worker_free);
static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
_cleanup_(worker_freep) struct worker *worker = NULL;
int r;
assert(ret);
assert(manager);
assert(worker_monitor);
assert(pid > 1);
/* close monitor, but keep address around */
device_monitor_disconnect(worker_monitor);
worker = new(struct worker, 1);
if (!worker)
return -ENOMEM;
*worker = (struct worker) {
.manager = manager,
.monitor = sd_device_monitor_ref(worker_monitor),
.pid = pid,
};
r = hashmap_ensure_put(&manager->workers, &worker_hash_op, PID_TO_PTR(pid), worker);
if (r < 0)
return r;
*ret = TAKE_PTR(worker);
return 0;
}
static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
struct event *event = userdata;
assert(event);
assert(event->worker);
kill_and_sigcont(event->worker->pid, arg_timeout_signal);
event->worker->state = WORKER_KILLED;
log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
return 1;
}
static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
struct event *event = userdata;
assert(event);
assert(event->worker);
log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
return 1;
}
static void worker_attach_event(struct worker *worker, struct event *event) {
sd_event *e;
assert(worker);
assert(worker->manager);
assert(event);
assert(!event->worker);
assert(!worker->event);
worker->state = WORKER_RUNNING;
worker->event = event;
event->state = EVENT_RUNNING;
event->worker = worker;
e = worker->manager->event;
(void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
on_event_timeout_warning, event);
(void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
arg_event_timeout_usec, USEC_PER_SEC,
on_event_timeout, event);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(Worker*, worker_free);
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, Worker, worker_free);
static void manager_clear_for_worker(Manager *manager) {
assert(manager);
@@ -314,8 +245,109 @@ static Manager* manager_free(Manager *manager) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
_cleanup_(worker_freep) Worker *worker = NULL;
int r;
assert(ret);
assert(manager);
assert(worker_monitor);
assert(pid > 1);
/* close monitor, but keep address around */
device_monitor_disconnect(worker_monitor);
worker = new(Worker, 1);
if (!worker)
return -ENOMEM;
*worker = (Worker) {
.manager = manager,
.monitor = sd_device_monitor_ref(worker_monitor),
.pid = pid,
};
r = hashmap_ensure_put(&manager->workers, &worker_hash_op, PID_TO_PTR(pid), worker);
if (r < 0)
return r;
*ret = TAKE_PTR(worker);
return 0;
}
static void manager_kill_workers(Manager *manager, bool force) {
Worker *worker;
assert(manager);
HASHMAP_FOREACH(worker, manager->workers) {
if (worker->state == WORKER_KILLED)
continue;
if (worker->state == WORKER_RUNNING && !force) {
worker->state = WORKER_KILLING;
continue;
}
worker->state = WORKER_KILLED;
(void) kill(worker->pid, SIGTERM);
}
}
static void manager_exit(Manager *manager) {
assert(manager);
manager->exit = true;
sd_notify(false,
"STOPPING=1\n"
"STATUS=Starting shutdown...");
/* close sources of new events and discard buffered events */
manager->ctrl = udev_ctrl_unref(manager->ctrl);
manager->inotify_event = sd_event_source_unref(manager->inotify_event);
manager->inotify_fd = safe_close(manager->inotify_fd);
manager->monitor = sd_device_monitor_unref(manager->monitor);
/* discard queued events and kill workers */
event_queue_cleanup(manager, EVENT_QUEUED);
manager_kill_workers(manager, true);
}
/* reload requested, HUP signal received, rules changed, builtin changed */
static void manager_reload(Manager *manager) {
assert(manager);
sd_notify(false,
"RELOADING=1\n"
"STATUS=Flushing configuration...");
manager_kill_workers(manager, false);
manager->rules = udev_rules_free(manager->rules);
udev_builtin_exit();
sd_notifyf(false,
"READY=1\n"
"STATUS=Processing with %u children at max", arg_children_max);
}
static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *manager = userdata;
assert(manager);
log_debug("Cleanup idle workers");
manager_kill_workers(manager, false);
return 1;
}
static int worker_send_message(int fd) {
struct worker_message message = {};
WorkerMessage message = {};
return loop_write(fd, &message, sizeof(message), false);
}
@@ -591,9 +623,59 @@ static int worker_main(Manager *_manager, sd_device_monitor *monitor, sd_device
return 0;
}
static int worker_spawn(Manager *manager, struct event *event) {
static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
Event *event = userdata;
assert(event);
assert(event->worker);
kill_and_sigcont(event->worker->pid, arg_timeout_signal);
event->worker->state = WORKER_KILLED;
log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
return 1;
}
static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
Event *event = userdata;
assert(event);
assert(event->worker);
log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
return 1;
}
static void worker_attach_event(Worker *worker, Event *event) {
sd_event *e;
assert(worker);
assert(worker->manager);
assert(event);
assert(!event->worker);
assert(!worker->event);
worker->state = WORKER_RUNNING;
worker->event = event;
event->state = EVENT_RUNNING;
event->worker = worker;
e = worker->manager->event;
(void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
on_event_timeout_warning, event);
(void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
arg_event_timeout_usec, USEC_PER_SEC,
on_event_timeout, event);
}
static int worker_spawn(Manager *manager, Event *event) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *worker_monitor = NULL;
struct worker *worker;
Worker *worker;
pid_t pid;
int r;
@@ -635,16 +717,18 @@ static int worker_spawn(Manager *manager, struct event *event) {
return 0;
}
static void event_run(Manager *manager, struct event *event) {
static int event_run(Event *event) {
static bool log_children_max_reached = true;
struct worker *worker;
Manager *manager;
Worker *worker;
int r;
assert(manager);
assert(event);
assert(event->manager);
log_device_uevent(event->dev, "Device ready for processing");
manager = event->manager;
HASHMAP_FOREACH(worker, manager->workers) {
if (worker->state != WORKER_IDLE)
continue;
@@ -658,110 +742,73 @@ static void event_run(Manager *manager, struct event *event) {
continue;
}
worker_attach_event(worker, event);
return;
return 1; /* event is now processing. */
}
if (hashmap_size(manager->workers) >= arg_children_max) {
/* Avoid spamming the debug logs if the limit is already reached and
* many events still need to be processed */
if (log_children_max_reached && arg_children_max > 1) {
log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
log_children_max_reached = false;
}
return;
return 0; /* no free worker */
}
/* Re-enable the debug message for the next batch of events */
log_children_max_reached = true;
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
and, until the next SELinux policy changes, we safe further reloads in future children */
* and, until the next SELinux policy changes, we safe further reloads in future children */
mac_selinux_maybe_reload();
/* start new worker and pass initial device */
worker_spawn(manager, event);
}
static int event_queue_insert(Manager *manager, sd_device *dev) {
_cleanup_(sd_device_unrefp) sd_device *clone = NULL;
struct event *event;
uint64_t seqnum;
int r;
assert(manager);
assert(dev);
/* only one process can add events to the queue */
assert(manager->pid == getpid_cached());
/* We only accepts devices received by device monitor. */
r = sd_device_get_seqnum(dev, &seqnum);
r = worker_spawn(manager, event);
if (r < 0)
return r;
/* Save original device to restore the state on failures. */
r = device_shallow_clone(dev, &clone);
if (r < 0)
return r;
r = device_copy_properties(clone, dev);
if (r < 0)
return r;
event = new(struct event, 1);
if (!event)
return -ENOMEM;
*event = (struct event) {
.manager = manager,
.dev = sd_device_ref(dev),
.dev_kernel = TAKE_PTR(clone),
.seqnum = seqnum,
.state = EVENT_QUEUED,
};
if (LIST_IS_EMPTY(manager->events)) {
r = touch("/run/udev/queue");
if (r < 0)
log_warning_errno(r, "Failed to touch /run/udev/queue: %m");
}
LIST_APPEND(event, manager->events, event);
log_device_uevent(dev, "Device is queued");
return 0;
return 1; /* event is now processing. */
}
static void manager_kill_workers(Manager *manager, bool force) {
struct worker *worker;
assert(manager);
HASHMAP_FOREACH(worker, manager->workers) {
if (worker->state == WORKER_KILLED)
continue;
if (worker->state == WORKER_RUNNING && !force) {
worker->state = WORKER_KILLING;
continue;
}
worker->state = WORKER_KILLED;
(void) kill(worker->pid, SIGTERM);
}
}
/* lookup event for identical, parent, child device */
static int is_device_busy(Manager *manager, struct event *event) {
static int event_is_blocked(Event *event) {
const char *subsystem, *devpath, *devpath_old = NULL;
dev_t devnum = makedev(0, 0);
struct event *loop_event;
Event *loop_event;
size_t devpath_len;
int r, ifindex = 0;
bool is_block;
/* lookup event for identical, parent, child device */
assert(event);
assert(event->manager);
assert(event->blocker_seqnum <= event->seqnum);
if (event->blocker_seqnum == event->seqnum)
/* we have checked previously and no blocker found */
return false;
LIST_FOREACH(event, loop_event, event->manager->events) {
/* we already found a later event, earlier cannot block us, no need to check again */
if (loop_event->seqnum < event->blocker_seqnum)
continue;
/* event we checked earlier still exists, no need to check again */
if (loop_event->seqnum == event->blocker_seqnum)
return true;
/* found ourself, no later event can block us */
if (loop_event->seqnum >= event->seqnum)
goto no_blocker;
/* found event we have not checked */
break;
}
assert(loop_event);
assert(loop_event->seqnum > event->blocker_seqnum &&
loop_event->seqnum < event->seqnum);
r = sd_device_get_subsystem(event->dev, &subsystem);
if (r < 0)
return r;
@@ -787,21 +834,13 @@ static int is_device_busy(Manager *manager, struct event *event) {
return r;
/* check if queue contains events we depend on */
LIST_FOREACH(event, loop_event, manager->events) {
LIST_FOREACH(event, loop_event, loop_event) {
size_t loop_devpath_len, common;
const char *loop_devpath;
/* we already found a later event, earlier cannot block us, no need to check again */
if (loop_event->seqnum < event->delaying_seqnum)
continue;
/* event we checked earlier still exists, no need to check again */
if (loop_event->seqnum == event->delaying_seqnum)
return true;
/* found ourself, no later event can block us */
if (loop_event->seqnum >= event->seqnum)
break;
goto no_blocker;
/* check major/minor */
if (major(devnum) != 0) {
@@ -813,7 +852,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
if (sd_device_get_devnum(loop_event->dev, &d) >= 0 &&
devnum == d && is_block == streq(s, "block"))
goto set_delaying_seqnum;
break;
}
/* check network device ifindex */
@@ -822,7 +861,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 &&
ifindex == i)
goto set_delaying_seqnum;
break;
}
if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0)
@@ -830,7 +869,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
/* check our old name */
if (devpath_old && streq(devpath_old, loop_devpath))
goto set_delaying_seqnum;
break;
loop_devpath_len = strlen(loop_devpath);
@@ -843,80 +882,32 @@ static int is_device_busy(Manager *manager, struct event *event) {
/* identical device event found */
if (devpath_len == loop_devpath_len)
goto set_delaying_seqnum;
break;
/* parent device event found */
if (devpath[common] == '/')
goto set_delaying_seqnum;
break;
/* child device event found */
if (loop_devpath[common] == '/')
goto set_delaying_seqnum;
break;
}
return false;
assert(loop_event);
set_delaying_seqnum:
log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
event->seqnum, loop_event->seqnum);
event->delaying_seqnum = loop_event->seqnum;
event->blocker_seqnum = loop_event->seqnum;
return true;
no_blocker:
event->blocker_seqnum = event->seqnum;
return false;
}
static void manager_exit(Manager *manager) {
assert(manager);
manager->exit = true;
sd_notify(false,
"STOPPING=1\n"
"STATUS=Starting shutdown...");
/* close sources of new events and discard buffered events */
manager->ctrl = udev_ctrl_unref(manager->ctrl);
manager->inotify_event = sd_event_source_unref(manager->inotify_event);
manager->inotify_fd = safe_close(manager->inotify_fd);
manager->monitor = sd_device_monitor_unref(manager->monitor);
/* discard queued events and kill workers */
event_queue_cleanup(manager, EVENT_QUEUED);
manager_kill_workers(manager, true);
}
/* reload requested, HUP signal received, rules changed, builtin changed */
static void manager_reload(Manager *manager) {
assert(manager);
sd_notify(false,
"RELOADING=1\n"
"STATUS=Flushing configuration...");
manager_kill_workers(manager, false);
manager->rules = udev_rules_free(manager->rules);
udev_builtin_exit();
sd_notifyf(false,
"READY=1\n"
"STATUS=Processing with %u children at max", arg_children_max);
}
static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *manager = userdata;
assert(manager);
log_debug("Cleanup idle workers");
manager_kill_workers(manager, false);
return 1;
}
static void event_queue_start(Manager *manager) {
struct event *event;
static int event_queue_start(Manager *manager) {
Event *event, *event_next;
usec_t usec;
int r;
@@ -924,12 +915,12 @@ static void event_queue_start(Manager *manager) {
if (LIST_IS_EMPTY(manager->events) ||
manager->exit || manager->stop_exec_queue)
return;
return 0;
assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
/* check for changed config, every 3 seconds at most */
if (manager->last_usec == 0 ||
usec - manager->last_usec > 3 * USEC_PER_SEC) {
usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
if (udev_rules_check_timestamp(manager->rules) ||
udev_builtin_validate())
manager_reload(manager);
@@ -945,33 +936,111 @@ static void event_queue_start(Manager *manager) {
if (!manager->rules) {
r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
if (r < 0) {
log_warning_errno(r, "Failed to read udev rules: %m");
return;
}
if (r < 0)
return log_warning_errno(r, "Failed to read udev rules: %m");
}
LIST_FOREACH(event, event, manager->events) {
LIST_FOREACH_SAFE(event, event, event_next, manager->events) {
if (event->state != EVENT_QUEUED)
continue;
/* do not start event if parent or child event is still running */
if (is_device_busy(manager, event) != 0)
/* do not start event if parent or child event is still running or queued */
r = event_is_blocked(event);
if (r < 0) {
sd_device_action_t a = _SD_DEVICE_ACTION_INVALID;
(void) sd_device_get_action(event->dev, &a);
log_device_warning_errno(event->dev, r,
"Failed to check event dependency, "
"skipping event (SEQNUM=%"PRIu64", ACTION=%s)",
event->seqnum,
strna(device_action_to_string(a)));
event_free(event);
return r;
}
if (r > 0)
continue;
event_run(manager, event);
r = event_run(event);
if (r <= 0)
return r;
}
return 0;
}
static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
struct event *event, *tmp;
static int event_queue_insert(Manager *manager, sd_device *dev) {
_cleanup_(sd_device_unrefp) sd_device *clone = NULL;
Event *event;
uint64_t seqnum;
int r;
LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
if (match_type != EVENT_UNDEF && match_type != event->state)
continue;
assert(manager);
assert(dev);
event_free(event);
/* only one process can add events to the queue */
assert(manager->pid == getpid_cached());
/* We only accepts devices received by device monitor. */
r = sd_device_get_seqnum(dev, &seqnum);
if (r < 0)
return r;
/* Save original device to restore the state on failures. */
r = device_shallow_clone(dev, &clone);
if (r < 0)
return r;
r = device_copy_properties(clone, dev);
if (r < 0)
return r;
event = new(Event, 1);
if (!event)
return -ENOMEM;
*event = (Event) {
.manager = manager,
.dev = sd_device_ref(dev),
.dev_kernel = TAKE_PTR(clone),
.seqnum = seqnum,
.state = EVENT_QUEUED,
};
if (LIST_IS_EMPTY(manager->events)) {
r = touch("/run/udev/queue");
if (r < 0)
log_warning_errno(r, "Failed to touch /run/udev/queue, ignoring: %m");
}
LIST_APPEND(event, manager->events, event);
log_device_uevent(dev, "Device is queued");
return 0;
}
static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *manager = userdata;
int r;
assert(manager);
DEVICE_TRACE_POINT(kernel_uevent_received, dev);
device_ensure_usec_initialized(dev, NULL);
r = event_queue_insert(manager, dev);
if (r < 0) {
log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
return 1;
}
/* we have fresh events, try to schedule them */
event_queue_start(manager);
return 1;
}
static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -980,7 +1049,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
assert(manager);
for (;;) {
struct worker_message msg;
WorkerMessage msg;
struct iovec iovec = {
.iov_base = &msg,
.iov_len = sizeof(msg),
@@ -994,7 +1063,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
};
ssize_t size;
struct ucred *ucred;
struct worker *worker;
Worker *worker;
size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
if (size == -EINTR)
@@ -1007,7 +1076,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
cmsg_close_all(&msghdr);
if (size != sizeof(struct worker_message)) {
if (size != sizeof(WorkerMessage)) {
log_warning("Ignoring worker message with invalid size %zi bytes", size);
continue;
}
@@ -1041,30 +1110,8 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
return 1;
}
static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *manager = userdata;
int r;
assert(manager);
DEVICE_TRACE_POINT(kernel_uevent_received, dev);
device_ensure_usec_initialized(dev, NULL);
r = event_queue_insert(manager, dev);
if (r < 0) {
log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
return 1;
}
/* we have fresh events, try to schedule them */
event_queue_start(manager);
return 1;
}
/* receive the udevd message from userspace */
static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, const union udev_ctrl_msg_value *value, void *userdata) {
static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) {
Manager *manager = userdata;
int r;
@@ -1357,7 +1404,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
for (;;) {
pid_t pid;
int status;
struct worker *worker;
Worker *worker;
pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)