summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAxel Davy <axel.davy@ens.fr>2014-06-08 19:42:15 -0400
committerDave Airlie <airlied@gmail.com>2014-07-01 13:07:30 +1000
commit7ab925a6aafca106e7682dfc21e7c9351380809e (patch)
tree01e2e62886c3612222af0bf53a546f5748537922
parentda3a47d6824b63621a7d865112558a6e8026688b (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.c185
-rw-r--r--src/loader/loader.h7
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
222static char *
223get_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
285static char *
286get_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
310static int
311drm_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
326int 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
397int 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);
41char * 41char *
42loader_get_device_name_for_fd(int fd); 42loader_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
49int
50loader_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.