udevadm: trigger: implement --initialized-match/nomatch arguments

systemd-udev-trigger.service by default triggeres all devices regardless
of whether they were already recognized by systemd-udevd.

There are machines (especially in embedded environments) where
systemd-udev-trigger.service is configured to run at a later stage of
the boot sequence, which can lead to quite a lot of devices being
triggered although they were already recognized by systemd-udevd.

Re-triggering a lot of devices is a relatively expensive operation and
therefore should be avoided if unnecessary.

Therefore this patch introduces --initialized-nomatch, which filters out
devices that are already present in the udev database. For consistance
reasons --initialized-match is implemented as well, which filters out devices
that are *not* already present in the udev database.

Replaces #19949.
This commit is contained in:
Danilo Krummrich 2021-06-14 17:46:00 +02:00 committed by Yu Watanabe
parent d8b50e5df7
commit 678f2b1667
4 changed files with 46 additions and 1 deletions

View file

@ -395,6 +395,35 @@
then each matching result is ORed, that is, all children of each specified device are triggered.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--initialized-match</option></term>
<term><option>--initialized-nomatch</option></term>
<listitem>
<para>When <option>--initialized-match</option> is specified, trigger events for devices
that are already initialized by <command>systemd-udevd</command>, and skip devices that
are not initialized yet.</para>
<para>When <option>--initialized-nomatch</option> is specified, trigger events for devices
that are not initialized by <command>systemd-udevd</command> yet, and skip devices that
are already initialized.</para>
<para>Here, initialized devices are those for which at least one udev rule already
completed execution for any action but <literal>remove</literal> — that set a property
or other device setting (and thus has an entry in the udev device database). Devices are
no longer considered initialized if a <literal>remove</literal> action is seen for them
(which removes their entry in the udev device database). Note that devices that have no
udev rules are never considered initialized, but might still be announced via the sd-device
API (or similar). Typically, it is thus essential that applications which intend to use
such a match, make sure a suitable udev rule is installed that sets at least one property
on devices that shall be matched.</para>
<para>WARNING: <option>--initialized-nomatch</option> can potentially save a significant
amount of time compared to re-triggering all devices in the system and e.g. can be used to
optimize boot time. However, this is not safe to be used in a boot sequence in general.
Especially, when udev rules for a device depend on its parent devices (e.g.
<literal>ATTRS</literal> or <literal>IMPORT{parent}</literal> keys, see
<citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for more details), the final state of the device becomes easily unstable with this option.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-w</option></term>
<term><option>--settle</option></term>

View file

@ -51,7 +51,8 @@ _udevadm() {
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
-w --wait-for-initialization --value'
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file --property'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid
--initialized-match --initialized-nomatch'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match

View file

@ -34,6 +34,8 @@ _udevadm_trigger(){
'--tag-match=property[Trigger events for devices with a matching tag.]' \
'--sysname-match=[Trigger events for devices with a matching sys device name.]' \
'--parent-match=[Trigger events for all children of a given device.]' \
'--initialized-match[Trigger events for devices that are already initialized.]' \
'--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
'--uuid[Print synthetic uevent UUID.]' \
'--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]'
}

View file

@ -11,6 +11,7 @@
#include "device-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "set.h"
@ -226,6 +227,8 @@ static int help(void) {
" -y --sysname-match=NAME Trigger devices with this /sys path\n"
" --name-match=NAME Trigger devices with this /dev name\n"
" -b --parent-match=NAME Trigger devices with that parent device\n"
" --initialized-match Trigger devices that are already initialized\n"
" --initialized-nomatch Trigger devices that are not initialized yet\n"
" -w --settle Wait for the triggered events to complete\n"
" --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n"
" before triggering uevents\n"
@ -243,6 +246,8 @@ int trigger_main(int argc, char *argv[], void *userdata) {
ARG_PING,
ARG_UUID,
ARG_PRIORITIZED_SUBSYSTEM,
ARG_INITIALIZED_MATCH,
ARG_INITIALIZED_NOMATCH,
};
static const struct option options[] = {
@ -260,6 +265,8 @@ int trigger_main(int argc, char *argv[], void *userdata) {
{ "sysname-match", required_argument, NULL, 'y' },
{ "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' },
{ "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH },
{ "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH },
{ "settle", no_argument, NULL, 'w' },
{ "wait-daemon", optional_argument, NULL, ARG_PING },
{ "version", no_argument, NULL, 'V' },
@ -426,6 +433,12 @@ int trigger_main(int argc, char *argv[], void *userdata) {
}
break;
}
case ARG_INITIALIZED_MATCH:
case ARG_INITIALIZED_NOMATCH:
r = device_enumerator_add_match_is_initialized(e, c == ARG_INITIALIZED_MATCH ? MATCH_INITIALIZED_YES : MATCH_INITIALIZED_NO);
if (r < 0)
return log_error_errno(r, "Failed to set initialized filter: %m");
break;
case 'V':
return print_version();
case 'h':