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 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).