diff options
author | Axel Davy <axel.davy@ens.fr> | 2014-06-08 19:42:15 -0400 |
---|---|---|
committer | Dave Airlie <airlied@gmail.com> | 2014-07-01 13:07:30 +1000 |
commit | 7ab925a6aafca106e7682dfc21e7c9351380809e (patch) | |
tree | 01e2e62886c3612222af0bf53a546f5748537922 | |
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 <axel.davy@ens.fr>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-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 @@ | |||
70 | #ifdef HAVE_LIBUDEV | 70 | #ifdef HAVE_LIBUDEV |
71 | #include <assert.h> | 71 | #include <assert.h> |
72 | #include <dlfcn.h> | 72 | #include <dlfcn.h> |
73 | #include <fcntl.h> | ||
74 | #include <unistd.h> | ||
75 | #include <stdlib.h> | ||
76 | #include <errno.h> | ||
73 | #endif | 77 | #endif |
74 | #ifdef HAVE_SYSFS | 78 | #ifdef HAVE_SYSFS |
75 | #include <sys/stat.h> | 79 | #include <sys/stat.h> |
@@ -214,6 +218,187 @@ out: | |||
214 | 218 | ||
215 | return (*chip_id >= 0); | 219 | return (*chip_id >= 0); |
216 | } | 220 | } |
221 | |||
222 | static char * | ||
223 | get_render_node_from_id_path_tag(struct udev *udev, | ||
224 | char *id_path_tag, | ||
225 | char another_tag) | ||
226 | { | ||
227 | struct udev_device *device; | ||
228 | struct udev_enumerate *e; | ||
229 | struct udev_list_entry *entry; | ||
230 | const char *path, *id_path_tag_tmp; | ||
231 | char *path_res; | ||
232 | char found = 0; | ||
233 | UDEV_SYMBOL(struct udev_enumerate *, udev_enumerate_new, | ||
234 | (struct udev *)); | ||
235 | UDEV_SYMBOL(int, udev_enumerate_add_match_subsystem, | ||
236 | (struct udev_enumerate *, const char *)); | ||
237 | UDEV_SYMBOL(int, udev_enumerate_add_match_sysname, | ||
238 | (struct udev_enumerate *, const char *)); | ||
239 | UDEV_SYMBOL(int, udev_enumerate_scan_devices, | ||
240 | (struct udev_enumerate *)); | ||
241 | UDEV_SYMBOL(struct udev_list_entry *, udev_enumerate_get_list_entry, | ||
242 | (struct udev_enumerate *)); | ||
243 | UDEV_SYMBOL(struct udev_list_entry *, udev_list_entry_get_next, | ||
244 | (struct udev_list_entry *)); | ||
245 | UDEV_SYMBOL(const char *, udev_list_entry_get_name, | ||
246 | (struct udev_list_entry *)); | ||
247 | UDEV_SYMBOL(struct udev_device *, udev_device_new_from_syspath, | ||
248 | (struct udev *, const char *)); | ||
249 | UDEV_SYMBOL(const char *, udev_device_get_property_value, | ||
250 | (struct udev_device *, const char *)); | ||
251 | UDEV_SYMBOL(const char *, udev_device_get_devnode, | ||
252 | (struct udev_device *)); | ||
253 | UDEV_SYMBOL(struct udev_device *, udev_device_unref, | ||
254 | (struct udev_device *)); | ||
255 | |||
256 | e = udev_enumerate_new(udev); | ||
257 | udev_enumerate_add_match_subsystem(e, "drm"); | ||
258 | udev_enumerate_add_match_sysname(e, "render*"); | ||
259 | |||
260 | udev_enumerate_scan_devices(e); | ||
261 | udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { | ||
262 | path = udev_list_entry_get_name(entry); | ||
263 | device = udev_device_new_from_syspath(udev, path); | ||
264 | if (!device) | ||
265 | continue; | ||
266 | id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG"); | ||
267 | if (id_path_tag_tmp) { | ||
268 | if ((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp)) || | ||
269 | (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) { | ||
270 | found = 1; | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | udev_device_unref(device); | ||
275 | } | ||
276 | |||
277 | if (found) { | ||
278 | path_res = strdup(udev_device_get_devnode(device)); | ||
279 | udev_device_unref(device); | ||
280 | return path_res; | ||
281 | } | ||
282 | return NULL; | ||
283 | } | ||
284 | |||
285 | static char * | ||
286 | get_id_path_tag_from_fd(struct udev *udev, int fd) | ||
287 | { | ||
288 | struct udev_device *device; | ||
289 | const char *id_path_tag_tmp; | ||
290 | char *id_path_tag; | ||
291 | UDEV_SYMBOL(const char *, udev_device_get_property_value, | ||
292 | (struct udev_device *, const char *)); | ||
293 | UDEV_SYMBOL(struct udev_device *, udev_device_unref, | ||
294 | (struct udev_device *)); | ||
295 | |||
296 | device = udev_device_new_from_fd(udev, fd); | ||
297 | if (!device) | ||
298 | return NULL; | ||
299 | |||
300 | id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG"); | ||
301 | if (!id_path_tag_tmp) | ||
302 | return NULL; | ||
303 | |||
304 | id_path_tag = strdup(id_path_tag_tmp); | ||
305 | |||
306 | udev_device_unref(device); | ||
307 | return id_path_tag; | ||
308 | } | ||
309 | |||
310 | static int | ||
311 | drm_open_device(const char *device_name) | ||
312 | { | ||
313 | int fd; | ||
314 | #ifdef O_CLOEXEC | ||
315 | fd = open(device_name, O_RDWR | O_CLOEXEC); | ||
316 | if (fd == -1 && errno == EINVAL) | ||
317 | #endif | ||
318 | { | ||
319 | fd = open(device_name, O_RDWR); | ||
320 | if (fd != -1) | ||
321 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); | ||
322 | } | ||
323 | return fd; | ||
324 | } | ||
325 | |||
326 | int loader_get_user_preferred_fd(int default_fd, int *different_device) | ||
327 | { | ||
328 | struct udev *udev; | ||
329 | const char *dri_prime = getenv("DRI_PRIME"); | ||
330 | char *prime = NULL; | ||
331 | int is_different_device = 0, fd = default_fd; | ||
332 | char *default_device_id_path_tag; | ||
333 | char *device_name = NULL; | ||
334 | char another_tag = 0; | ||
335 | UDEV_SYMBOL(struct udev *, udev_new, (void)); | ||
336 | UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *)); | ||
337 | |||
338 | if (dri_prime) | ||
339 | prime = strdup(dri_prime); | ||
340 | |||
341 | if (prime == NULL) { | ||
342 | *different_device = 0; | ||
343 | return default_fd; | ||
344 | } | ||
345 | |||
346 | udev = udev_new(); | ||
347 | if (!udev) | ||
348 | goto prime_clean; | ||
349 | |||
350 | default_device_id_path_tag = get_id_path_tag_from_fd(udev, default_fd); | ||
351 | if (!default_device_id_path_tag) | ||
352 | goto udev_clean; | ||
353 | |||
354 | is_different_device = 1; | ||
355 | /* two format are supported: | ||
356 | * "1": choose any other card than the card used by default. | ||
357 | * id_path_tag: (for example "pci-0000_02_00_0") choose the card | ||
358 | * with this id_path_tag. | ||
359 | */ | ||
360 | if (!strcmp(prime,"1")) { | ||
361 | free(prime); | ||
362 | prime = strdup(default_device_id_path_tag); | ||
363 | /* request a card with a different card than the default card */ | ||
364 | another_tag = 1; | ||
365 | } else if (!strcmp(default_device_id_path_tag, prime)) | ||
366 | /* we are to get a new fd (render-node) of the same device */ | ||
367 | is_different_device = 0; | ||
368 | |||
369 | device_name = get_render_node_from_id_path_tag(udev, | ||
370 | prime, | ||
371 | another_tag); | ||
372 | if (device_name == NULL) { | ||
373 | is_different_device = 0; | ||
374 | goto default_device_clean; | ||
375 | } | ||
376 | |||
377 | fd = drm_open_device(device_name); | ||
378 | if (fd > 0) { | ||
379 | close(default_fd); | ||
380 | } else { | ||
381 | fd = default_fd; | ||
382 | is_different_device = 0; | ||
383 | } | ||
384 | free(device_name); | ||
385 | |||
386 | default_device_clean: | ||
387 | free(default_device_id_path_tag); | ||
388 | udev_clean: | ||
389 | udev_unref(udev); | ||
390 | prime_clean: | ||
391 | free(prime); | ||
392 | |||
393 | *different_device = is_different_device; | ||
394 | return fd; | ||
395 | } | ||
396 | #else | ||
397 | int loader_get_user_preferred_fd(int default_fd, int *different_device) | ||
398 | { | ||
399 | *different_device = 0; | ||
400 | return default_fd; | ||
401 | } | ||
217 | #endif | 402 | #endif |
218 | 403 | ||
219 | #if defined(HAVE_SYSFS) | 404 | #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); | |||
41 | char * | 41 | char * |
42 | loader_get_device_name_for_fd(int fd); | 42 | loader_get_device_name_for_fd(int fd); |
43 | 43 | ||
44 | /* Function to get a different device than the one we are to use by default, | ||
45 | * if the user requests so and it is possible. The initial fd will be closed | ||
46 | * if neccessary. The returned fd is potentially a render-node. | ||
47 | */ | ||
48 | |||
49 | int | ||
50 | loader_get_user_preferred_fd(int default_fd, int *different_device); | ||
44 | 51 | ||
45 | /* for logging.. keep this aligned with egllog.h so we can just use | 52 | /* for logging.. keep this aligned with egllog.h so we can just use |
46 | * _eglLog directly. | 53 | * _eglLog directly. |