]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/device: handle ID_PROCESSING udev property
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 25 Nov 2024 16:19:35 +0000 (01:19 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 20 Dec 2024 01:52:57 +0000 (10:52 +0900)
If an enumerated device has ID_PROCESSING=1 property, and the service
manager does not know if the device has been processed by udevd
previously (that is, Device.deserialized_found does not have
DEVICE_FOUND_UDEV), then drop DEVICE_FOUND_UDEV flag from the device and
make the device not enter the active state.

Follow-up for 405be62f05d76f1845f347737b5972158c79dd3e, which was
reverted by c4fc22c4defc5983e53f4ce048e15ea7d31e6a75.

src/core/device.c
src/core/device.h

index 85b94e2d60ca91a4311574d63ffc7779e98d407b..e201d099768e668d3127084428a9b71165321320 100644 (file)
@@ -334,6 +334,20 @@ static void device_catchup(Unit *u) {
         Device *d = ASSERT_PTR(DEVICE(u));
 
         /* Second, let's update the state with the enumerated state */
+
+        /* If Device.found (set from Device.deserialized_found) does not have DEVICE_FOUND_UDEV, and the
+         * device has not been processed by udevd while enumeration, it indicates the unit was never active
+         * before reexecution, hence we can safely drop the flag from Device.enumerated_found. The device
+         * will be set up later when udev finishes processing (see also comment in
+         * device_setup_devlink_unit_one()).
+         *
+         * NB: ðŸ’£ðŸ’£ðŸ’£ If Device.found already contains udev, i.e. the unit was fully ready before
+         * reexecution, do not unset the flag. Otherwise, e.g. if systemd-udev-trigger.service is started
+         * just before reexec, reload, and so on, devices being reprocessed (carrying ID_PROCESSING=1
+         * property) on enumeration and will enter dead state. See issue #35329. */
+        if (!FLAGS_SET(d->found, DEVICE_FOUND_UDEV) && !d->processed)
+                d->enumerated_found &= ~DEVICE_FOUND_UDEV;
+
         device_update_found_one(d, d->enumerated_found, _DEVICE_FOUND_MASK);
 }
 
@@ -779,8 +793,16 @@ static int device_setup_devlink_unit_one(Manager *m, const char *devlink, Set **
         assert(ready_units);
         assert(not_ready_units);
 
-        if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev))
+        if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev)) {
+                if (MANAGER_IS_RUNNING(m) && device_is_processed(dev) <= 0)
+                        /* The device is being processed by udevd. We will receive relevant uevent for the
+                         * device later when completed. Let's ignore the device now. */
+                        return 0;
+
+                /* Note, even if the device is being processed by udevd, setup the unit on enumerate.
+                 * See also the comments in device_catchup(). */
                 return device_setup_unit(m, dev, devlink, /* main = */ false, ready_units);
+        }
 
         /* the devlink is already removed or not ready */
         if (device_by_path(m, devlink, &u) < 0)
@@ -1051,13 +1073,32 @@ static void device_enumerate(Manager *m) {
 
         FOREACH_DEVICE(e, dev) {
                 _cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
+                const char *syspath;
+                bool processed;
                 Device *d;
 
+                r = sd_device_get_syspath(dev, &syspath);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to get syspath of enumerated device, ignoring: %m");
+                        continue;
+                }
+
+                r = device_is_processed(dev);
+                if (r < 0)
+                        log_device_debug_errno(dev, r, "Failed to check if device is processed by udevd, assuming not: %m");
+                processed = r > 0;
+
                 if (device_setup_units(m, dev, &ready_units, &not_ready_units) < 0)
                         continue;
 
-                SET_FOREACH(d, ready_units)
+                SET_FOREACH(d, ready_units) {
                         device_update_found_one(d, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+
+                        /* Why we need to check the syspath here? Because the device unit may be generated by
+                         * a devlink, and the syspath may be different from the one of the original device. */
+                        if (path_equal(d->sysfs, syspath))
+                                d->processed = processed;
+                }
                 SET_FOREACH(d, not_ready_units)
                         device_update_found_one(d, DEVICE_NOT_FOUND, DEVICE_FOUND_UDEV);
         }
index 4eb656777aec10da35102c1c3addfb992f5b6d77..631f45d45ebec647d5f2847b1c4a7cae5307bc4a 100644 (file)
@@ -29,7 +29,9 @@ struct Device {
 
         DeviceState state, deserialized_state;
         DeviceFound found, deserialized_found, enumerated_found;
-
+        bool processed; /* Whether udevd has done processing the device, i.e. the device has database and
+                         * ID_PROCESSING=1 udev property is not set. This is used only by enumeration and
+                         * subsequent catchup process. */
         bool bind_mounts;
 
         /* The SYSTEMD_WANTS udev property for this device the last time we saw it */