summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2021-03-05 12:25:36 +0100
committerHans de Goede <hdegoede@redhat.com>2021-03-06 10:57:05 +0100
commit42ee6809b0cb669c23cac8cb289b0d101a19fa47 (patch)
treefbaac0a7158cfa2585cbb0b76c97f006399b829e
parentdc8dc20b2685d09c68b4f90a1a34f0f9cc024a74 (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.c93
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