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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.