diff options
author | Axel Davy <[email protected]> | 2014-06-08 19:42:15 -0400 |
---|---|---|
committer | Dave Airlie <[email protected]> | 2014-07-01 13:07:30 +1000 |
commit | 7ab925a6aafca106e7682dfc21e7c9351380809e (patch) | |
tree | 01e2e62886c3612222af0bf53a546f5748537922 /src/loader | |
parent | da3a47d6824b63621a7d865112558a6e8026688b (diff) |
loader: add gpu selection code via DRI_PRIME.
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 <[email protected]>
Reviewed-by: Emil Velikov <[email protected]>
Signed-off-by: Dave Airlie <[email protected]>
Diffstat (limited to 'src/loader')
-rw-r--r-- | src/loader/loader.c | 185 | ||||
-rw-r--r-- | src/loader/loader.h | 7 |
2 files changed, 192 insertions, 0 deletions
diff --git a/src/loader/loader.c b/src/loader/loader.c index 0f262653b29..19d99d52ef2 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 dfd77baad0e..fa57950de2c 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. |