From: Scott James Remnant <scott@canonical.com>
To: linux-hotplug@vger.kernel.org
Subject: Re: Revisiting threaded udevd
Date: Wed, 24 Dec 2008 11:37:01 +0000 [thread overview]
Message-ID: <1230118621.18723.57.camel@quest> (raw)
In-Reply-To: <494F7942.6060702@tuffmail.co.uk>
[-- Attachment #1.1: Type: text/plain, Size: 615 bytes --]
On Mon, 2008-12-22 at 11:25 +0000, Alan Jenkins wrote:
> I have some time to look at this again now.
>
I was just looking at this myself, since it formed a natural part of the
performance testing I'm doing on Ubuntu.
I got as far as reapplying the patches to current git head, but then hit
a segfault :-(
Attached is the patch against head.
The segfault occurs in the udev_device_get_properties() call at the top
of udev_exec(), because that calls update_envp_monitor_buf() which calls
free().
I assume this is non-reentrant territory?
Scott
--
Scott James Remnant
scott@canonical.com
[-- Attachment #1.2: threaded.patch --]
[-- Type: text/x-patch, Size: 23778 bytes --]
=== modified file 'configure.ac'
--- configure.ac 2008-12-16 15:17:53 +0000
+++ configure.ac 2008-12-24 11:16:17 +0000
@@ -9,6 +9,8 @@
AC_SYS_LARGEFILE
AC_PROG_LIBTOOL
+sinclude(acx_pthread.m4)
+
dnl /* libudev version */
LIBUDEV_LT_CURRENT=0
LIBUDEV_LT_REVISION=6
@@ -25,6 +27,8 @@
AC_SUBST(VOLID_LT_REVISION)
AC_SUBST(VOLID_LT_AGE)
+ACX_PTHREAD(AC_DEFINE(HAVE_PTHREAD), AC_MSG_FAILURE([pthreads unavailable]))
+
AC_PATH_PROG([XSLTPROC], [xsltproc])
AC_CHECK_LIB(c, inotify_init,
=== modified file 'udev/Makefile.am'
--- udev/Makefile.am 2008-12-06 03:03:08 +0000
+++ udev/Makefile.am 2008-12-24 11:16:17 +0000
@@ -1,5 +1,9 @@
include $(top_srcdir)/Makefile.am.inc
+LIBS += $(PTHREAD_LIBS)
+CFLAGS += $(PTHREAD_CFLAGS)
+CC=$(PTHREAD_CC)
+
SUBDIRS = \
lib
@@ -18,6 +22,7 @@
udev-event.c \
udev-node.c \
udev-rules.c \
+ udev-exec.c \
udev-util.c \
lib/libudev.h \
lib/libudev-private.h \
=== modified file 'udev/test-udev.c'
--- udev/test-udev.c 2008-10-24 08:51:04 +0000
+++ udev/test-udev.c 2008-12-24 11:16:28 +0000
@@ -60,6 +60,9 @@
info(udev, "version %s\n", VERSION);
udev_selinux_init(udev);
+ /* fork exec daemon before opening fds or changing signal handling */
+ udev_exec_init(udev);
+
/* set signal handlers */
memset(&act, 0x00, sizeof(act));
act.sa_handler = (void (*)(int)) sig_handler;
@@ -69,9 +72,6 @@
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
- /* trigger timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
-
action = getenv("ACTION");
devpath = getenv("DEVPATH");
subsystem = getenv("SUBSYSTEM");
@@ -99,10 +99,6 @@
event = udev_event_new(dev);
err = udev_event_execute_rules(event, rules);
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(dev) >= 0)
- alarm(udev_device_get_event_timeout(dev));
-
if (err == 0 && !event->ignore_device && udev_get_run(udev))
udev_event_execute_run(event);
@@ -111,6 +107,7 @@
fail:
udev_rules_unref(rules);
exit:
+ udev_exec_cleanup(udev);
udev_selinux_exit(udev);
udev_unref(udev);
if (err != 0)
=== modified file 'udev/udev-event.c'
--- udev/udev-event.c 2008-12-02 00:04:23 +0000
+++ udev/udev-event.c 2008-12-24 11:10:39 +0000
@@ -717,7 +717,7 @@
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_exec(event->dev, program, NULL, 0, NULL) != 0) {
if (!udev_list_entry_get_flag(list_entry))
err = -1;
}
=== modified file 'udev/udev-rules.c'
--- udev/udev-rules.c 2008-11-24 20:24:04 +0000
+++ udev/udev-rules.c 2008-12-24 11:16:01 +0000
@@ -731,13 +731,11 @@
static int import_program_into_properties(struct udev_device *dev, const char *program)
{
struct udev *udev = udev_device_get_udev(dev);
- char **envp;
char result[2048];
size_t reslen;
char *line;
- envp = udev_device_get_properties_envp(dev);
- if (util_run_program(udev, program, envp, result, sizeof(result), &reslen) != 0)
+ if (udev_exec(dev, program, result, sizeof(result), &reslen) != 0)
return -1;
line = result;
@@ -2190,19 +2188,17 @@
case TK_M_PROGRAM:
{
char program[UTIL_PATH_SIZE];
- char **envp;
char result[UTIL_PATH_SIZE];
free(event->program_result);
event->program_result = NULL;
util_strlcpy(program, &rules->buf[cur->key.value_off], sizeof(program));
udev_event_apply_format(event, program, sizeof(program));
- envp = udev_device_get_properties_envp(event->dev);
info(event->udev, "PROGRAM '%s' %s:%u\n",
program,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL) != 0) {
+ if (udev_exec(event->udev, program, result, sizeof(result), NULL) != 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
=== modified file 'udev/udev-util.c'
--- udev/udev-util.c 2008-12-19 23:03:53 +0000
+++ udev/udev-util.c 2008-12-24 11:12:52 +0000
@@ -25,7 +25,6 @@
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
-#include <sys/wait.h>
#include "udev.h"
@@ -238,215 +237,3 @@
udev_device_unref(dev);
return 0;
}
-
-int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen)
-{
- int status;
- int outpipe[2] = {-1, -1};
- int errpipe[2] = {-1, -1};
- pid_t pid;
- char arg[UTIL_PATH_SIZE];
- char program[UTIL_PATH_SIZE];
- char *argv[(sizeof(arg) / 2) + 1];
- int devnull;
- int i;
- int err = 0;
-
- /* build argv from command */
- util_strlcpy(arg, command, sizeof(arg));
- i = 0;
- if (strchr(arg, ' ') != NULL) {
- char *pos = arg;
-
- while (pos != NULL && pos[0] != '\0') {
- if (pos[0] == '\'') {
- /* do not separate quotes */
- pos++;
- argv[i] = strsep(&pos, "\'");
- while (pos != NULL && pos[0] == ' ')
- pos++;
- } else {
- argv[i] = strsep(&pos, " ");
- }
- dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
- i++;
- }
- argv[i] = NULL;
- } else {
- argv[0] = arg;
- argv[1] = NULL;
- }
- info(udev, "'%s'\n", command);
-
- /* prepare pipes from child to parent */
- if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(outpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
- if (udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(errpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
-
- /* allow programs in /lib/udev/ to be called without the path */
- if (strchr(argv[0], '/') == NULL) {
- util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program));
- util_strlcat(program, argv[0], sizeof(program));
- argv[0] = program;
- }
-
- pid = fork();
- switch(pid) {
- case 0:
- /* child closes parent ends of pipes */
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* discard child output or connect to pipe */
- devnull = open("/dev/null", O_RDWR);
- if (devnull > 0) {
- dup2(devnull, STDIN_FILENO);
- if (outpipe[WRITE_END] < 0)
- dup2(devnull, STDOUT_FILENO);
- if (errpipe[WRITE_END] < 0)
- dup2(devnull, STDERR_FILENO);
- close(devnull);
- } else
- err(udev, "open /dev/null failed: %m\n");
- if (outpipe[WRITE_END] > 0) {
- dup2(outpipe[WRITE_END], STDOUT_FILENO);
- close(outpipe[WRITE_END]);
- }
- if (errpipe[WRITE_END] > 0) {
- dup2(errpipe[WRITE_END], STDERR_FILENO);
- close(errpipe[WRITE_END]);
- }
- execve(argv[0], argv, envp);
- if (errno == ENOENT || errno == ENOTDIR) {
- /* may be on a filesytem which is not mounted right now */
- info(udev, "program '%s' not found\n", argv[0]);
- } else {
- /* other problems */
- err(udev, "exec of program '%s' failed\n", argv[0]);
- }
- _exit(1);
- case -1:
- err(udev, "fork of '%s' failed: %m\n", argv[0]);
- return -1;
- default:
- /* read from child if requested */
- if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- ssize_t count;
- size_t respos = 0;
-
- /* parent closes child ends of pipes */
- if (outpipe[WRITE_END] > 0)
- close(outpipe[WRITE_END]);
- if (errpipe[WRITE_END] > 0)
- close(errpipe[WRITE_END]);
-
- /* read child output */
- while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- int fdcount;
- fd_set readfds;
-
- FD_ZERO(&readfds);
- if (outpipe[READ_END] > 0)
- FD_SET(outpipe[READ_END], &readfds);
- if (errpipe[READ_END] > 0)
- FD_SET(errpipe[READ_END], &readfds);
- fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -1;
- break;
- }
-
- /* get stdout */
- if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
- char inbuf[1024];
- char *pos;
- char *line;
-
- count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
- if (count <= 0) {
- close(outpipe[READ_END]);
- outpipe[READ_END] = -1;
- if (count < 0) {
- err(udev, "stdin read failed: %m\n");
- err = -1;
- }
- continue;
- }
- inbuf[count] = '\0';
-
- /* store result for rule processing */
- if (result) {
- if (respos + count < ressize) {
- memcpy(&result[respos], inbuf, count);
- respos += count;
- } else {
- err(udev, "ressize %ld too short\n", (long)ressize);
- err = -1;
- }
- }
- pos = inbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stdout) '%s'\n", argv[0], line);
- }
-
- /* get stderr */
- if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
- char errbuf[1024];
- char *pos;
- char *line;
-
- count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
- if (count <= 0) {
- close(errpipe[READ_END]);
- errpipe[READ_END] = -1;
- if (count < 0)
- err(udev, "stderr read failed: %m\n");
- continue;
- }
- errbuf[count] = '\0';
- pos = errbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stderr) '%s'\n", argv[0], line);
- }
- }
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* return the childs stdout string */
- if (result) {
- result[respos] = '\0';
- dbg(udev, "result='%s'\n", result);
- if (reslen)
- *reslen = respos;
- }
- }
- waitpid(pid, &status, 0);
- if (WIFEXITED(status)) {
- info(udev, "'%s' returned with status %i\n", argv[0], WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else {
- err(udev, "'%s' abnormal exit\n", command);
- err = -1;
- }
- }
- return err;
-}
=== modified file 'udev/udev.h'
--- udev/udev.h 2008-11-05 20:49:52 +0000
+++ udev/udev.h 2008-12-24 11:16:47 +0000
@@ -35,6 +35,9 @@
#define READ_END 0
#define WRITE_END 1
+#define UDEVD_PRIORITY -4 /* nice level for daemon */
+#define UDEV_PRIORITY -2 /* nice level for programs run by daemon*/
+
static inline void logging_init(const char *program_name)
{
openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON);
@@ -64,7 +67,7 @@
uid_t uid;
gid_t gid;
struct udev_list_node run_list;
- pid_t pid;
+ pthread_t thread;
int exitstatus;
time_t queue_time;
unsigned long long int delaying_seqnum;
@@ -100,14 +103,18 @@
extern int udev_node_remove(struct udev_device *dev, int test);
extern void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old, int test);
+/* udev-exec.c */
+extern int udev_exec_init(struct udev *udev);
+extern void udev_exec_cleanup(struct udev *udev);
+extern int udev_exec(struct udev_device *dev, const char *command,
+ char *result, size_t ressize, size_t *reslen);
+
/* udev-util.c */
extern int util_create_path(struct udev *udev, const char *path);
extern int util_delete_path(struct udev *udev, const char *path);
extern int util_unlink_secure(struct udev *udev, const char *filename);
extern uid_t util_lookup_user(struct udev *udev, const char *user);
extern gid_t util_lookup_group(struct udev *udev, const char *group);
-extern int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen);
extern int util_resolve_subsys_kernel(struct udev *udev, const char *string,
char *result, size_t maxsize, int read_value);
=== modified file 'udev/udevd.c'
--- udev/udevd.c 2008-11-17 13:43:58 +0000
+++ udev/udevd.c 2008-12-24 11:28:17 +0000
@@ -35,12 +35,10 @@
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
#endif
+#include <pthread.h>
#include "udev.h"
-#define UDEVD_PRIORITY -4
-#define UDEV_PRIORITY -2
-
/* maximum limit of forked childs */
#define UDEVD_MAX_CHILDS 256
@@ -64,7 +62,7 @@
static struct udev_monitor *kernel_monitor;
static int inotify_fd = -1;
static int signal_pipe[2] = {-1, -1};
-static volatile int sigchilds_waiting;
+static int event_done_pipe[2] = {-1, -1};
static volatile int udev_exit;
static volatile int reload_config;
static int run_exec_q;
@@ -73,6 +71,8 @@
static int childs;
static struct udev_list_node event_list;
+pthread_attr_t thread_attr;
+
enum event_state {
EVENT_QUEUED,
EVENT_FINISHED,
@@ -167,87 +167,68 @@
udev_event_unref(event);
}
-static void asmlinkage event_sig_handler(int signum)
-{
- if (signum == SIGALRM)
- exit(1);
-}
-
-static void event_fork(struct udev_event *event)
-{
- pid_t pid;
- struct sigaction act;
- int err;
-
- if (debug_trace) {
- event->trace = 1;
- fprintf(stderr, "fork %s (%llu)\n",
- udev_device_get_syspath(event->dev),
- udev_device_get_seqnum(event->dev));
- }
-
- pid = fork();
- switch (pid) {
- case 0:
- /* child */
- udev_monitor_unref(kernel_monitor);
- udev_ctrl_unref(udev_ctrl);
- if (inotify_fd >= 0)
- close(inotify_fd);
- close(signal_pipe[READ_END]);
- close(signal_pipe[WRITE_END]);
- logging_close();
- logging_init("udevd-event");
- setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
-
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = (void (*)(int)) event_sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- 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);
-
- /* set timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
-
- /* apply rules, create node, symlinks */
- err = udev_event_execute_rules(event, rules);
-
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(event->dev) >= 0)
- alarm(udev_device_get_event_timeout(event->dev));
-
- /* execute RUN= */
- 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);
- logging_close();
- if (err != 0)
- exit(1);
- exit(0);
- case -1:
- err(event->udev, "fork of child failed: %m\n");
+static int event_run(struct udev_event *event)
+{
+ int err;
+
+ /* apply rules, create node, symlinks */
+ err = udev_event_execute_rules(event, rules);
+
+ /* execute RUN= */
+ 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);
+
+ return err;
+}
+
+static void *event_run_pthread(void *arg)
+{
+ struct udev_event *event = arg;
+ int err;
+
+ err = event_run(event);
+
+ /* write to pipe, which will wakeup select() in our mainloop */
+ write(event_done_pipe[WRITE_END], &event, sizeof(struct udev_event *));
+
+ return (void *) (uintptr_t) err;
+}
+
+static void event_thread_create(struct udev_event *event)
+{
+ if (pthread_create(&event->thread, &thread_attr, event_run_pthread, event) != 0) {
+ err(event->udev, "create thread 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;
- childs++;
- }
+ }
+
+ info(event->udev, "seq %llu created, '%s' '%s', %ld seconds old\n",
+ udev_device_get_seqnum(event->dev),
+ udev_device_get_action(event->dev),
+ udev_device_get_subsystem(event->dev),
+ time(NULL) - event->queue_time);
+}
+
+static int event_thread_join(struct udev_event *event)
+{
+ void *result;
+ int err;
+
+ err = pthread_join(event->thread, &result);
+ if (err) {
+ err(event->udev, "failed to retrieve thread return value: %m\n");
+ return -1;
+ }
+
+ return (int) (uintptr_t) result;
+}
+
+static void event_thread_kill(struct udev_event *event)
+{
+ pthread_kill(event->thread, SIGTERM);
+
+ /* Make sure thread has died before we return */
+ event_thread_join(event);
}
static void event_queue_insert(struct udev_event *event)
@@ -279,7 +260,7 @@
/* run all events with a timeout set immediately */
if (udev_device_get_timeout(event->dev) > 0) {
- event_fork(event);
+ event_thread_create(event);
return;
}
}
@@ -420,7 +401,7 @@
break;
}
- if (loop_event->pid != 0)
+ if (loop_event->thread)
continue;
/* do not start event if parent or child event is still running */
@@ -431,7 +412,7 @@
continue;
}
- event_fork(loop_event);
+ event_thread_create(loop_event);
dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev));
}
}
@@ -509,12 +490,9 @@
switch (signum) {
case SIGINT:
case SIGTERM:
+ case SIGCHLD:
udev_exit = 1;
break;
- case SIGCHLD:
- /* set flag, then write to pipe if needed */
- sigchilds_waiting = 1;
- break;
case SIGHUP:
reload_config = 1;
break;
@@ -524,50 +502,17 @@
write(signal_pipe[WRITE_END], "", 1);
}
-static void udev_done(int pid, int exitstatus)
-{
- struct udev_list_node *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;
- }
- }
-}
-
-static void reap_sigchilds(void)
-{
- pid_t pid;
- int status;
-
- 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);
- }
+static void udev_done(struct udev_event *event)
+{
+ event->exitstatus = event_thread_join(event);
+ info(event->udev, "seq %llu cleanup, status %i, %ld seconds old\n",
+ udev_device_get_seqnum(event->dev),
+ event->exitstatus, time(NULL) - event->queue_time);
+
+ event_queue_delete(event);
+
+ /* there may be events waiting with the same devpath */
+ run_exec_q = 1;
}
static void cleanup_queue_dir(struct udev *udev)
@@ -636,6 +581,8 @@
int err;
int fd;
struct sigaction act;
+ int pagesize;
+ int stacksize;
fd_set readfds;
const char *value;
int daemonize = 0;
@@ -706,6 +653,13 @@
if (write(STDERR_FILENO, 0, 0) < 0)
dup2(fd, STDERR_FILENO);
+ chdir("/");
+ umask(022);
+ setsid();
+
+ /* fork exec daemon before opening more fds or changing signal handling */
+ udev_exec_init(udev);
+
/* init control socket, bind() ensures, that only one udevd instance is running */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
if (udev_ctrl == NULL) {
@@ -736,6 +690,11 @@
err(udev, "error getting pipes: %m\n");
goto exit;
}
+ err = pipe(event_done_pipe);
+ if (err < 0) {
+ err(udev, "error getting pipes: %m\n");
+ goto exit;
+ }
err = fcntl(signal_pipe[READ_END], F_GETFL, 0);
if (err < 0) {
@@ -799,9 +758,28 @@
/* set scheduling priority for the daemon */
setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
- chdir("/");
- umask(022);
- setsid();
+ pthread_attr_init(&thread_attr);
+
+ /* To create many threads on 32-bit, it may be necessary to explicitly limit the stack size.
+
+ udevd doesn't use crazy recursion or deep call-chains.
+ But it does allocate quite a few buffers and temporary strings on the stack.
+ We may also need extra space for exotic NSS modules (user/group lookup, e.g. over LDAP).
+ Make a generous guess, double it, and double it again!
+
+ This comes to just over 200Kb. The default on i386 is 2Mb.
+ */
+ stacksize = PTHREAD_STACK_MIN;
+ stacksize += UTIL_PATH_SIZE * 8;
+ stacksize += UTIL_LINE_SIZE * 8;
+ stacksize += UTIL_NAME_SIZE * 8;
+ stacksize += 4096 * 8;
+ stacksize *= 4;
+
+ /* Round up to a whole number of pages */
+ pagesize = sysconf(_SC_PAGESIZE);
+ stacksize = ((stacksize / pagesize) + 1) * pagesize;
+ pthread_attr_setstacksize(&thread_attr, stacksize);
/* OOM_DISABLE == -17 */
fd = open("/proc/self/oom_adj", O_RDWR);
@@ -887,12 +865,14 @@
maxfd = udev_ctrl_get_fd(udev_ctrl);
maxfd = UDEV_MAX(maxfd, udev_monitor_get_fd(kernel_monitor));
maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
+ maxfd = UDEV_MAX(maxfd, event_done_pipe[READ_END]);
maxfd = UDEV_MAX(maxfd, inotify_fd);
while (!udev_exit) {
int fdcount;
FD_ZERO(&readfds);
FD_SET(signal_pipe[READ_END], &readfds);
+ FD_SET(event_done_pipe[READ_END], &readfds);
FD_SET(udev_ctrl_get_fd(udev_ctrl), &readfds);
FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
if (inotify_fd >= 0)
@@ -931,6 +911,21 @@
read(signal_pipe[READ_END], &buf, sizeof(buf));
}
+ /* processed events */
+ if (FD_ISSET(event_done_pipe[READ_END], &readfds)) {
+ struct udev_event *buf[32];
+ ssize_t count;
+ unsigned i;
+
+ count = read(event_done_pipe[READ_END], &buf, sizeof(buf));
+ if (count < 0) {
+ err(udev, "read error on thread pipe: %m\n");
+ } else {
+ for (i = 0; i < count / sizeof(buf[0]); i++)
+ udev_done(buf[i]);
+ }
+ }
+
/* rules directory inotify watch */
if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) {
int nbytes;
@@ -963,11 +958,6 @@
}
}
- if (sigchilds_waiting) {
- sigchilds_waiting = 0;
- reap_sigchilds();
- }
-
if (run_exec_q) {
run_exec_q = 0;
if (!stop_exec_q)
@@ -976,8 +966,19 @@
}
cleanup_queue_dir(udev);
rc = 0;
+ /* Kill event threads before destroying global udev context */
+ if (!udev_list_is_empty(&event_list)) {
+ struct udev_list_node *loop;
+
+ udev_list_node_foreach(loop, &event_list) {
+ struct udev_event *loop_event = node_to_event(loop);
+
+ event_thread_kill(loop_event);
+ }
+ }
exit:
udev_rules_unref(rules);
+ pthread_attr_destroy(&thread_attr);
if (signal_pipe[READ_END] >= 0)
close(signal_pipe[READ_END]);
if (signal_pipe[WRITE_END] >= 0)
@@ -986,6 +987,7 @@
if (inotify_fd >= 0)
close(inotify_fd);
udev_monitor_unref(kernel_monitor);
+ udev_exec_cleanup(udev);
udev_selinux_exit(udev);
udev_unref(udev);
logging_close();
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
next prev parent reply other threads:[~2008-12-24 11:37 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-12-22 11:25 Revisiting threaded udevd Alan Jenkins
2008-12-24 11:37 ` Scott James Remnant [this message]
2008-12-25 17:36 ` Alan Jenkins
2008-12-27 10:29 ` Scott James Remnant
2008-12-27 19:07 ` Alan Jenkins
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1230118621.18723.57.camel@quest \
--to=scott@canonical.com \
--cc=linux-hotplug@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).