Re: [GIT] Experimental threaded udev

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jun 2, 2009 at 13:39, Kay Sievers <kay.sievers@xxxxxxxx> wrote:
> On Tue, Jun 2, 2009 at 11:13, Alan Jenkins <alan-jenkins@xxxxxxxxxxxxxx> wrote:
>>> Version 3, which should clean up events with a worker that died. Also
>>> kills all workers if the config has changed.
>>
>> For strict correctness, I guess we need to do the same for "udevadm control
>> --log-priority" and "udevadm control --env".
>
> Yeah, makes sense, will add that.

Did that.

>> Hmm, it says worker_kill_idle().  If there are running events at the time,
>> their stale workers will survive.  At the moment kill(worker->pid, SIGTERM)
>> allows the current event to finish before it terminates, so one solution
>> would be to just kill all the workers.
>
> The main daemon will wakeup with a timeout as long as workers exists.
> There migt be several rounds of killing, until all workers are gone.

We kill all workers now.

Version 5. I guess we should go for the signalfd stuff, it looks prett
nice. The socketpair instead of the rtsignal is also nice, it allows
us to pass back arbitrary data from finished events to the main
daemon.

This is a full coldplug run for 520 devices:
  $ time udev/udevd
  []
  user 0m0.016s
  sys	0m0.049s

We need less than a second now (compared to 1.5 for the current version):
  $ time (udevadm trigger; udevadm settle)
  real	0m0.983s

It spawned 35 workers for 500 events:
  $ ps ax | grep udevd | wc -l
  35

Now that the page faults are gone, and the main daemon does not need
that much CPU anymore, all the silly things show up, like running a
few shell scripts. :)

  $ time (udevadm trigger; udevadm settle)
  real	0m0.959s

  $ mv /lib/udev/path_id /lib/udev/path_id.0

  $ time (udevadm trigger -Snet; udevadm settle)
  real	0m0.714s

Thanks,
Kay
diff --git a/NEWS b/NEWS
index ac44d7a..8e9a1bc 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ udev 143
 ========
 Bugfixes.
 
+Inotify support is required now to compile and run udev.
+
 The format of the queue exported by the udev damon has changed. There is
 no longer a /dev/.udev/queue/ directory. The queue can be accessed with
 udevadm settle and libudedv.
diff --git a/configure.ac b/configure.ac
index f1d008e..f126146 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,10 +23,6 @@ AC_SUBST(LIBUDEV_LT_AGE)
 
 AC_PATH_PROG([XSLTPROC], [xsltproc])
 
-AC_CHECK_LIB(c, inotify_init,
-	[AC_DEFINE([HAVE_INOTIFY], 1, [inotify available])],
-	[AC_MSG_WARN([inotify support disabled])])
-
 AC_ARG_WITH(udev-prefix,
 	AS_HELP_STRING([--with-udev-prefix=DIR], [add prefix to internal udev path names]),
 	[], [with_udev_prefix='${exec_prefix}'])
diff --git a/udev/Makefile.am b/udev/Makefile.am
index 6cd2f23..94989e6 100644
--- a/udev/Makefile.am
+++ b/udev/Makefile.am
@@ -14,7 +14,6 @@ common_ldadd =
 
 common_files = \
 	udev.h \
-	udev-sysdeps.h \
 	udev-event.c \
 	udev-watch.c \
 	udev-node.c \
diff --git a/udev/lib/libudev-monitor.c b/udev/lib/libudev-monitor.c
index 395a4d2..54c9576 100644
--- a/udev/lib/libudev-monitor.c
+++ b/udev/lib/libudev-monitor.c
@@ -32,7 +32,6 @@ struct udev_monitor {
 	int refcount;
 	int sock;
 	struct sockaddr_nl snl;
-	struct sockaddr_nl snl_peer;
 	struct sockaddr_un sun;
 	socklen_t addrlen;
 	struct udev_list_node filter_subsystem_list;
@@ -171,8 +170,8 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
 		return NULL;
 
 	if (name == NULL)
-		return NULL;
-	if (strcmp(name, "kernel") == 0)
+		group = 0;
+	else if (strcmp(name, "kernel") == 0)
 		group = UDEV_MONITOR_KERNEL;
 	else if (strcmp(name, "udev") == 0)
 		group = UDEV_MONITOR_UDEV;
@@ -193,8 +192,6 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
 
 	udev_monitor->snl.nl_family = AF_NETLINK;
 	udev_monitor->snl.nl_groups = group;
-	udev_monitor->snl_peer.nl_family = AF_NETLINK;
-	udev_monitor->snl_peer.nl_groups = UDEV_MONITOR_UDEV;
 
 	dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group);
 	return udev_monitor;
@@ -434,7 +431,6 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito
 	struct iovec iov;
 	char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
 	struct cmsghdr *cmsg;
-	struct sockaddr_nl snl;
 	struct ucred *cred;
 	char buf[8192];
 	ssize_t buflen;
@@ -459,11 +455,6 @@ retry:
 	smsg.msg_control = cred_msg;
 	smsg.msg_controllen = sizeof(cred_msg);
 
-	if (udev_monitor->snl.nl_family != 0) {
-		smsg.msg_name = &snl;
-		smsg.msg_namelen = sizeof(snl);
-	}
-
 	buflen = recvmsg(udev_monitor->sock, &smsg, 0);
 	if (buflen < 0) {
 		if (errno != EINTR)
@@ -476,20 +467,6 @@ retry:
 		return NULL;
 	}
 
-	if (udev_monitor->snl.nl_family != 0) {
-		if (snl.nl_groups == 0) {
-			info(udev_monitor->udev, "unicast netlink message ignored\n");
-			return NULL;
-		}
-		if (snl.nl_groups == UDEV_MONITOR_KERNEL) {
-			if (snl.nl_pid > 0) {
-				info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", snl.nl_pid);
-				return NULL;
-			}
-			is_kernel = 1;
-		}
-	}
-
 	cmsg = CMSG_FIRSTHDR(&smsg);
 	if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
 		info(udev_monitor->udev, "no sender credentials received, message ignored\n");
@@ -621,7 +598,7 @@ retry:
 	return udev_device;
 }
 
-int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
+int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_device *udev_device, pid_t pid)
 {
 	struct msghdr smsg;
 	struct iovec iov[2];
@@ -660,6 +637,7 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_devi
 	} else if (udev_monitor->snl.nl_family != 0) {
 		const char *val;
 		struct udev_monitor_netlink_header nlh;
+		struct sockaddr_nl snl_peer;
 
 
 		/* add versioned header */
@@ -680,11 +658,18 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_devi
 		iov[1].iov_base = (char *)buf;
 		iov[1].iov_len = blen;
 
+		/* we will always get ECONNREFUSED when sending to the muticast group */
+		memset(&snl_peer, 0x00, sizeof(struct sockaddr_nl));
+		snl_peer.nl_family = AF_NETLINK;
+		if (pid > 0)
+			snl_peer.nl_pid = pid;
+		else
+			snl_peer.nl_groups = UDEV_MONITOR_UDEV;
+
 		memset(&smsg, 0x00, sizeof(struct msghdr));
 		smsg.msg_iov = iov;
 		smsg.msg_iovlen = 2;
-		/* no destination besides the muticast group, we will always get ECONNREFUSED */
-		smsg.msg_name = &udev_monitor->snl_peer;
+		smsg.msg_name = &snl_peer;
 		smsg.msg_namelen = sizeof(struct sockaddr_nl);
 	} else {
 		return -1;
diff --git a/udev/lib/libudev-private.h b/udev/lib/libudev-private.h
index dc02a84..e600802 100644
--- a/udev/lib/libudev-private.h
+++ b/udev/lib/libudev-private.h
@@ -86,7 +86,7 @@ int udev_device_delete_db(struct udev_device *udev_device);
 int udev_device_rename_db(struct udev_device *udev_device, const char *devpath);
 
 /* libudev-monitor - netlink/unix socket communication  */
-int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_device *udev_device);
+int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_device *udev_device, pid_t pid);
 int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
 
 /* libudev-ctrl - daemon runtime setup */
diff --git a/udev/udev-event.c b/udev/udev-event.c
index d521251..8ab262a 100644
--- a/udev/udev-event.c
+++ b/udev/udev-event.c
@@ -734,18 +734,13 @@ int udev_event_execute_run(struct udev_event *event)
 			monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]);
 			if (monitor == NULL)
 				continue;
-			udev_monitor_send_device(monitor, event->dev);
+			udev_monitor_send_device(monitor, event->dev, 0);
 			udev_monitor_unref(monitor);
 		} else {
 			char program[UTIL_PATH_SIZE];
 			char **envp;
 
 			udev_event_apply_format(event, cmd, program, sizeof(program));
-			if (event->trace)
-				fprintf(stderr, "run  %s (%llu) '%s'\n",
-				       udev_device_get_syspath(event->dev),
-				       udev_device_get_seqnum(event->dev),
-				       program);
 			envp = udev_device_get_properties_envp(event->dev);
 			if (util_run_program(event->udev, program, envp, NULL, 0, NULL) != 0) {
 				if (!udev_list_entry_get_flag(list_entry))
diff --git a/udev/udev-sysdeps.h b/udev/udev-sysdeps.h
deleted file mode 100644
index 35671ba..0000000
--- a/udev/udev-sysdeps.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * wrapping of libc features and kernel interfaces
- *
- * Copyright (C) 2005-2008 Kay Sievers <kay.sievers@xxxxxxxx>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _UDEV_SYSDEPS_H_
-#define _UDEV_SYSDEPS_H_
-
-#include <stdint.h>
-#include <errno.h>
-
-#ifndef HAVE_INOTIFY
-static inline int inotify_init(void)
-{
-	errno = ENOSYS;
-	return -1;
-}
-
-static inline int inotify_add_watch(int fd, const char *name, uint32_t mask)
-{
-	return -1;
-}
-
-#define IN_CREATE	0
-#define IN_DELETE	0
-#define IN_MOVE		0
-#define IN_CLOSE_WRITE	0
-
-#endif /* HAVE_INOTIFY */
-#endif
diff --git a/udev/udev-watch.c b/udev/udev-watch.c
index 53492e5..5a49c96 100644
--- a/udev/udev-watch.c
+++ b/udev/udev-watch.c
@@ -26,27 +26,24 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#ifdef HAVE_INOTIFY
 #include <sys/inotify.h>
-#endif
 
 #include "udev.h"
 
-int inotify_fd = -1;
+static int inotify_fd = -1;
 
 /* inotify descriptor, will be shared with rules directory;
  * set to cloexec since we need our children to be able to add
  * watches for us
  */
-void udev_watch_init(struct udev *udev)
+int udev_watch_init(struct udev *udev)
 {
 	inotify_fd = inotify_init();
 	if (inotify_fd >= 0)
 		util_set_fd_cloexec(inotify_fd);
-	else if (errno == ENOSYS)
-		info(udev, "unable to use inotify, udevd will not monitor rule files changes\n");
 	else
 		err(udev, "inotify_init failed: %m\n");
+	return inotify_fd;
 }
 
 /* move any old watches directory out of the way, and then restore
diff --git a/udev/udev.h b/udev/udev.h
index 8f2c1c6..7187975 100644
--- a/udev/udev.h
+++ b/udev/udev.h
@@ -22,7 +22,6 @@
 #include <sys/types.h>
 #include <sys/param.h>
 
-#include "udev-sysdeps.h"
 #include "lib/libudev.h"
 #include "lib/libudev-private.h"
 
@@ -53,7 +52,6 @@ static inline void logging_close(void)
 }
 
 struct udev_event {
-	struct udev_list_node node;
 	struct udev *udev;
 	struct udev_device *dev;
 	struct udev_device *dev_parent;
@@ -64,10 +62,6 @@ struct udev_event {
 	uid_t uid;
 	gid_t gid;
 	struct udev_list_node run_list;
-	pid_t pid;
-	int exitstatus;
-	time_t queue_time;
-	unsigned long long int delaying_seqnum;
 	unsigned int group_final:1;
 	unsigned int owner_final:1;
 	unsigned int mode_final:1;
@@ -76,7 +70,6 @@ struct udev_event {
 	unsigned int run_final:1;
 	unsigned int ignore_device:1;
 	unsigned int inotify_watch:1;
-	unsigned int trace:1;
 };
 
 struct udev_watch {
@@ -101,8 +94,7 @@ int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
 				   char *result, size_t maxsize, int read_value);
 
 /* udev-watch.c */
-extern int inotify_fd;
-void udev_watch_init(struct udev *udev);
+int udev_watch_init(struct udev *udev);
 void udev_watch_restore(struct udev *udev);
 void udev_watch_begin(struct udev *udev, struct udev_device *dev);
 void udev_watch_end(struct udev *udev, struct udev_device *dev);
diff --git a/udev/udevadm.xml b/udev/udevadm.xml
index 538180b..2e03d98 100644
--- a/udev/udevadm.xml
+++ b/udev/udevadm.xml
@@ -285,9 +285,9 @@
               <term><option>--reload-rules</option></term>
               <listitem>
                 <para>Signal udevd to reload the rules files.
-                Usually the udev daemon detects changes automatically, this may
-                only be needed on systems without inotify support. Reloading rules
-                does not apply any changes to already existing devices.</para>
+                The udev daemon detects changes automatically, this option is
+                usually not needed. Reloading rules does not apply any changes
+                to already existing devices.</para>
               </listitem>
             </varlistentry>
             <varlistentry>
diff --git a/udev/udevd.c b/udev/udevd.c
index 37b547a..6b47a30 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@xxxxxxxx>
+ * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@xxxxxxxx>
  * Copyright (C) 2004 Chris Friesen <chris_friesen@xxxxxxxxxxxx>
  * Copyright (C) 2009 Canonical Ltd.
  * Copyright (C) 2009 Scott James Remnant <scott@xxxxxxxxxxxx>
@@ -30,23 +30,21 @@
 #include <time.h>
 #include <getopt.h>
 #include <dirent.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/signalfd.h>
 #include <sys/select.h>
 #include <sys/poll.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
-#ifdef HAVE_INOTIFY
 #include <sys/inotify.h>
-#endif
 
 #include "udev.h"
 
 #define UDEVD_PRIORITY			-4
 #define UDEV_PRIORITY			-2
 
-/* maximum limit of forked childs */
-#define UDEVD_MAX_CHILDS		256
-
 static int debug;
 
 static void log_fn(struct udev *udev, int priority,
@@ -61,163 +59,320 @@ static void log_fn(struct udev *udev, int priority,
 	}
 }
 
-static void reap_sigchilds(void);
-
 static int debug_trace;
 static struct udev_rules *rules;
 static struct udev_queue_export *udev_queue_export;
 static struct udev_ctrl *udev_ctrl;
-static struct udev_monitor *kernel_monitor;
-static volatile sig_atomic_t sigchilds_waiting;
-static volatile sig_atomic_t udev_exit;
-static volatile sig_atomic_t reload_config;
-static volatile sig_atomic_t signal_received;
-static volatile pid_t settle_pid;
-static int run_exec_q;
-static int stop_exec_q;
+static struct udev_monitor *monitor;
+static int worker_socket[2];
+static pid_t settle_pid;
+static int stop_exec_queue;
+static int reload_config;
 static int max_childs;
 static int childs;
 static struct udev_list_node event_list;
-
-static struct udev_event *node_to_event(struct udev_list_node *node)
+static struct udev_list_node worker_list;
+static int udev_exit;
+static volatile sig_atomic_t worker_exit;
+
+enum poll_fd {
+	FD_CONTROL,
+	FD_NETLINK,
+	FD_INOTIFY,
+	FD_SIGNAL,
+	FD_WORKER,
+};
+
+static struct pollfd pfd[] = {
+	[FD_NETLINK] = { .events = POLLIN },
+	[FD_WORKER] =  { .events = POLLIN },
+	[FD_SIGNAL] =  { .events = POLLIN },
+	[FD_INOTIFY] = { .events = POLLIN },
+	[FD_CONTROL] = { .events = POLLIN },
+};
+
+enum event_state {
+	EVENT_UNDEF,
+	EVENT_QUEUED,
+	EVENT_RUNNING,
+};
+
+struct event {
+	struct udev_list_node node;
+	struct udev *udev;
+	struct udev_device *dev;
+	enum event_state state;
+	int exitcode;
+	unsigned long long int delaying_seqnum;
+	unsigned long long int seqnum;
+	const char *devpath;
+	size_t devpath_len;
+	const char *devpath_old;
+};
+
+static struct event *node_to_event(struct udev_list_node *node)
 {
 	char *event;
 
 	event = (char *)node;
-	event -= offsetof(struct udev_event, node);
-	return (struct udev_event *)event;
+	event -= offsetof(struct event, node);
+	return (struct event *)event;
 }
 
-static void event_queue_delete(struct udev_event *event)
+enum worker_state {
+	WORKER_UNDEF,
+	WORKER_RUNNING,
+	WORKER_IDLE,
+	WORKER_KILLED,
+};
+
+struct worker {
+	struct udev_list_node node;
+	pid_t pid;
+	enum worker_state state;
+	struct event *event;
+};
+
+/* passed from worker to main process */
+struct worker_message {
+	pid_t pid;
+	int exitcode;
+};
+
+static struct worker *node_to_worker(struct udev_list_node *node)
+{
+	char *worker;
+
+	worker = (char *)node;
+	worker -= offsetof(struct worker, node);
+	return (struct worker *)worker;
+}
+
+static void event_queue_delete(struct event *event)
 {
 	udev_list_node_remove(&event->node);
 
 	/* mark as failed, if "add" event returns non-zero */
-	if (event->exitstatus && strcmp(udev_device_get_action(event->dev), "add") == 0)
+	if (event->exitcode && strcmp(udev_device_get_action(event->dev), "add") == 0)
 		udev_queue_export_device_failed(udev_queue_export, event->dev);
 	else
 		udev_queue_export_device_finished(udev_queue_export, event->dev);
 
 	udev_device_unref(event->dev);
-	udev_event_unref(event);
+	free(event);
 }
 
 static void event_sig_handler(int signum)
 {
-	if (signum == SIGALRM)
+	switch (signum) {
+	case SIGALRM:
 		_exit(1);
+		break;
+	case SIGTERM:
+		worker_exit = 1;
+		break;
+	}
 }
 
-static void event_fork(struct udev_event *event)
+static void worker_new(struct event *event)
 {
+	struct worker *worker;
 	pid_t pid;
 	struct sigaction act;
-	int err;
-
-#if 0
-	/* single process, no forking, just for testing/profiling */
-	err = udev_event_execute_rules(event, rules);
-	if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
-		udev_event_execute_run(event);
-	info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
-	event_queue_delete(event);
-	return;
-#endif
 
-	if (debug_trace) {
-		event->trace = 1;
-		fprintf(stderr, "fork %s (%llu)\n",
-		       udev_device_get_syspath(event->dev),
-		       udev_device_get_seqnum(event->dev));
-	}
+	worker = calloc(1, sizeof(struct worker));
+	if (worker == NULL)
+		return;
 
 	pid = fork();
 	switch (pid) {
-	case 0:
-		/* child */
+	case 0: {
+		sigset_t mask;
+		struct udev_device *dev;
+
 		udev_queue_export_unref(udev_queue_export);
 		udev_ctrl_unref(udev_ctrl);
+		udev_monitor_unref(monitor);
+		close(pfd[FD_SIGNAL].fd);
+		close(worker_socket[READ_END]);
 		logging_close();
-		logging_init("udevd-event");
+		logging_init("udevd-work");
 		setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
 
+		/* re-open socket to listen to udevd only, and send back libudev events */
+		monitor = udev_monitor_new_from_netlink(event->udev, NULL);
+		if (monitor == NULL)
+			_exit(2);
+		udev_monitor_enable_receiving(monitor);
+
 		/* set signal handlers */
 		memset(&act, 0x00, sizeof(act));
 		act.sa_handler = event_sig_handler;
 		sigemptyset (&act.sa_mask);
 		act.sa_flags = 0;
+		sigaction(SIGTERM, &act, NULL);
 		sigaction(SIGALRM, &act, NULL);
 
-		/* reset to default */
-		act.sa_handler = SIG_DFL;
-		sigaction(SIGINT, &act, NULL);
-		sigaction(SIGTERM, &act, NULL);
-		sigaction(SIGCHLD, &act, NULL);
-		sigaction(SIGHUP, &act, NULL);
+		/* unblock signals */
+		sigfillset(&mask);
+		sigdelset(&mask, SIGTERM);
+		sigdelset(&mask, SIGALRM);
+		sigprocmask(SIG_SETMASK, &mask, NULL);
 
-		/* set timeout to prevent hanging processes */
-		alarm(UDEV_EVENT_TIMEOUT);
+		/* request TERM signal if main daemon exits */
+		prctl(PR_SET_PDEATHSIG, SIGTERM);
 
-		/* apply rules, create node, symlinks */
-		err = udev_event_execute_rules(event, rules);
+		/* initial device */
+		dev = event->dev;
 
-		/* rules may change/disable the timeout */
-		if (udev_device_get_event_timeout(event->dev) >= 0)
-			alarm(udev_device_get_event_timeout(event->dev));
+		while (!worker_exit) {
+			struct udev_event *udev_event;
+			struct worker_message msg;
+			int err;
 
-		/* execute RUN= */
-		if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
-			udev_event_execute_run(event);
+			udev_event = udev_event_new(dev);
+			if (udev_event == NULL)
+				_exit(3);
 
-		/* apply/restore inotify watch */
-		if (err == 0 && event->inotify_watch) {
-			udev_watch_begin(event->udev, event->dev);
-			udev_device_update_db(event->dev);
-		}
+			/* set timeout to prevent hanging processes */
+			alarm(UDEV_EVENT_TIMEOUT);
+
+			/* apply rules, create node, symlinks */
+			err = udev_event_execute_rules(udev_event, rules);
+
+			/* rules may change/disable the timeout */
+			if (udev_device_get_event_timeout(dev) >= 0)
+				alarm(udev_device_get_event_timeout(dev));
+
+			/* execute RUN= */
+			if (err == 0 && !udev_event->ignore_device && udev_get_run(udev_event->udev))
+				udev_event_execute_run(udev_event);
+
+			/* reset alarm */
+			alarm(0);
+
+			/* apply/restore inotify watch */
+			if (err == 0 && udev_event->inotify_watch) {
+				udev_watch_begin(udev_event->udev, dev);
+				udev_device_update_db(dev);
+			}
+
+			/* send processed event back to libudev listeners */
+			udev_monitor_send_device(monitor, dev, 0);
+
+			info(event->udev, "seq %llu finished with %i\n", udev_device_get_seqnum(dev), err);
+			udev_device_unref(dev);
+			udev_event_unref(udev_event);
+
+			/* send back the result of the event execution */
+			msg.exitcode = err;
+			msg.pid = getpid();
+			send(worker_socket[WRITE_END], &msg, sizeof(struct worker_message), 0);
 
-		/* send processed event back to the kernel netlink socket */
-		udev_monitor_send_device(kernel_monitor, event->dev);
+			/* wait for more device messages from udevd */
+			do
+				dev = udev_monitor_receive_device(monitor);
+			while (!worker_exit && dev == NULL);
+		}
 
-		info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
+		udev_monitor_unref(monitor);
 		logging_close();
-		if (err != 0)
-			exit(1);
 		exit(0);
+	}
 	case -1:
+		event->state = EVENT_QUEUED;
+		free(worker);
 		err(event->udev, "fork of child failed: %m\n");
-		event_queue_delete(event);
 		break;
 	default:
-		/* get SIGCHLD in main loop */
-		info(event->udev, "seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n",
-		     udev_device_get_seqnum(event->dev),
-		     pid,
-		     udev_device_get_action(event->dev),
-		     udev_device_get_subsystem(event->dev),
-		     time(NULL) - event->queue_time);
-		event->pid = pid;
+		event->state = EVENT_RUNNING;
+		worker->event = event;
+		worker->pid = pid;
+		worker->state = WORKER_RUNNING;
+		udev_list_node_append(&worker->node, &worker_list);
 		childs++;
+		break;
+	}
+}
+
+static void event_run(struct event *event)
+{
+	struct udev_list_node *loop;
+
+	udev_list_node_foreach(loop, &worker_list) {
+		struct worker *worker = node_to_worker(loop);
+		ssize_t count;
+
+		if (worker->state != WORKER_IDLE)
+			continue;
+
+		worker->event = event;
+		worker->state = WORKER_RUNNING;
+		event->state = EVENT_RUNNING;
+		count = udev_monitor_send_device(monitor, event->dev, worker->pid);
+		if (count < 0) {
+			err(event->udev, "worker [%u] did not accept message, kill it\n", worker->pid);
+			event->state = EVENT_QUEUED;
+			worker->state = WORKER_KILLED;
+			kill(worker->pid, SIGKILL);
+			continue;
+		}
+		return;
 	}
+
+	if (childs >= max_childs) {
+		info(event->udev, "maximum number (%i) of childs reached\n", childs);
+		return;
+	}
+
+	/* start new worker and pass initial device */
+	worker_new(event);
 }
 
-static void event_queue_insert(struct udev_event *event)
+static void event_queue_insert(struct udev_device *dev)
 {
-	event->queue_time = time(NULL);
+	struct event *event;
+
+	event = calloc(1, sizeof(struct event));
+	if (event == NULL)
+		return;
 
-	udev_queue_export_device_queued(udev_queue_export, event->dev);
-	info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev),
-	     udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
+	event->udev = udev_device_get_udev(dev);
+	event->dev = dev;
+	event->seqnum = udev_device_get_seqnum(dev);
+	event->devpath = udev_device_get_devpath(dev);
+	event->devpath_len = strlen(event->devpath);
+	event->devpath_old = udev_device_get_devpath_old(dev);
 
+	udev_queue_export_device_queued(udev_queue_export, dev);
+	info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
+	     udev_device_get_action(dev), udev_device_get_subsystem(dev));
+
+	event->state = EVENT_QUEUED;
 	udev_list_node_append(&event->node, &event_list);
-	run_exec_q = 1;
 
 	/* run all events with a timeout set immediately */
-	if (udev_device_get_timeout(event->dev) > 0) {
-		event_fork(event);
+	if (udev_device_get_timeout(dev) > 0) {
+		worker_new(event);
 		return;
 	}
 }
 
+static void worker_kill(void)
+{
+	struct udev_list_node *loop;
+
+	udev_list_node_foreach(loop, &worker_list) {
+		struct worker *worker = node_to_worker(loop);
+
+		if (worker->state == WORKER_KILLED)
+			continue;
+
+		worker->state = WORKER_KILLED;
+		kill(worker->pid, SIGTERM);
+	}
+}
+
 static int mem_size_mb(void)
 {
 	FILE *f;
@@ -241,112 +396,111 @@ static int mem_size_mb(void)
 	return memsize;
 }
 
-static int compare_devpath(const char *running, const char *waiting)
-{
-	int i = 0;
-
-	while (running[i] != '\0' && running[i] == waiting[i])
-		i++;
-
-	/* identical device event found */
-	if (running[i] == '\0' && waiting[i] == '\0')
-		return 1;
-
-	/* parent device event found */
-	if (running[i] == '\0' && waiting[i] == '/')
-		return 2;
-
-	/* child device event found */
-	if (running[i] == '/' && waiting[i] == '\0')
-		return 3;
-
-	/* no matching event */
-	return 0;
-}
-
 /* lookup event for identical, parent, child device */
-static int devpath_busy(struct udev_event *event)
+static int devpath_busy(struct event *event)
 {
 	struct udev_list_node *loop;
+	size_t common;
 
 	/* check if queue contains events we depend on */
 	udev_list_node_foreach(loop, &event_list) {
-		struct udev_event *loop_event = node_to_event(loop);
+		struct event *loop_event = node_to_event(loop);
 
 		/* we already found a later event, earlier can not block us, no need to check again */
-		if (udev_device_get_seqnum(loop_event->dev) < event->delaying_seqnum)
+		if (loop_event->seqnum < event->delaying_seqnum)
 			continue;
 
 		/* event we checked earlier still exists, no need to check again */
-		if (udev_device_get_seqnum(loop_event->dev) == event->delaying_seqnum)
+		if (loop_event->seqnum == event->delaying_seqnum)
 			return 2;
 
 		/* found ourself, no later event can block us */
-		if (udev_device_get_seqnum(loop_event->dev) >= udev_device_get_seqnum(event->dev))
+		if (loop_event->seqnum >= event->seqnum)
 			break;
 
 		/* check our old name */
-		if (udev_device_get_devpath_old(event->dev) != NULL)
-			if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) {
-				event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
+		if (event->devpath_old != NULL)
+			if (strcmp(loop_event->devpath, event->devpath_old) == 0) {
+				event->delaying_seqnum = loop_event->seqnum;
 				return 3;
 			}
 
-		/* check identical, parent, or child device event */
-		if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) {
-			dbg(event->udev, "%llu, device event still pending %llu (%s)\n",
-			    udev_device_get_seqnum(event->dev),
-			    udev_device_get_seqnum(loop_event->dev),
-			    udev_device_get_devpath(loop_event->dev));
-			event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
+		/* compare devpath */
+		common = MIN(loop_event->devpath_len, event->devpath_len);
+
+		/* one devpath is contained in the other? */
+		if (memcmp(loop_event->devpath, event->devpath, common) != 0)
+			continue;
+
+		/* identical device event found */
+		if (loop_event->devpath_len == event->devpath_len) {
+			event->delaying_seqnum = loop_event->seqnum;
 			return 4;
 		}
+
+		/* parent device event found */
+		if (event->devpath[common] == '/') {
+			event->delaying_seqnum = loop_event->seqnum;
+			return 5;
+		}
+
+		/* child device event found */
+		if (loop_event->devpath[common] == '/') {
+			event->delaying_seqnum = loop_event->seqnum;
+			return 6;
+		}
+
+		/* no matching device */
+		continue;
 	}
+
 	return 0;
 }
 
-/* serializes events for the identical and parent and child devices */
-static void event_queue_manager(struct udev *udev)
+static void events_start(struct udev *udev)
 {
 	struct udev_list_node *loop;
-	struct udev_list_node *tmp;
-
-start_over:
-	if (udev_list_is_empty(&event_list)) {
-		if (childs > 0) {
-			err(udev, "event list empty, but childs count is %i", childs);
-			childs = 0;
-		}
-		return;
-	}
-
-	udev_list_node_foreach_safe(loop, tmp, &event_list) {
-		struct udev_event *loop_event = node_to_event(loop);
 
-		if (childs >= max_childs) {
-			info(udev, "maximum number (%i) of childs reached\n", childs);
-			break;
-		}
+	udev_list_node_foreach(loop, &event_list) {
+		struct event *event = node_to_event(loop);
 
-		if (loop_event->pid != 0)
+		if (event->state != EVENT_QUEUED)
 			continue;
 
 		/* do not start event if parent or child event is still running */
-		if (devpath_busy(loop_event) != 0) {
-			dbg(udev, "delay seq %llu (%s)\n",
-			    udev_device_get_seqnum(loop_event->dev),
-			    udev_device_get_devpath(loop_event->dev));
+		if (devpath_busy(event) != 0) {
+			dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath);
 			continue;
 		}
 
-		event_fork(loop_event);
-		dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev));
+		event_run(event);
+	}
+}
+
+static void worker_returned(void)
+{
+	while (1) {
+		struct worker_message msg;
+		ssize_t size;
+		struct udev_list_node *loop;
+
+		size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
+		if (size != sizeof(struct worker_message))
+			break;
+
+		/* lookup worker who sent the signal */
+		udev_list_node_foreach(loop, &worker_list) {
+			struct worker *worker = node_to_worker(loop);
+
+			if (worker->pid != msg.pid)
+				continue;
 
-		/* retry if events finished in the meantime */
-		if (sigchilds_waiting) {
-			sigchilds_waiting = 0;
-			reap_sigchilds();
-			goto start_over;
+			/* worker returned */
+			worker->event->exitcode = msg.exitcode;
+			event_queue_delete(worker->event);
+			worker->event = NULL;
+			worker->state = WORKER_IDLE;
+			break;
 		}
 	}
 }
@@ -367,17 +521,17 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
 	if (i >= 0) {
 		info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i);
 		udev_set_log_priority(udev, i);
+		worker_kill();
 	}
 
 	if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
 		info(udev, "udevd message (STOP_EXEC_QUEUE) received\n");
-		stop_exec_q = 1;
+		stop_exec_queue = 1;
 	}
 
 	if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
 		info(udev, "udevd message (START_EXEC_QUEUE) received\n");
-		stop_exec_q = 0;
-		event_queue_manager(udev);
+		stop_exec_queue = 0;
 	}
 
 	if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) {
@@ -409,6 +563,7 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
 			}
 			free(key);
 		}
+		worker_kill();
 	}
 
 	i = udev_ctrl_get_set_max_childs(ctrl_msg);
@@ -420,6 +575,8 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
 	settle_pid = udev_ctrl_get_settle(ctrl_msg);
 	if (settle_pid > 0) {
 		info(udev, "udevd message (SETTLE) received\n");
+		kill(settle_pid, SIGUSR1);
+		settle_pid = 0;
 	}
 	udev_ctrl_msg_unref(ctrl_msg);
 }
@@ -427,22 +584,20 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
 /* read inotify messages */
 static int handle_inotify(struct udev *udev)
 {
-	int nbytes, pos;
+	ssize_t nbytes, pos;
 	char *buf;
 	struct inotify_event *ev;
 
-	if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+	if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
 		return 0;
 
 	buf = malloc(nbytes);
 	if (buf == NULL) {
 		err(udev, "error getting buffer for inotify, disable watching\n");
-		close(inotify_fd);
-		inotify_fd = -1;
-		return 0;
+		return -1;
 	}
 
-	read(inotify_fd, buf, nbytes);
+	nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes);
 
 	for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
 		struct udev_device *dev;
@@ -480,68 +635,44 @@ static int handle_inotify(struct udev *udev)
 	return 0;
 }
 
-static void sig_handler(int signum)
+static void handle_signal(int signo)
 {
-	switch (signum) {
-		case SIGINT:
-		case SIGTERM:
-			udev_exit = 1;
-			break;
-		case SIGCHLD:
-			/* set flag, then write to pipe if needed */
-			sigchilds_waiting = 1;
-			break;
-		case SIGHUP:
-			reload_config = 1;
-			break;
-	}
+	switch (signo) {
+	case SIGINT:
+	case SIGTERM:
+		udev_exit = 1;
+		break;
+	case SIGCHLD:
+		while (1) {
+			pid_t pid;
+			struct udev_list_node *loop, *tmp;
 
-	signal_received = 1;
-}
+			pid = waitpid(-1, NULL, WNOHANG);
+			if (pid <= 0)
+				break;
 
-static void udev_done(int pid, int exitstatus)
-{
-	struct udev_list_node *loop;
+			udev_list_node_foreach_safe(loop, tmp, &worker_list) {
+				struct worker *worker = node_to_worker(loop);
 
-	/* find event associated with pid and delete it */
-	udev_list_node_foreach(loop, &event_list) {
-		struct udev_event *loop_event = node_to_event(loop);
-
-		if (loop_event->pid == pid) {
-			info(loop_event->udev, "seq %llu cleanup, pid [%d], status %i, %ld seconds old\n",
-			     udev_device_get_seqnum(loop_event->dev), loop_event->pid,
-			     exitstatus, time(NULL) - loop_event->queue_time);
-			loop_event->exitstatus = exitstatus;
-			if (debug_trace)
-				fprintf(stderr, "exit %s (%llu)\n",
-				       udev_device_get_syspath(loop_event->dev),
-				       udev_device_get_seqnum(loop_event->dev));
-			event_queue_delete(loop_event);
-			childs--;
-
-			/* there may be dependent events waiting */
-			run_exec_q = 1;
-			return;
-		}
-	}
-}
+				if (worker->pid != pid)
+					continue;
 
-static void reap_sigchilds(void)
-{
-	pid_t pid;
-	int status;
+				/* fail event, if worker died unexpectedly */
+				if (worker->event != NULL) {
+					worker->event->exitcode = 127;
+					event_queue_delete(worker->event);
+				}
 
-	while (1) {
-		pid = waitpid(-1, &status, WNOHANG);
-		if (pid <= 0)
-			break;
-		if (WIFEXITED(status))
-			status = WEXITSTATUS(status);
-		else if (WIFSIGNALED(status))
-			status = WTERMSIG(status) + 128;
-		else
-			status = 0;
-		udev_done(pid, status);
+				udev_list_node_remove(&worker->node);
+				free(worker);
+				childs--;
+				break;
+			}
+		}
+		break;
+	case SIGHUP:
+		reload_config = 1;
+		break;
 	}
 }
 
@@ -576,7 +707,7 @@ int main(int argc, char *argv[])
 {
 	struct udev *udev;
 	int fd;
-	struct sigaction act;
+	sigset_t mask;
 	const char *value;
 	int daemonize = 0;
 	int resolve_names = 1;
@@ -669,29 +800,76 @@ int main(int argc, char *argv[])
 		rc = 1;
 		goto exit;
 	}
-
 	if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
 		fprintf(stderr, "error binding control socket, seems udevd is already running\n");
 		err(udev, "error binding control socket, seems udevd is already running\n");
 		rc = 1;
 		goto exit;
 	}
+	pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl);
 
-	kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel");
-	if (kernel_monitor == NULL || udev_monitor_enable_receiving(kernel_monitor) < 0) {
+	monitor = udev_monitor_new_from_netlink(udev, "kernel");
+	if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) {
 		fprintf(stderr, "error initializing netlink socket\n");
 		err(udev, "error initializing netlink socket\n");
 		rc = 3;
 		goto exit;
 	}
-	udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
+	udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
+	pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor);
+
+	pfd[FD_INOTIFY].fd = udev_watch_init(udev);
+	if (pfd[FD_INOTIFY].fd < 0) {
+		fprintf(stderr, "error initializing inotify\n");
+		err(udev, "error initializing inotify\n");
+		rc = 4;
+		goto exit;
+	}
+
+	if (udev_get_rules_path(udev) != NULL) {
+		inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev),
+				  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
+	} else {
+		char filename[UTIL_PATH_SIZE];
+
+		inotify_add_watch(pfd[FD_INOTIFY].fd, UDEV_PREFIX "/lib/udev/rules.d",
+				  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
+		inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d",
+				  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
+
+		/* watch dynamic rules directory */
+		util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
+		inotify_add_watch(pfd[FD_INOTIFY].fd, filename,
+				  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
+	}
+	udev_watch_restore(udev);
+
+	/* block and listen to all signals on signalfd */
+	sigfillset(&mask);
+	sigprocmask(SIG_SETMASK, &mask, NULL);
+	pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0);
+	if (pfd[FD_SIGNAL].fd < 0) {
+		fprintf(stderr, "error getting signalfd\n");
+		err(udev, "error getting signalfd\n");
+		rc = 5;
+		goto exit;
+	}
+
+	/* unnamed socket from workers to the main daemon */
+	if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, worker_socket) < 0) {
+		fprintf(stderr, "error getting socketpair\n");
+		err(udev, "error getting socketpair\n");
+		rc = 6;
+		goto exit;
+	}
+	pfd[FD_WORKER].fd = worker_socket[READ_END];
 
 	rules = udev_rules_new(udev, resolve_names);
 	if (rules == NULL) {
 		err(udev, "error reading rules\n");
 		goto exit;
 	}
-	udev_list_init(&event_list);
+
 	udev_queue_export = udev_queue_export_new(udev);
 	if (udev_queue_export == NULL) {
 		err(udev, "error creating queue file\n");
@@ -704,19 +882,19 @@ int main(int argc, char *argv[])
 		pid = fork();
 		switch (pid) {
 		case 0:
-			dbg(udev, "daemonized fork running\n");
 			break;
 		case -1:
 			err(udev, "fork of daemon failed: %m\n");
 			rc = 4;
 			goto exit;
 		default:
-			dbg(udev, "child [%u] running, parent exits\n", pid);
 			rc = 0;
 			goto exit;
 		}
 	}
 
+	startup_log(udev);
+
 	/* redirect std{out,err} */
 	if (!debug && !debug_trace) {
 		dup2(fd, STDIN_FILENO);
@@ -742,159 +920,109 @@ int main(int argc, char *argv[])
 		close(fd);
 	}
 
-	startup_log(udev);
-
-	/* set signal handlers */
-	memset(&act, 0x00, sizeof(struct sigaction));
-	act.sa_handler = sig_handler;
-	sigemptyset(&act.sa_mask);
-	act.sa_flags = SA_RESTART;
-	sigaction(SIGINT, &act, NULL);
-	sigaction(SIGTERM, &act, NULL);
-	sigaction(SIGCHLD, &act, NULL);
-	sigaction(SIGHUP, &act, NULL);
-
-	/* watch rules directory */
-	udev_watch_init(udev);
-	if (inotify_fd >= 0) {
-		if (udev_get_rules_path(udev) != NULL) {
-			inotify_add_watch(inotify_fd, udev_get_rules_path(udev),
-					  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
-		} else {
-			char filename[UTIL_PATH_SIZE];
-
-			inotify_add_watch(inotify_fd, UDEV_PREFIX "/lib/udev/rules.d",
-					  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
-			inotify_add_watch(inotify_fd, SYSCONFDIR "/udev/rules.d",
-					  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
-
-			/* watch dynamic rules directory */
-			util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
-			inotify_add_watch(inotify_fd, filename,
-					  IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
-		}
-
-		udev_watch_restore(udev);
-	}
-
 	/* in trace mode run one event after the other */
 	if (debug_trace) {
 		max_childs = 1;
 	} else {
 		int memsize = mem_size_mb();
+
 		if (memsize > 0)
-			max_childs = 128 + (memsize / 4);
+			max_childs = 128 + (memsize / 8);
 		else
-			max_childs = UDEVD_MAX_CHILDS;
+			max_childs = 128;
 	}
+
 	/* possibly overwrite maximum limit of executed events */
 	value = getenv("UDEVD_MAX_CHILDS");
 	if (value)
 		max_childs = strtoul(value, NULL, 10);
 	info(udev, "initialize max_childs to %u\n", max_childs);
 
+	udev_list_init(&event_list);
+	udev_list_init(&worker_list);
+
 	while (!udev_exit) {
-		sigset_t blocked_mask, orig_mask;
-		struct pollfd pfd[4];
-		struct pollfd *ctrl_poll, *monitor_poll, *inotify_poll = NULL;
-		int nfds = 0;
 		int fdcount;
+		int timeout;
 
-		sigfillset(&blocked_mask);
-		sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask);
-		if (signal_received) {
-			sigprocmask(SIG_SETMASK, &orig_mask, NULL);
-			goto handle_signals;
-		}
+		/* set timeout to kill idle workers */
+		if (udev_list_is_empty(&event_list) && !udev_list_is_empty(&worker_list))
+			timeout = 3 * 1000;
+		else
+			timeout = -1;
+		/* wait for events */
+		fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout);
+		if (fdcount < 0)
+			continue;
 
-		ctrl_poll = &pfd[nfds++];
-		ctrl_poll->fd = udev_ctrl_get_fd(udev_ctrl);
-		ctrl_poll->events = POLLIN;
+		/* timeout - kill idle workers */
+		if (fdcount == 0)
+			worker_kill();
 
-		monitor_poll = &pfd[nfds++];
-		monitor_poll->fd = udev_monitor_get_fd(kernel_monitor);
-		monitor_poll->events = POLLIN;
+		/* event has finished */
+		if (pfd[FD_WORKER].revents & POLLIN)
+			worker_returned();
 
-		if (inotify_fd >= 0) {
-			inotify_poll = &pfd[nfds++];
-			inotify_poll->fd = inotify_fd;
-			inotify_poll->events = POLLIN;
-		}
+		/* get kernel uevent */
+		if (pfd[FD_NETLINK].revents & POLLIN) {
+			struct udev_device *dev;
 
-		fdcount = ppoll(pfd, nfds, NULL, &orig_mask);
-		sigprocmask(SIG_SETMASK, &orig_mask, NULL);
-		if (fdcount < 0) {
-			if (errno == EINTR)
-				goto handle_signals;
-			err(udev, "error in select: %m\n");
-			continue;
+			dev = udev_monitor_receive_device(monitor);
+			if (dev != NULL)
+				event_queue_insert(dev);
+			else
+				udev_device_unref(dev);
 		}
 
 		/* get control message */
-		if (ctrl_poll->revents & POLLIN)
+		if (pfd[FD_CONTROL].revents & POLLIN)
 			handle_ctrl_msg(udev_ctrl);
 
-		/* get kernel uevent */
-		if (monitor_poll->revents & POLLIN) {
-			struct udev_device *dev;
+		/* start new events */
+		if (!udev_list_is_empty(&event_list) && !stop_exec_queue)
+			events_start(udev);
 
-			dev = udev_monitor_receive_device(kernel_monitor);
-			if (dev != NULL) {
-				struct udev_event *event;
+		/* get signal */
+		if (pfd[FD_SIGNAL].revents & POLLIN) {
+			struct signalfd_siginfo fdsi;
+			ssize_t size;
 
-				event = udev_event_new(dev);
-				if (event != NULL)
-					event_queue_insert(event);
-				else
-					udev_device_unref(dev);
-			}
+			size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo));
+			if (size == sizeof(struct signalfd_siginfo))
+				handle_signal(fdsi.ssi_signo);
 		}
 
-		/* rules directory inotify watch */
-		if (inotify_poll && (inotify_poll->revents & POLLIN))
+		/* device node and rules directory inotify watch */
+		if (pfd[FD_INOTIFY].revents & POLLIN)
 			handle_inotify(udev);
 
-handle_signals:
-		signal_received = 0;
-
 		/* rules changed, set by inotify or a HUP signal */
 		if (reload_config) {
 			struct udev_rules *rules_new;
 
-			reload_config = 0;
+			worker_kill();
 			rules_new = udev_rules_new(udev, resolve_names);
 			if (rules_new != NULL) {
 				udev_rules_unref(rules);
 				rules = rules_new;
 			}
-		}
-
-		if (sigchilds_waiting) {
-			sigchilds_waiting = 0;
-			reap_sigchilds();
-		}
-
-		if (run_exec_q) {
-			run_exec_q = 0;
-			if (!stop_exec_q)
-				event_queue_manager(udev);
-		}
-
-		if (settle_pid > 0) {
-			kill(settle_pid, SIGUSR1);
-			settle_pid = 0;
+			reload_config = 0;
 		}
 	}
+
 	udev_queue_export_cleanup(udev_queue_export);
 	rc = 0;
 exit:
-
 	udev_queue_export_unref(udev_queue_export);
 	udev_rules_unref(rules);
 	udev_ctrl_unref(udev_ctrl);
-	if (inotify_fd >= 0)
-		close(inotify_fd);
-	udev_monitor_unref(kernel_monitor);
+	if (pfd[FD_SIGNAL].fd >= 0)
+		close(pfd[FD_SIGNAL].fd);
+	if (worker_socket[READ_END] >= 0)
+		close(worker_socket[READ_END]);
+	if (worker_socket[WRITE_END] >= 0)
+		close(worker_socket[WRITE_END]);
+	udev_monitor_unref(monitor);
 	udev_selinux_exit(udev);
 	udev_unref(udev);
 	logging_close();

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux