From: Chris Friesen <chris_friesen@sympatico.ca>
To: linux-hotplug@vger.kernel.org
Subject: [PATCH[ udevd race conditions and performance, assorted cleanups
Date: Sat, 27 Mar 2004 04:48:15 +0000 [thread overview]
Message-ID: <4065078F.1070008@sympatico.ca> (raw)
[-- Attachment #1: Type: text/plain, Size: 1013 bytes --]
This patch covers a number of areas:
1) sysfs.h is fixed up to use the common dbg() macro. This fixes the
case where DEBUG is defined but USE_LOG isn't.
2) udevstart.c is modified to include the proper headers, rather than
getting them indirectly which can break depending on Makefile flags
3) udevd.c gets some major changes:
a) I added a pipe from the signal handler. This fixes the race
conditions that I mentioned earlier. Basically, the point of the pipe
is to force the select() call to return immediately if a signal handler
fired before we actually started the select() call. This then lets us
run the appropriate code based on flags set in the signal handler proper.
b) I added a number of flags to coalesce calls to common routines. This
should make things slightly more efficient.
c) since most calls will tend to come in with a sequence number larger
than what has been received, I switched msg_queue_insert() to scan the
msg_list backwards to improve performance.
Comments?
Chris
[-- Attachment #2: udevd.diff --]
[-- Type: text/plain, Size: 7923 bytes --]
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet udev/libsysfs/sysfs.h myudev/libsysfs/sysfs.h
--- udev/libsysfs/sysfs.h Fri Mar 26 21:01:44 2004
+++ myudev/libsysfs/sysfs.h Fri Mar 26 22:08:11 2004
@@ -37,9 +37,7 @@
#ifdef DEBUG
#include "../logging.h"
#define dprintf(format, arg...) \
- do { \
- log_message (LOG_DEBUG , "%s: " format , __FUNCTION__ , ## arg); \
- } while (0)
+ dbg(format, ##arg)
#else
#define dprintf(format, arg...) do { } while (0)
#endif
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet udev/udevd.c myudev/udevd.c
--- udev/udevd.c Fri Mar 26 21:01:44 2004
+++ myudev/udevd.c Fri Mar 26 22:50:41 2004
@@ -33,6 +33,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
+#include <fcntl.h>
#include "list.h"
#include "udev.h"
@@ -41,9 +42,12 @@
#include "udevd.h"
#include "logging.h"
+static int pipefds[2];
static int expected_seqnum = 0;
volatile static int children_waiting;
-volatile static int msg_q_timeout;
+volatile static int run_msg_q;
+volatile static int sig_flag;
+static int run_exec_q;
static LIST_HEAD(msg_list);
static LIST_HEAD(exec_list);
@@ -51,6 +55,8 @@
static void exec_queue_manager(void);
static void msg_queue_manager(void);
+static void user_sighandler(void);
+static void reap_kids(void);
#ifdef LOG
unsigned char logname[LOGNAME_SIZE];
@@ -66,10 +72,12 @@
static void msg_dump_queue(void)
{
+#ifdef DEBUG
struct hotplug_msg *msg;
list_for_each_entry(msg, &msg_list, list)
dbg("sequence %d in queue", msg->seqnum);
+#endif
}
static void msg_dump(struct hotplug_msg *msg)
@@ -92,7 +100,6 @@
{
list_del(&msg->list);
free(msg);
- exec_queue_manager();
}
/* orders the message in the queue by sequence number */
@@ -100,18 +107,20 @@
{
struct hotplug_msg *loop_msg;
- /* sort message by sequence number into list*/
- list_for_each_entry(loop_msg, &msg_list, list)
- if (loop_msg->seqnum > msg->seqnum)
+ /* sort message by sequence number into list. events
+ * will tend to come in order, so scan the list backwards
+ */
+ list_for_each_entry_reverse(loop_msg, &msg_list, list)
+ if (loop_msg->seqnum < msg->seqnum)
break;
- list_add_tail(&msg->list, &loop_msg->list);
+ list_add(&msg->list, &loop_msg->list);
dbg("queued message seq %d", msg->seqnum);
/* store timestamp of queuing */
msg->queue_time = time(NULL);
/* run msg queue manager */
- msg_queue_manager();
+ run_msg_q = 1;
return ;
}
@@ -138,6 +147,9 @@
case -1:
dbg("fork of child failed");
run_queue_delete(msg);
+ /* note: we never managed to run, so we had no impact on
+ * running_with_devpath(), so don't bother setting run_exec_q
+ */
break;
default:
/* get SIGCHLD in main loop */
@@ -180,7 +192,7 @@
static void msg_move_exec(struct hotplug_msg *msg)
{
list_move_tail(&msg->list, &exec_list);
- exec_queue_manager();
+ run_exec_q = 1;
expected_seqnum = msg->seqnum+1;
dbg("moved seq %d to exec, next expected is %d",
msg->seqnum, expected_seqnum);
@@ -276,7 +288,7 @@
/* if no seqnum is given, we move straight to exec queue */
if (msg->seqnum == -1) {
list_add(&msg->list, &exec_list);
- exec_queue_manager();
+ run_exec_q = 1;
} else {
msg_queue_insert(msg);
}
@@ -289,19 +301,37 @@
static void sig_handler(int signum)
{
+ int rc;
switch (signum) {
case SIGINT:
case SIGTERM:
exit(20 + signum);
break;
case SIGALRM:
- msg_q_timeout = 1;
+ /* set flag, then write to pipe if needed */
+ run_msg_q = 1;
+ goto do_write;
break;
case SIGCHLD:
+ /* set flag, then write to pipe if needed */
children_waiting = 1;
+ goto do_write;
break;
default:
dbg("unhandled signal");
+ return;
+ }
+
+do_write:
+ /* if pipe is empty, write to pipe to force select to return
+ * immediately when it gets called
+ */
+ if (!sig_flag) {
+ rc = write(pipefds[1],&signum,sizeof(signum));
+ if (rc < 0)
+ dbg("unable to write to pipe");
+ else
+ sig_flag = 1;
}
}
@@ -314,19 +344,53 @@
if (msg->pid == pid) {
dbg("<== exec seq %d came back", msg->seqnum);
run_queue_delete(msg);
+
+ /* we want to run the exec queue manager since there may
+ * be events waiting with the devpath of the one that
+ * just finished
+ */
+ run_exec_q = 1;
return;
}
}
}
+static void reap_kids()
+{
+ /* reap all dead children */
+ while(1) {
+ int pid = waitpid(-1, 0, WNOHANG);
+ if ((pid == -1) || (pid == 0))
+ break;
+ udev_done(pid);
+ }
+}
+
+/* just read everything from the pipe and clear the flag,
+ * the useful flags were set in the signal handler
+ */
+static void user_sighandler()
+{
+ int sig;
+ while(1) {
+ int rc = read(pipefds[0],&sig,sizeof(sig));
+ if (rc < 0)
+ break;
+
+ sig_flag = 0;
+ }
+}
+
+
int main(int argc, char *argv[])
{
- int ssock;
+ int ssock, maxsockplus;
struct sockaddr_un saddr;
socklen_t addrlen;
int retval;
const int on = 1;
struct sigaction act;
+ fd_set readfds;
init_logging("udevd");
dbg("version %s", UDEV_VERSION);
@@ -335,16 +399,32 @@
dbg("need to be root, exit");
exit(1);
}
+
+ /* setup signal handler pipe */
+ retval = pipe(pipefds);
+ if (retval < 0) {
+ dbg("error getting pipes: %s", strerror(errno));
+ exit(1);
+ }
+
+ retval = fcntl(pipefds[0], F_SETFL, O_NONBLOCK);
+ if (retval < 0) {
+ dbg("fcntl on read pipe: %s", strerror(errno));
+ exit(1);
+ }
+
+ retval = fcntl(pipefds[1], F_SETFL, O_NONBLOCK);
+ if (retval < 0) {
+ dbg("fcntl on write pipe: %s", strerror(errno));
+ exit(1);
+ }
- /* set signal handler */
+ /* set signal handlers */
act.sa_handler = sig_handler;
- sigemptyset (&act.sa_mask);
+ sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
-
- /* we want these two to interrupt system calls */
- act.sa_flags = 0;
sigaction(SIGALRM, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
@@ -370,23 +450,50 @@
/* enable receiving of the sender credentials */
setsockopt(ssock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ FD_ZERO(&readfds);
+ FD_SET(ssock, &readfds);
+ FD_SET(pipefds[0], &readfds);
+ maxsockplus = ssock+1;
while (1) {
- handle_msg(ssock);
-
- while(msg_q_timeout) {
- msg_q_timeout = 0;
- msg_queue_manager();
+ fd_set workreadfds = readfds;
+ retval = select(maxsockplus, &workreadfds, NULL, NULL, NULL);
+
+ if (retval < 0) {
+ dbg("error in select: %s", strerror(errno));
+ continue;
}
-
- while(children_waiting) {
+
+ if (FD_ISSET(ssock, &workreadfds))
+ handle_msg(ssock);
+
+ if (FD_ISSET(pipefds[0], &workreadfds))
+ user_sighandler();
+
+ if (children_waiting) {
children_waiting = 0;
- /* reap all dead children */
- while(1) {
- int pid = waitpid(-1, 0, WNOHANG);
- if ((pid == -1) || (pid == 0))
- break;
- udev_done(pid);
+ reap_kids();
+ }
+
+ if (run_msg_q) {
+ run_msg_q = 0;
+ msg_queue_manager();
+ }
+
+ if (run_exec_q) {
+
+ /* this is tricky. exec_queue_manager() loops over exec_list, and
+ * calls running_with_devpath(), which loops over running_list. This gives
+ * O(N*M), which can get *nasty*. Clean up running_list before
+ * calling exec_queue_manager().
+ */
+
+ if (children_waiting) {
+ children_waiting = 0;
+ reap_kids();
}
+
+ run_exec_q = 0;
+ exec_queue_manager();
}
}
exit:
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet udev/udevstart.c myudev/udevstart.c
--- udev/udevstart.c Fri Mar 26 21:01:44 2004
+++ myudev/udevstart.c Fri Mar 26 23:33:41 2004
@@ -29,6 +29,8 @@
#include <ctype.h>
#include <dirent.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "logging.h"
next reply other threads:[~2004-03-27 4:48 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-03-27 4:48 Chris Friesen [this message]
2004-03-27 11:30 ` [PATCH[ udevd race conditions and performance, assorted cleanups Kay Sievers
2004-03-28 1:08 ` Kay Sievers
2004-03-29 4:47 ` Chris Friesen
2004-03-31 23:04 ` Greg KH
2004-03-31 23:04 ` Greg KH
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=4065078F.1070008@sympatico.ca \
--to=chris_friesen@sympatico.ca \
--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.