Skip to content

Commit

Permalink
Started listening for udev events
Browse files Browse the repository at this point in the history
The idea (I think), is listen for when there is
an 'add' event for a GPU which we do not already utilise. In this case,
we try to open that device, and add it as a sub-backend to the main drm
backend.

Then, the outputs of this new GPU should become visible to e.g.: sway.

This is the essence of GPU hotplugging I think.

The 'libbacktrace' dependency is only temporary - I have used it for
debugging this code and understanding better how wlroots works...
  • Loading branch information
neon64 committed Oct 7, 2020
1 parent c22fa2f commit dc32ece
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 28 deletions.
80 changes: 56 additions & 24 deletions backend/drm/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
#include <wlr/types/wlr_list.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <execinfo.h>
#include "backend/drm/drm.h"
#include "util/signal.h"
#include <backtrace.h>

struct wlr_drm_backend *get_drm_backend_from_backend(
struct wlr_backend *wlr_backend) {
Expand All @@ -28,23 +28,26 @@ static bool backend_start(struct wlr_backend *backend) {
return true;
}

static void print_trace(void)
{
void *array[10];
char **strings;
int size, i;
static int bt_callback(void *data, uintptr_t pc,
const char *filename, int lineno,
const char *function) {
wlr_log(WLR_DEBUG, "Backtrace: %s in %s:%d", function, filename, lineno);
return 0;
}

size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
if (strings != NULL)
{
static void bt_error(void *data, const char *msg,
int errnum) {
wlr_log(WLR_ERROR, "Backtrace error%s", msg);
}

fprintf (stderr, "Obtained %d stack frames.\n", size);
for (i = 0; i < size; i++)
fprintf (stderr, "%s\n", strings[i]);
}
static void print_trace(void) {
wlr_log(WLR_DEBUG, "Trying to obtain a backtrace");

struct backtrace_state *s = backtrace_create_state(NULL, 0,bt_error, NULL);

wlr_log(WLR_DEBUG, "Tried to obtain a backtrace");

free (strings);
backtrace_full(s, 0, bt_callback, bt_error, NULL);
}

static void backend_destroy(struct wlr_backend *backend) {
Expand Down Expand Up @@ -134,20 +137,49 @@ static void drm_invalidated(struct wl_listener *listener, void *data) {

char *name = drmGetDeviceNameFromFd2(drm->fd);
wlr_log(WLR_DEBUG, "%s invalidated", name);
wlr_log(WLR_INFO, "Should we destroy the DRM backend? %s", name);
if(drm->parent != NULL) {
char *name2 = drmGetDeviceNameFromFd2(drm->parent->fd);
wlr_log(WLR_INFO, "Parent drm backend: %p %s", drm->parent, name2);
free(name2);


size_t seen_len = wl_list_length(&drm->outputs);
wlr_log(WLR_DEBUG, "%ld outputs before scan", seen_len);

scan_drm_connectors(drm);

// if all the drm connectors (outputs?) are disconnected, then we try to
// destroy the whole drm backend so we can unload drivers etc...
struct wlr_drm_connector *c = NULL;
bool all_disconnected = true;
wl_list_for_each(c, &drm->outputs, link) {
if(c->state != WLR_DRM_CONN_DISCONNECTED) {
all_disconnected = false;
}
wlr_log(WLR_INFO, "drm connector state: %s", c->output.name);
wlr_log(WLR_INFO, "drm connector state: %d", c->state);
wlr_log(WLR_INFO, "drm connector output mode: %p", c->desired_mode);
wlr_log(WLR_INFO, "drm connector output mode: %d", c->output.enabled);
}
wlr_log(WLR_INFO, "Trying to anyway... hehe");
// wlr_backend_destroy(conn->output.backend);

seen_len = wl_list_length(&drm->outputs);
wlr_log(WLR_DEBUG, "%ld outputs after scan", seen_len);

// TODO: only destroy backend if we find that all drm_connectors have been lost ?
// i.e.: have unplugged all monitors

wlr_log(WLR_INFO, "Should we destroy the DRM backend? %s - %d", name, all_disconnected);
free(name);

scan_drm_connectors(drm);
if(drm->parent != NULL) {
name = drmGetDeviceNameFromFd2(drm->parent->fd);
wlr_log(WLR_INFO, "Parent drm backend: %p %s", drm->parent, name);
free(name);
}

wlr_backend_destroy(&drm->backend);
print_trace();

if(all_disconnected) {
wlr_log(WLR_INFO, "Destroying DRM backend anyway... hehe");
// this is what we added - to destroy the backend when drm is invalidated...
wlr_backend_destroy(&drm->backend);
}
}

static void handle_session_destroy(struct wl_listener *listener, void *data) {
Expand Down
99 changes: 96 additions & 3 deletions backend/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <ctype.h>
#include "backend/session/session.h"
#include "util/signal.h"

Expand All @@ -32,6 +33,28 @@ static const struct session_impl *impls[] = {
NULL,
};

// determines if the sysname is of the form card[0-9]*
static bool is_card(const char *name) {
wlr_log(WLR_DEBUG, "determining if '%s' is a card", name);

if(strncmp(name, "card", strlen("card")) != 0) {
// doesn't start with card
return false;
}

name = name + strlen("card");
while(*name != '\0') {
if(!isdigit(*name)) {
return false;
}
name++;
}

return true;
}

// NOTE to self: this is an important function - need to look at this to detect e.g.:
// when new graphics cards / drm devices are added to the system
static int udev_event(int fd, uint32_t mask, void *data) {
struct wlr_session *session = data;

Expand All @@ -45,17 +68,54 @@ static int udev_event(int fd, uint32_t mask, void *data) {
wlr_log(WLR_DEBUG, "udev event for %s (%s)",
udev_device_get_sysname(udev_dev), action);

if (!action || strcmp(action, "change") != 0) {
if (!action) {
goto out;
}

dev_t devnum = udev_device_get_devnum(udev_dev);
struct wlr_device *dev;

bool found = false;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_signal_emit_safe(&dev->signal, session);
break;
found = true;
if (strcmp(action, "change") == 0) {
wlr_log(WLR_DEBUG, "found device for event %ld", dev->dev);
wlr_signal_emit_safe(&dev->signal, session);
break;
}
}
}

// there is a drm device which we don't yet know about
// in the `wlr_session_find_gpus` function, they use
// - udev_enumerate_add_match_subsystem(en, "drm")
// - udev_enumerate_add_match_sysname(en, "card[0-9]*")
//
// to restrict to only GPUs
// we attempt to do the same with `udev_device_get_subsystem`
// and `udev_device_get_sysname`
if (!found && strcmp(action, "add") == 0
&& strcmp(udev_device_get_subsystem(udev_dev), "drm") == 0
&& is_card(udev_device_get_sysname(udev_dev))) {
wlr_log(WLR_INFO, "wlroots detected a fresh drm device, trying to add to backend");

int gpu_fd = session_try_add_gpu(session, udev_dev);

if(gpu_fd >= 0) {
wlr_log(WLR_INFO, "got GPU!");
// struct wlr_backend *drm = wlr_drm_backend_create(display, session,
// gpus[i], primary_drm, create_renderer_func);
// if (!drm) {
// wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]);
// continue;
// }
//
// if (!primary_drm) {
// primary_drm = drm;
// }
//
// wlr_multi_backend_add(backend, drm);
}
}

Expand Down Expand Up @@ -368,3 +428,36 @@ size_t wlr_session_find_gpus(struct wlr_session *session,

return i;
}

int session_try_add_gpu(struct wlr_session *session, struct udev_device *udev_dev) {
bool is_boot_vga = false;

const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
if (!seat) {
seat = "seat0";
}
if (session->seat[0] && strcmp(session->seat, seat) != 0) {
return -1;
}

// This is owned by 'udev_dev', so we don't need to free it
struct udev_device *pci =
udev_device_get_parent_with_subsystem_devtype(udev_dev, "pci", NULL);

if (pci) {
const char *id = udev_device_get_sysattr_value(pci, "boot_vga");
if (id && strcmp(id, "1") == 0) {
is_boot_vga = true;
}
}

int fd = open_if_kms(session, udev_device_get_devnode(udev_dev));
if (fd < 0) {
wlr_log(WLR_INFO, "failed to open device %s", udev_device_get_devnode(udev_dev));
return fd;
}

wlr_log(WLR_INFO, "isbootvga? %d", is_boot_vga);

return fd;
}
1 change: 1 addition & 0 deletions include/backend/session/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
struct wlr_session;

void session_init(struct wlr_session *session);
int session_try_add_gpu(struct wlr_session *session, struct udev_device *udev_dev);

#endif
4 changes: 3 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ udev = dependency('libudev')
pixman = dependency('pixman-1')
math = cc.find_library('m')
rt = cc.find_library('rt')
backtrace = cc.find_library('backtrace')

if cc.has_header('EGL/eglmesaext.h', dependencies: egl)
conf_data.set10('WLR_HAS_EGLMESAEXT_H', true)
Expand All @@ -131,6 +132,7 @@ wlr_deps = [
pixman,
math,
rt,
backtrace
]

libinput_ver = libinput.version().split('.')
Expand Down Expand Up @@ -194,4 +196,4 @@ pkgconfig.generate(lib_wlr,
filebase: meson.project_name(),
name: meson.project_name(),
description: 'Wayland compositor library',
)
)

0 comments on commit dc32ece

Please sign in to comment.