mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
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:
parent
b61f48a0e8
commit
1e03b754ae
12
NEWS
12
NEWS
@ -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
25
README
@ -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
1
TODO
@ -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)
|
||||
|
@ -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}'])
|
||||
|
@ -14,7 +14,6 @@ common_ldadd =
|
||||
|
||||
common_files = \
|
||||
udev.h \
|
||||
udev-sysdeps.h \
|
||||
udev-event.c \
|
||||
udev-watch.c \
|
||||
udev-node.c \
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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))
|
||||
|
@ -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
|
@ -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
|
||||
|
10
udev/udev.h
10
udev/udev.h
@ -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);
|
||||
|
@ -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>
|
||||
|
828
udev/udevd.c
828
udev/udevd.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user