diff options
author | Hans de Goede <hdegoede@redhat.com> | 2021-03-05 12:25:36 +0100 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2021-03-06 10:57:05 +0100 |
commit | 42ee6809b0cb669c23cac8cb289b0d101a19fa47 (patch) | |
tree | fbaac0a7158cfa2585cbb0b76c97f006399b829e | |
parent | dc8dc20b2685d09c68b4f90a1a34f0f9cc024a74 (diff) |
ply-device-manager: Speed up DRM-connector probing
During the initial monitor/connector enumeration on boot the kernel
fires a large number of change events. If we process these 1 by 1,
we spend a lot of time probing the DRM-connectors. So instead we
collect them all and then coalescence them so that if there are multiple
change events pending for a single card, we only re-probe the card once.
Here are some numbers of the probing times before / after this patch:
1. Lenovo X1 carbon 8th gen connected to a Lenovo Thunderbolt dock gen 2
with 2 FullHD monitors connected:
Before: add event card0: 00:00:02.543 last change complete at: 00:00:04.250,
12 change events processed, 13 probes done!
After: add event card0: 00:00:02.548 last change complete at: 00:00:04.049
1 change event processed, 2 probes done!
2. Intel skylake CPU + iGPU based desktop with 2 FullHD monitors connected:
Before: add event card0: 00:00:02.394 last change complete at: 00:00:05.024,
5 change events processed, 6 probes done!
After: add event card0: 00:00:02.343 last change complete at: 00:00:03.744,
1 change event processed, 2 probes done!
In the Thunderbolt dock case we probe the DRM-connectors 2 times instead
of 13 times after this change. This does not lead to a big speed-up though
because the dock caches the monitors EDID info and the DP aux channel to
the dock is quite fast.
In the desktop case we only reduce the amount of probes from 6 to 2, so
less then in the Thunderbolt dock case, but since we don't have the EDID
caching happening there this does reduce the time which it takes to probe
the DRM-connectors from 2.6 seconds to 1.4 seconds which is a huge
improvement.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r-- | src/libply-splash-core/ply-device-manager.c | 93 |
1 files changed, 77 insertions, 16 deletions
diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c index 322a6f4e..13c2b1b2 100644 --- a/src/libply-splash-core/ply-device-manager.c +++ b/src/libply-splash-core/ply-device-manager.c @@ -426,33 +426,94 @@ verify_add_or_change (ply_device_manager_t *manager, return true; } -static void -on_udev_event (ply_device_manager_t *manager) +static bool +duplicate_device_path (ply_list_t *events, const char *device_path) { struct udev_device *device; + ply_list_node_t *node; + + for (node = ply_list_get_first_node (events); + node; node = ply_list_get_next_node (events, node)) { + device = ply_list_node_get_data (node); + + if (strcmp (udev_device_get_devnode (device), device_path) == 0) + return true; + } + + return false; +} + +static void +process_udev_add_or_change_events (ply_device_manager_t *manager, ply_list_t *events) +{ const char *action, *device_path; + struct udev_device *device; + ply_list_node_t *node; - device = udev_monitor_receive_device (manager->udev_monitor); - if (device == NULL) - return; + while ((node = ply_list_get_first_node (events))) { + device = ply_list_node_get_data (node); + action = udev_device_get_action (device); + device_path = udev_device_get_devnode (device); - action = udev_device_get_action (device); - device_path = udev_device_get_devnode (device); - if (action == NULL || device_path == NULL) { + on_drm_udev_add_or_change (manager, action, device_path, device); + + ply_list_remove_node (events, node); udev_device_unref (device); - return; } +} + +static void +on_udev_event (ply_device_manager_t *manager) +{ + const char *action, *device_path; + struct udev_device *device; + ply_list_t *pending_events; + + pending_events = ply_list_new (); + + /* + * During the initial monitor/connector enumeration on boot the kernel + * fires a large number of change events. If we process these 1 by 1, + * we spend a lot of time probing the drm-connectors. So instead we + * collect them all and then coalescence them so that if there are multiple + * change events pending for a single card, we only re-probe the card once. + */ + while ((device = udev_monitor_receive_device (manager->udev_monitor))) { + action = udev_device_get_action (device); + device_path = udev_device_get_devnode (device); - ply_trace ("got %s event for device %s", action, device_path); + if (action == NULL || device_path == NULL) + goto unref; - if (strcmp (action, "add") == 0 || strcmp (action, "change") == 0) { - if (verify_add_or_change (manager, action, device_path, device)) - on_drm_udev_add_or_change (manager, action, device_path, device); - } else if (strcmp (action, "remove") == 0) { - free_devices_from_device_path (manager, device_path, true); + ply_trace ("got %s event for device %s", action, device_path); + + /* + * Add/change events before and after a remove may not be + * coalesced together. So flush the queue and then process + * the remove event immediately. + */ + if (strcmp (action, "remove") == 0) { + process_udev_add_or_change_events (manager, pending_events); + free_devices_from_device_path (manager, device_path, true); + goto unref; + } + + if (!verify_add_or_change (manager, action, device_path, device)) + goto unref; + + if (duplicate_device_path (pending_events, device_path)) { + ply_trace ("ignoring duplicate %s event for device %s", action, device_path); + goto unref; + } + + ply_list_append_data (pending_events, udev_device_ref(device)); +unref: + udev_device_unref (device); } - udev_device_unref (device); + process_udev_add_or_change_events (manager, pending_events); + + ply_list_free (pending_events); } static void |