From 10e686c2128bde0e70e7c137191dbe004d2f46fd Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mon, 3 Mar 2014 13:27:30 -0500 Subject: [PATCH 1/2] device-manager: handle drm hotplug separately from /dev/fb Right now, we piggyback off fb subsystem events to know whether or not a drm device is hotplugged (since all drm devices have fb devices for backward compat). This commit makes drm and fb processing more independent, so we don't rely on the compat device being available for drm hotplug to work.. --- src/libply-splash-core/ply-device-manager.c | 182 ++++++++++++++-------------- 1 file changed, 89 insertions(+), 93 deletions(-) diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c index 36e814d..25f7d54 100644 --- a/src/libply-splash-core/ply-device-manager.c +++ b/src/libply-splash-core/ply-device-manager.c @@ -11,60 +11,63 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "ply-device-manager.h" #include #include #include #include #include #include #include #include #include #include #include "ply-logger.h" #include "ply-event-loop.h" #include "ply-hashtable.h" #include "ply-list.h" #include "ply-utils.h" +#define SUBSYSTEM_DRM "drm" +#define SUBSYSTEM_FRAME_BUFFER "graphics" + static void create_seat_for_terminal_and_renderer_type (ply_device_manager_t *manager, const char *device_path, ply_terminal_t *terminal, ply_renderer_type_t renderer_type); struct _ply_device_manager { ply_device_manager_flags_t flags; ply_event_loop_t *loop; ply_hashtable_t *terminals; ply_terminal_t *local_console_terminal; ply_seat_t *local_console_seat; ply_list_t *seats; struct udev *udev_context; struct udev_queue *udev_queue; int udev_queue_fd; ply_fd_watch_t *udev_queue_fd_watch; struct udev_monitor *udev_monitor; ply_seat_added_handler_t seat_added_handler; ply_seat_removed_handler_t seat_removed_handler; void *seat_event_handler_data; }; static void detach_from_event_loop (ply_device_manager_t *manager) { assert (manager != NULL); manager->loop = NULL; } @@ -84,342 +87,334 @@ attach_to_event_loop (ply_device_manager_t *manager, manager); } static bool device_is_for_local_console (ply_device_manager_t *manager, struct udev_device *device) { const char *device_path; struct udev_device *bus_device; char *bus_device_path; const char *boot_vga; bool for_local_console; /* Look at the associated bus device to see if this card is the * card the kernel is using for its console. */ device_path = udev_device_get_syspath (device); asprintf (&bus_device_path, "%s/device", device_path); bus_device = udev_device_new_from_syspath (manager->udev_context, bus_device_path); boot_vga = udev_device_get_sysattr_value (bus_device, "boot_vga"); free (bus_device_path); if (boot_vga != NULL && strcmp (boot_vga, "1") == 0) for_local_console = true; else for_local_console = false; return for_local_console; } -static char * -get_drm_device_node_path_from_fb_device (ply_device_manager_t *manager, - struct udev_device *fb_device) +static bool +fb_device_has_drm_device (ply_device_manager_t *manager, + struct udev_device *fb_device) { struct udev_enumerate *card_matches; struct udev_list_entry *card_entry; const char *id_path; - char *device_node_path = NULL; + bool has_drm_device = false; /* We want to see if the framebuffer is associated with a DRM-capable * graphics card, if it is, we'll use the DRM device */ card_matches = udev_enumerate_new (manager->udev_context); udev_enumerate_add_match_is_initialized(card_matches); udev_enumerate_add_match_parent (card_matches, udev_device_get_parent (fb_device)); udev_enumerate_add_match_subsystem (card_matches, "drm"); id_path = udev_device_get_property_value (fb_device, "ID_PATH"); udev_enumerate_add_match_property (card_matches, "ID_PATH", id_path); ply_trace ("trying to find associated drm node for fb device (path: %s)", id_path); udev_enumerate_scan_devices (card_matches); /* there should only ever be at most one match so we don't iterate through * the list, but just look at the first entry */ card_entry = udev_enumerate_get_list_entry (card_matches); if (card_entry != NULL) { struct udev_device *card_device = NULL; const char *card_node; const char *card_path; card_path = udev_list_entry_get_name (card_entry); card_device = udev_device_new_from_syspath (manager->udev_context, card_path); card_node = udev_device_get_devnode (card_device); if (card_node != NULL) - device_node_path = strdup (card_node); + has_drm_device = true; else ply_trace ("no card node!"); udev_device_unref (card_device); } else { ply_trace ("no card entry!"); } udev_enumerate_unref (card_matches); - return device_node_path; + return has_drm_device; } static void create_seat_for_udev_device (ply_device_manager_t *manager, struct udev_device *device) { bool for_local_console; - char *card_path; + const char *device_path; ply_terminal_t *terminal = NULL; for_local_console = device_is_for_local_console (manager, device); ply_trace ("device is for local console: %s", for_local_console? "yes" : "no"); if (for_local_console) terminal = manager->local_console_terminal; - card_path = get_drm_device_node_path_from_fb_device (manager, device); + device_path = udev_device_get_devnode (device); - if (card_path != NULL) - { - create_seat_for_terminal_and_renderer_type (manager, - card_path, - terminal, - PLY_RENDERER_TYPE_DRM); - free (card_path); - } - else + if (device_path != NULL) { - const char *fb_device_node_path; + const char *subsystem; + ply_renderer_type_t renderer_type = PLY_RENDERER_TYPE_NONE; + + subsystem = udev_device_get_subsystem (device); + + if (strcmp (subsystem, SUBSYSTEM_DRM) == 0) + { + ply_trace ("found DRM device %s", device_path); + renderer_type = PLY_RENDERER_TYPE_DRM; + } + else if (strcmp (subsystem, SUBSYSTEM_FRAME_BUFFER) == 0) + { + ply_trace ("found frame buffer device %s", device_path); + if (!fb_device_has_drm_device (manager, device)) + { + renderer_type = PLY_RENDERER_TYPE_FRAME_BUFFER; + } + else + { + ply_trace ("ignoring, since there's a DRM device associated with it"); + } + } - fb_device_node_path = udev_device_get_devnode (device); - if (fb_device_node_path != NULL) + if (renderer_type != PLY_RENDERER_TYPE_NONE) create_seat_for_terminal_and_renderer_type (manager, - fb_device_node_path, + device_path, terminal, - PLY_RENDERER_TYPE_FRAME_BUFFER); + renderer_type); } } static void free_seat_from_device_path (ply_device_manager_t *manager, const char *device_path) { ply_list_node_t *node; node = ply_list_get_first_node (manager->seats); while (node != NULL) { ply_seat_t *seat; ply_renderer_t *renderer; ply_list_node_t *next_node; const char *renderer_device_path; seat = ply_list_node_get_data (node); next_node = ply_list_get_next_node (manager->seats, node); renderer = ply_seat_get_renderer (seat); if (renderer != NULL) { renderer_device_path = ply_renderer_get_device_name (renderer); if (renderer_device_path != NULL) { if (strcmp (device_path, renderer_device_path) == 0) { ply_trace ("removing seat associated with %s", device_path); if (manager->seat_removed_handler != NULL) manager->seat_removed_handler (manager->seat_event_handler_data, seat); ply_seat_free (seat); ply_list_remove_node (manager->seats, node); break; } } } node = next_node; } } static void free_seat_for_udev_device (ply_device_manager_t *manager, struct udev_device *device) { - char *card_path; - - card_path = get_drm_device_node_path_from_fb_device (manager, device); - - if (card_path != NULL) - { - free_seat_from_device_path (manager, card_path); - free (card_path); - } - else - { - const char *fb_device_node_path; + const char *device_path; - fb_device_node_path = udev_device_get_devnode (device); + device_path = udev_device_get_devnode (device); - if (fb_device_node_path != NULL) - free_seat_from_device_path (manager, fb_device_node_path); - } + if (device_path != NULL) + free_seat_from_device_path (manager, device_path); } static bool -scan_graphics_devices (ply_device_manager_t *manager) +create_seats_for_subsystem (ply_device_manager_t *manager, + const char *subsystem) { - struct udev_enumerate *fb_matches; - struct udev_list_entry *fb_entry; + struct udev_enumerate *matches; + struct udev_list_entry *entry; bool found_device = false; - ply_trace ("scanning for graphics devices"); - /* graphics subsystem is for /dev/fb devices. kms drivers provide /dev/fb for backward - * compatibility, and do so at the end of their initialization, so we can be confident - * that when this subsystem is available the drm device is fully initialized */ - fb_matches = udev_enumerate_new (manager->udev_context); - udev_enumerate_add_match_subsystem (fb_matches, "graphics"); - udev_enumerate_scan_devices (fb_matches); + ply_trace ("creating seats for %s devices", + strcmp (subsystem, SUBSYSTEM_FRAME_BUFFER) == 0? + "frame buffer": + subsystem); - udev_list_entry_foreach (fb_entry, udev_enumerate_get_list_entry (fb_matches)) + matches = udev_enumerate_new (manager->udev_context); + udev_enumerate_add_match_subsystem (matches, subsystem); + udev_enumerate_scan_devices (matches); + + udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (matches)) { - struct udev_device *fb_device = NULL; - const char *fb_path; + struct udev_device *device = NULL; + const char *path; - fb_path = udev_list_entry_get_name (fb_entry); + path = udev_list_entry_get_name (entry); - if (fb_path == NULL) + if (path == NULL) { - ply_trace ("fb path was null!"); + ply_trace ("path was null!"); continue; } - ply_trace ("found device %s", fb_path); + ply_trace ("found device %s", path); - /* skip virtual fbcon device - */ - if (strcmp (fb_path, "/sys/devices/virtual/graphics/fbcon") == 0) - { - ply_trace ("ignoring since it's fbcon"); - continue; - } - - fb_device = udev_device_new_from_syspath (manager->udev_context, fb_path); + device = udev_device_new_from_syspath (manager->udev_context, path); /* if device isn't fully initialized, we'll get an add event later */ - if (udev_device_get_is_initialized (fb_device)) + if (udev_device_get_is_initialized (device)) { ply_trace ("device is initialized"); + /* We only care about devices assigned to a (any) seat. Floating - * devices should be ignored. As a side-effect, this conveniently - * filters out the fbcon device which we don't care about. + * devices should be ignored. */ - if (udev_device_has_tag (fb_device, "seat")) + if (udev_device_has_tag (device, "seat")) { - const char *fb_node; - fb_node = udev_device_get_devnode (fb_device); - if (fb_node != NULL) + const char *node; + node = udev_device_get_devnode (device); + if (node != NULL) { - ply_trace ("found node %s", fb_node); + ply_trace ("found node %s", node); found_device = true; - create_seat_for_udev_device (manager, fb_device); + create_seat_for_udev_device (manager, device); } } else { ply_trace ("device doesn't have a seat tag"); } } else { ply_trace ("it's not initialized"); } - udev_device_unref (fb_device); + udev_device_unref (device); } - udev_enumerate_unref (fb_matches); + udev_enumerate_unref (matches); return found_device; } static void -on_udev_graphics_event (ply_device_manager_t *manager) +on_udev_event (ply_device_manager_t *manager) { struct udev_device *device; const char *action; device = udev_monitor_receive_device (manager->udev_monitor); if (device == NULL) return; action = udev_device_get_action (device); ply_trace ("got %s event for device %s", action, udev_device_get_sysname (device)); if (action == NULL) return; if (strcmp (action, "add") == 0) create_seat_for_udev_device (manager, device); else if (strcmp (action, "remove") == 0) free_seat_for_udev_device (manager, device); udev_device_unref (device); } static void watch_for_udev_events (ply_device_manager_t *manager) { int fd; assert (manager != NULL); assert (manager->udev_monitor == NULL); ply_trace ("watching for udev graphics device add and remove events"); manager->udev_monitor = udev_monitor_new_from_netlink (manager->udev_context, "udev"); - /* The filter matching here mimics the matching done in scan_graphics_devices. - * See the comments in that function, for an explanation of what we're doing. - */ - udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, "graphics", NULL); + udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_DRM, NULL); + udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_FRAME_BUFFER, NULL); udev_monitor_filter_add_match_tag (manager->udev_monitor, "seat"); udev_monitor_enable_receiving (manager->udev_monitor); fd = udev_monitor_get_fd (manager->udev_monitor); ply_event_loop_watch_fd (manager->loop, fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA, (ply_event_handler_t) - on_udev_graphics_event, + on_udev_event, NULL, manager); } static void free_seats (ply_device_manager_t *manager) { ply_list_node_t *node; ply_trace ("removing seats"); node = ply_list_get_first_node (manager->seats); while (node != NULL) { ply_seat_t *seat; ply_list_node_t *next_node; seat = ply_list_node_get_data (node); next_node = ply_list_get_next_node (manager->seats, node); if (manager->seat_removed_handler != NULL) manager->seat_removed_handler (manager->seat_event_handler_data, seat); ply_seat_free (seat); ply_list_remove_node (manager->seats, node); node = next_node; } } static void @@ -657,77 +652,78 @@ create_seats_from_terminals (ply_device_manager_t *manager) int num_consoles; ply_trace ("checking for consoles"); if (manager->flags & PLY_DEVICE_MANAGER_FLAGS_IGNORE_SERIAL_CONSOLES) { num_consoles = 0; ply_trace ("ignoring all consoles but default console because explicitly told to."); } else { num_consoles = add_consoles_from_file (manager, "/sys/class/tty/console/active"); if (num_consoles == 0) ply_trace ("ignoring all consoles but default console because /sys/class/tty/console/active could not be read"); } if (num_consoles > 1) { ply_trace ("serial consoles detected, managing them with details forced"); ply_hashtable_foreach (manager->terminals, (ply_hashtable_foreach_func_t *) create_seat_for_terminal, manager); return true; } return false; } -static bool +static void create_seats_from_udev (ply_device_manager_t *manager) { - bool found_device; + bool found_drm_device, found_fb_device; ply_trace ("Looking for devices from udev"); - found_device = scan_graphics_devices (manager); - if (!found_device) - { - ply_trace ("Creating non-graphical seat, since there's no suitable graphics hardware"); - create_seat_for_terminal_and_renderer_type (manager, - ply_terminal_get_name (manager->local_console_terminal), - manager->local_console_terminal, - PLY_RENDERER_TYPE_NONE); - } - return true; + found_drm_device = create_seats_for_subsystem (manager, SUBSYSTEM_DRM); + found_fb_device = create_seats_for_subsystem (manager, SUBSYSTEM_FRAME_BUFFER); + + if (found_drm_device || found_fb_device) + return; + + ply_trace ("Creating non-graphical seat, since there's no suitable graphics hardware"); + create_seat_for_terminal_and_renderer_type (manager, + ply_terminal_get_name (manager->local_console_terminal), + manager->local_console_terminal, + PLY_RENDERER_TYPE_NONE); } static void create_fallback_seat (ply_device_manager_t *manager) { create_seat_for_terminal_and_renderer_type (manager, ply_terminal_get_name (manager->local_console_terminal), manager->local_console_terminal, PLY_RENDERER_TYPE_AUTO); } static void on_udev_queue_changed (ply_device_manager_t *manager) { if (!udev_queue_get_queue_is_empty (manager->udev_queue)) return; ply_trace ("udev coldplug complete"); ply_event_loop_stop_watching_fd (manager->loop, manager->udev_queue_fd_watch); manager->udev_queue_fd_watch = NULL; udev_queue_unref (manager->udev_queue); close (manager->udev_queue_fd); manager->udev_queue_fd = -1; manager->udev_queue = NULL; create_seats_from_udev (manager); } -- 1.8.3.1 From 20603c552e7e31f65f6265ed7230ba4bcaf12bf5 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mon, 3 Mar 2014 17:25:44 -0500 Subject: [PATCH 2/2] device-manager: defer /dev/fb compat processing until after coldplug We never want to use a /dev/fb device if a DRM device will work instead (since it supports multi-monitor, isn't a legacy interface, etc) Unfortunately, right now plymouthd notices efifb at early start up, see's there is no DRM device associated with it and chooses it for the main display, which causes all sort of problems. This commit defers using /dev/fb devices until after udev settles. --- src/libply-splash-core/ply-device-manager.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c index 25f7d54..d06e1b5 100644 --- a/src/libply-splash-core/ply-device-manager.c +++ b/src/libply-splash-core/ply-device-manager.c @@ -161,62 +161,63 @@ fb_device_has_drm_device (ply_device_manager_t *manager, ply_trace ("no card entry!"); } udev_enumerate_unref (card_matches); return has_drm_device; } static void create_seat_for_udev_device (ply_device_manager_t *manager, struct udev_device *device) { bool for_local_console; const char *device_path; ply_terminal_t *terminal = NULL; for_local_console = device_is_for_local_console (manager, device); ply_trace ("device is for local console: %s", for_local_console? "yes" : "no"); if (for_local_console) terminal = manager->local_console_terminal; device_path = udev_device_get_devnode (device); if (device_path != NULL) { const char *subsystem; ply_renderer_type_t renderer_type = PLY_RENDERER_TYPE_NONE; subsystem = udev_device_get_subsystem (device); + ply_trace ("device subsystem is %s", subsystem); - if (strcmp (subsystem, SUBSYSTEM_DRM) == 0) + if (subsystem != NULL && strcmp (subsystem, SUBSYSTEM_DRM) == 0) { ply_trace ("found DRM device %s", device_path); renderer_type = PLY_RENDERER_TYPE_DRM; } else if (strcmp (subsystem, SUBSYSTEM_FRAME_BUFFER) == 0) { ply_trace ("found frame buffer device %s", device_path); if (!fb_device_has_drm_device (manager, device)) { renderer_type = PLY_RENDERER_TYPE_FRAME_BUFFER; } else { ply_trace ("ignoring, since there's a DRM device associated with it"); } } if (renderer_type != PLY_RENDERER_TYPE_NONE) create_seat_for_terminal_and_renderer_type (manager, device_path, terminal, renderer_type); } } static void free_seat_from_device_path (ply_device_manager_t *manager, const char *device_path) { ply_list_node_t *node; @@ -332,63 +333,80 @@ create_seats_for_subsystem (ply_device_manager_t *manager, { ply_trace ("it's not initialized"); } udev_device_unref (device); } udev_enumerate_unref (matches); return found_device; } static void on_udev_event (ply_device_manager_t *manager) { struct udev_device *device; const char *action; device = udev_monitor_receive_device (manager->udev_monitor); if (device == NULL) return; action = udev_device_get_action (device); ply_trace ("got %s event for device %s", action, udev_device_get_sysname (device)); if (action == NULL) return; if (strcmp (action, "add") == 0) - create_seat_for_udev_device (manager, device); + { + const char *subsystem; + bool coldplug_complete = manager->udev_queue_fd_watch == NULL; + + subsystem = udev_device_get_subsystem (device); + + if (strcmp (subsystem, SUBSYSTEM_DRM) == 0 || + coldplug_complete) + { + create_seat_for_udev_device (manager, device); + } + else + { + ply_trace ("ignoring since we only handle subsystem %s devices after coldplug completes", subsystem); + } + } else if (strcmp (action, "remove") == 0) - free_seat_for_udev_device (manager, device); + { + free_seat_for_udev_device (manager, device); + } udev_device_unref (device); } static void watch_for_udev_events (ply_device_manager_t *manager) { int fd; assert (manager != NULL); assert (manager->udev_monitor == NULL); ply_trace ("watching for udev graphics device add and remove events"); manager->udev_monitor = udev_monitor_new_from_netlink (manager->udev_context, "udev"); udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_DRM, NULL); udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_FRAME_BUFFER, NULL); udev_monitor_filter_add_match_tag (manager->udev_monitor, "seat"); udev_monitor_enable_receiving (manager->udev_monitor); fd = udev_monitor_get_fd (manager->udev_monitor); ply_event_loop_watch_fd (manager->loop, fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA, (ply_event_handler_t) on_udev_event, NULL, manager); } -- 1.8.3.1