udevd: convert to event worker processes

Event processes now get re-used after they handled an event. This reduces
pressure on the CPU significantly because cloned event processes no longer
cause page faults in the main daemon. After the events have settled, the
no longer needed worker processes get killed.
This commit is contained in:
Kay Sievers 2009-06-04 01:44:04 +02:00
parent b61f48a0e8
commit 1e03b754ae
13 changed files with 587 additions and 440 deletions

12
NEWS
View File

@ -2,9 +2,17 @@ udev 143
========
Bugfixes.
Event processes now get re-used after they handled an event. This reduces
pressure on the CPU significantly because cloned event processes no longer
cause page faults in the main daemon. After the events have settled, the
no longer needed worker processes get killed.
To be able to use signalfd(), udev depends on kernel version 2.6.25 now.
Also inotify support is required now to 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.
no longer a /dev/.udev/queue/ directory. The current event queue can be
accessed with udevadm settle and libudedv.
udev 142
========

25
README
View File

@ -9,11 +9,13 @@ Important Note:
recommend to replace a distro's udev installation with the upstream version.
Requirements:
- Version 2.6.22 of the Linux kernel for reliable operation of this release of
udev. The kernel must not use the CONFIG_SYSFS_DEPRECATED* option.
- Version 2.6.25 of the Linux kernel with sysfs, procfs, signalfd, inotify,
unix domain sockets, networking and hotplug enabled.
- The kernel must have sysfs, unix domain sockets and networking enabled.
Unix domain sockets (CONFIG_UNIX) as a loadable kernel module is not
- For reliable operation, the kernel must not use the CONFIG_SYSFS_DEPRECATED*
option.
- Unix domain sockets (CONFIG_UNIX) as a loadable kernel module is not
supported.
- The proc filesystem must be mounted on /proc/, the sysfs filesystem must
@ -29,21 +31,18 @@ Operation:
Udev creates and removes device nodes in /dev/, based on events the kernel
sends out on device discovery or removal.
- Very early in the boot process, the /dev/ directory should get a 'tmpfs'
filesystem mounted, which is populated from scratch by udev. Created nodes
or changed permissions will not survive a reboot, which is intentional.
- Early in the boot process, the /dev/ directory should get a 'tmpfs'
filesystem mounted, which is maintained by udev. Created nodes or changed
permissions will not survive a reboot, which is intentional.
- The content of /lib/udev/devices/ directory which contains the nodes,
symlinks and directories, which are always expected to be in /dev, should
be copied over to the tmpfs mounted /dev, to provide the required nodes
to initialize udev and continue booting.
- The old hotplug helper /sbin/hotplug should be disabled on bootup, before
actions like loading kernel modules are taken, which may cause a lot of
events.
- The udevd daemon must be started on bootup to receive netlink uevents
from the kernel driver core.
- The old hotplug helper /sbin/hotplug should be disabled in the kernel
configuration, it is not needed, and may render the system unusable
because of a fork-bombing behavior.
- All kernel events are matched against a set of specified rules in
/lib/udev/rules.d/ which make it possible to hook into the event

1
TODO
View File

@ -1,3 +1,4 @@
o add tests for kernel provided DEVNAME logic
o drop modprobe floppy alias (SUSE), it will be in the module (2.6.30)
o remove MMC rules, they got a modalias now (2.6.30)

View File

@ -5,6 +5,7 @@ AC_PREREQ(2.60)
AM_INIT_AUTOMAKE([check-news foreign 1.9 dist-bzip2])
AC_DISABLE_STATIC
AC_USE_SYSTEM_EXTENSIONS
dnl AM_SILENT_RULES
AC_SYS_LARGEFILE
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_LIBTOOL
@ -23,10 +24,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}'])

View File

@ -14,7 +14,6 @@ common_ldadd =
common_files = \
udev.h \
udev-sysdeps.h \
udev-event.c \
udev-watch.c \
udev-node.c \

View File

@ -32,15 +32,17 @@ struct udev_monitor {
int refcount;
int sock;
struct sockaddr_nl snl;
struct sockaddr_nl snl_peer;
struct sockaddr_nl snl_trusted_sender;
struct sockaddr_nl snl_destination;
struct sockaddr_un sun;
socklen_t addrlen;
struct udev_list_node filter_subsystem_list;
};
enum udev_monitor_netlink_group {
UDEV_MONITOR_KERNEL = 1,
UDEV_MONITOR_UDEV = 2,
UDEV_MONITOR_NONE,
UDEV_MONITOR_KERNEL,
UDEV_MONITOR_UDEV,
};
#define UDEV_MONITOR_MAGIC 0xcafe1dea
@ -171,11 +173,11 @@ 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 = UDEV_MONITOR_KERNEL;
group = UDEV_MONITOR_NONE;
else if (strcmp(name, "udev") == 0)
group = UDEV_MONITOR_UDEV;
else if (strcmp(name, "kernel") == 0)
group = UDEV_MONITOR_KERNEL;
else
return NULL;
@ -193,8 +195,10 @@ 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;
/* default destination for sending */
udev_monitor->snl_destination.nl_family = AF_NETLINK;
udev_monitor->snl_destination.nl_groups = UDEV_MONITOR_UDEV;
dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group);
return udev_monitor;
@ -281,6 +285,12 @@ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
return err;
}
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender)
{
udev_monitor->snl_trusted_sender.nl_pid = sender->snl.nl_pid;
return 0;
}
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
{
int err;
@ -293,6 +303,19 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
udev_monitor_filter_update(udev_monitor);
err = bind(udev_monitor->sock,
(struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
if (err == 0) {
struct sockaddr_nl snl;
socklen_t addrlen;
/*
* get the address the kernel has assigned us
* it is usually, but not neccessarily the pid
*/
addrlen = sizeof(struct sockaddr_nl);
err = getsockname(udev_monitor->sock, (struct sockaddr *)&snl, &addrlen);
if (err == 0)
udev_monitor->snl.nl_pid = snl.nl_pid;
}
} else {
return -EINVAL;
}
@ -314,6 +337,15 @@ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int
return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
}
int udev_monitor_disconnect(struct udev_monitor *udev_monitor)
{
int err;
err = close(udev_monitor->sock);
udev_monitor->sock = -1;
return err;
}
/**
* udev_monitor_ref:
* @udev_monitor: udev monitor
@ -478,10 +510,13 @@ retry:
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) {
/* unicast message, check if we trust the sender */
if (udev_monitor->snl_trusted_sender.nl_pid == 0 ||
snl.nl_pid != udev_monitor->snl_trusted_sender.nl_pid) {
info(udev_monitor->udev, "unicast netlink message ignored\n");
return NULL;
}
} else 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;
@ -621,7 +656,8 @@ 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_monitor *destination, struct udev_device *udev_device)
{
struct msghdr smsg;
struct iovec iov[2];
@ -683,8 +719,16 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_devi
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;
/*
* Use custom address for target, or the default one.
*
* If we send to a muticast group, we will get
* ECONNREFUSED, which is expected.
*/
if (destination != NULL)
smsg.msg_name = &destination->snl;
else
smsg.msg_name = &udev_monitor->snl_destination;
smsg.msg_namelen = sizeof(struct sockaddr_nl);
} else {
return -1;

View File

@ -86,7 +86,10 @@ 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_disconnect(struct udev_monitor *udev_monitor);
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender);
int udev_monitor_send_device(struct udev_monitor *udev_monitor,
struct udev_monitor *destination, struct udev_device *udev_device);
int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
/* libudev-ctrl - daemon runtime setup */

View File

@ -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, NULL, event->dev);
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))

View File

@ -1,44 +0,0 @@
/*
* wrapping of libc features and kernel interfaces
*
* Copyright (C) 2005-2008 Kay Sievers <kay.sievers@vrfy.org>
*
* 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

View File

@ -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

View File

@ -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);

View File

@ -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>

File diff suppressed because it is too large Load Diff