v2: Fix the leak of device_name v3: Rebased It enables to use the DRI_PRIME env var to specify which gpu to use. Two syntax are supported: If DRI_PRIME is 1 it means: take any other gpu than the default one. If DRI_PRIME is the ID_PATH_TAG of a device: choose this device if possible.
The ID_PATH_TAG is a tag filled by udev. You can check it with 'udevadm info' on the device node. For example it can be "pci-0000_01_00_0". Render-nodes need to be enabled to choose another gpu, and they need to have the ID_PATH_TAG advertised. It is possible for not very recent udev that the tag is not advertised for render-nodes, then ones need to add a file containing: SUBSYSTEM=="drm", IMPORT{builtin}="path_id" in /etc/udev/rules.d/ Signed-off-by: Axel Davy <axel.d...@ens.fr> --- src/loader/loader.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/loader/loader.h | 7 ++ 2 files changed, 192 insertions(+) diff --git a/src/loader/loader.c b/src/loader/loader.c index 0f26265..19d99d5 100644 --- a/src/loader/loader.c +++ b/src/loader/loader.c @@ -70,6 +70,10 @@ #ifdef HAVE_LIBUDEV #include <assert.h> #include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> #endif #ifdef HAVE_SYSFS #include <sys/stat.h> @@ -214,6 +218,187 @@ out: return (*chip_id >= 0); } + +static char * +get_render_node_from_id_path_tag(struct udev *udev, + char *id_path_tag, + char another_tag) +{ + struct udev_device *device; + struct udev_enumerate *e; + struct udev_list_entry *entry; + const char *path, *id_path_tag_tmp; + char *path_res; + char found = 0; + UDEV_SYMBOL(struct udev_enumerate *, udev_enumerate_new, + (struct udev *)); + UDEV_SYMBOL(int, udev_enumerate_add_match_subsystem, + (struct udev_enumerate *, const char *)); + UDEV_SYMBOL(int, udev_enumerate_add_match_sysname, + (struct udev_enumerate *, const char *)); + UDEV_SYMBOL(int, udev_enumerate_scan_devices, + (struct udev_enumerate *)); + UDEV_SYMBOL(struct udev_list_entry *, udev_enumerate_get_list_entry, + (struct udev_enumerate *)); + UDEV_SYMBOL(struct udev_list_entry *, udev_list_entry_get_next, + (struct udev_list_entry *)); + UDEV_SYMBOL(const char *, udev_list_entry_get_name, + (struct udev_list_entry *)); + UDEV_SYMBOL(struct udev_device *, udev_device_new_from_syspath, + (struct udev *, const char *)); + UDEV_SYMBOL(const char *, udev_device_get_property_value, + (struct udev_device *, const char *)); + UDEV_SYMBOL(const char *, udev_device_get_devnode, + (struct udev_device *)); + UDEV_SYMBOL(struct udev_device *, udev_device_unref, + (struct udev_device *)); + + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "drm"); + udev_enumerate_add_match_sysname(e, "render*"); + + udev_enumerate_scan_devices(e); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + if (!device) + continue; + id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG"); + if (id_path_tag_tmp) { + if ((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp)) || + (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) { + found = 1; + break; + } + } + udev_device_unref(device); + } + + if (found) { + path_res = strdup(udev_device_get_devnode(device)); + udev_device_unref(device); + return path_res; + } + return NULL; +} + +static char * +get_id_path_tag_from_fd(struct udev *udev, int fd) +{ + struct udev_device *device; + const char *id_path_tag_tmp; + char *id_path_tag; + UDEV_SYMBOL(const char *, udev_device_get_property_value, + (struct udev_device *, const char *)); + UDEV_SYMBOL(struct udev_device *, udev_device_unref, + (struct udev_device *)); + + device = udev_device_new_from_fd(udev, fd); + if (!device) + return NULL; + + id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG"); + if (!id_path_tag_tmp) + return NULL; + + id_path_tag = strdup(id_path_tag_tmp); + + udev_device_unref(device); + return id_path_tag; +} + +static int +drm_open_device(const char *device_name) +{ + int fd; +#ifdef O_CLOEXEC + fd = open(device_name, O_RDWR | O_CLOEXEC); + if (fd == -1 && errno == EINVAL) +#endif + { + fd = open(device_name, O_RDWR); + if (fd != -1) + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + } + return fd; +} + +int loader_get_user_preferred_fd(int default_fd, int *different_device) +{ + struct udev *udev; + const char *dri_prime = getenv("DRI_PRIME"); + char *prime = NULL; + int is_different_device = 0, fd = default_fd; + char *default_device_id_path_tag; + char *device_name = NULL; + char another_tag = 0; + UDEV_SYMBOL(struct udev *, udev_new, (void)); + UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *)); + + if (dri_prime) + prime = strdup(dri_prime); + + if (prime == NULL) { + *different_device = 0; + return default_fd; + } + + udev = udev_new(); + if (!udev) + goto prime_clean; + + default_device_id_path_tag = get_id_path_tag_from_fd(udev, default_fd); + if (!default_device_id_path_tag) + goto udev_clean; + + is_different_device = 1; + /* two format are supported: + * "1": choose any other card than the card used by default. + * id_path_tag: (for example "pci-0000_02_00_0") choose the card + * with this id_path_tag. + */ + if (!strcmp(prime,"1")) { + free(prime); + prime = strdup(default_device_id_path_tag); + /* request a card with a different card than the default card */ + another_tag = 1; + } else if (!strcmp(default_device_id_path_tag, prime)) + /* we are to get a new fd (render-node) of the same device */ + is_different_device = 0; + + device_name = get_render_node_from_id_path_tag(udev, + prime, + another_tag); + if (device_name == NULL) { + is_different_device = 0; + goto default_device_clean; + } + + fd = drm_open_device(device_name); + if (fd > 0) { + close(default_fd); + } else { + fd = default_fd; + is_different_device = 0; + } + free(device_name); + + default_device_clean: + free(default_device_id_path_tag); + udev_clean: + udev_unref(udev); + prime_clean: + free(prime); + + *different_device = is_different_device; + return fd; +} +#else +int loader_get_user_preferred_fd(int default_fd, int *different_device) +{ + *different_device = 0; + return default_fd; +} #endif #if defined(HAVE_SYSFS) diff --git a/src/loader/loader.h b/src/loader/loader.h index dfd77ba..fa57950 100644 --- a/src/loader/loader.h +++ b/src/loader/loader.h @@ -41,6 +41,13 @@ loader_get_driver_for_fd(int fd, unsigned driver_types); char * loader_get_device_name_for_fd(int fd); +/* Function to get a different device than the one we are to use by default, + * if the user requests so and it is possible. The initial fd will be closed + * if neccessary. The returned fd is potentially a render-node. + */ + +int +loader_get_user_preferred_fd(int default_fd, int *different_device); /* for logging.. keep this aligned with egllog.h so we can just use * _eglLog directly. -- 1.9.1 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev