Add exception handle for multithread case.
[platform/upstream/mesa.git] / src / egl / main / egldevice.c
1 /**************************************************************************
2  *
3  * Copyright 2015, 2018 Collabora
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27
28 #ifdef HAVE_LIBDRM
29 #include <xf86drm.h>
30 #endif
31 #include "util/compiler.h"
32 #include "util/macros.h"
33
34 #include "eglcurrent.h"
35 #include "egldevice.h"
36 #include "egllog.h"
37 #include "eglglobals.h"
38 #include "egltypedefs.h"
39
40
41 struct _egl_device {
42    _EGLDevice *Next;
43
44    const char *extensions;
45
46    EGLBoolean MESA_device_software;
47    EGLBoolean EXT_device_drm;
48    EGLBoolean EXT_device_drm_render_node;
49
50 #ifdef HAVE_LIBDRM
51    drmDevicePtr device;
52 #endif
53 };
54
55 void
56 _eglFiniDevice(void)
57 {
58    _EGLDevice *dev_list, *dev;
59
60    /* atexit function is called with global mutex locked */
61
62    dev_list = _eglGlobal.DeviceList;
63
64    /* The first device is static allocated SW device */
65    assert(dev_list);
66    assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
67    dev_list = dev_list->Next;
68
69    while (dev_list) {
70       /* pop list head */
71       dev = dev_list;
72       dev_list = dev_list->Next;
73
74 #ifdef HAVE_LIBDRM
75       assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
76       drmFreeDevice(&dev->device);
77 #endif
78       free(dev);
79    }
80
81    _eglGlobal.DeviceList = NULL;
82 }
83
84 EGLBoolean
85 _eglCheckDeviceHandle(EGLDeviceEXT device)
86 {
87    _EGLDevice *cur;
88
89    simple_mtx_lock(_eglGlobal.Mutex);
90    cur = _eglGlobal.DeviceList;
91    while (cur) {
92       if (cur == (_EGLDevice *) device)
93          break;
94       cur = cur->Next;
95    }
96    simple_mtx_unlock(_eglGlobal.Mutex);
97    return (cur != NULL);
98 }
99
100 _EGLDevice _eglSoftwareDevice = {
101    /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */
102    .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node",
103    .MESA_device_software = EGL_TRUE,
104    .EXT_device_drm_render_node = EGL_TRUE,
105 };
106
107 #ifdef HAVE_LIBDRM
108 /*
109  * Negative value on error, zero if newly added, one if already in list.
110  */
111 static int
112 _eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev)
113 {
114    _EGLDevice *dev;
115
116    if ((device->available_nodes & (1 << DRM_NODE_PRIMARY |
117                                    1 << DRM_NODE_RENDER)) == 0)
118       return -1;
119
120    dev = _eglGlobal.DeviceList;
121
122    /* The first device is always software */
123    assert(dev);
124    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
125
126    while (dev->Next) {
127       dev = dev->Next;
128
129       assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
130       if (drmDevicesEqual(device, dev->device) != 0) {
131          if (out_dev)
132             *out_dev = dev;
133          return 1;
134       }
135    }
136
137    dev->Next = calloc(1, sizeof(_EGLDevice));
138    if (!dev->Next) {
139       if (out_dev)
140          *out_dev = NULL;
141       return -1;
142    }
143
144    dev = dev->Next;
145    dev->extensions = "EGL_EXT_device_drm";
146    dev->EXT_device_drm = EGL_TRUE;
147    dev->device = device;
148
149    /* TODO: EGL_EXT_device_drm_render_node support for kmsro + renderonly */
150    if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
151       dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node";
152       dev->EXT_device_drm_render_node = EGL_TRUE;
153    }
154
155    if (out_dev)
156       *out_dev = dev;
157
158    return 0;
159 }
160 #endif
161
162 /* Adds a device in DeviceList, if needed for the given fd.
163  *
164  * If a software device, the fd is ignored.
165  */
166 _EGLDevice *
167 _eglAddDevice(int fd, bool software)
168 {
169    _EGLDevice *dev;
170
171    simple_mtx_lock(_eglGlobal.Mutex);
172    dev = _eglGlobal.DeviceList;
173
174    /*This exception only happen in multithread case, when _eglAtExit is   *
175     *called and _eglFiniDevice(where _eglGlobal.DeviceList is set to null)* 
176     *is called in main thread.                                            */
177    if (dev == NULL) {
178        _eglLog(_EGL_FATAL, "_eglGlobal.DeviceList is freed");
179        goto out;
180    }
181
182    /* The first device is always software */
183    assert(dev);
184    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
185    if (software)
186       goto out;
187
188 #ifdef HAVE_LIBDRM
189    drmDevicePtr device;
190
191    if (drmGetDevice2(fd, 0, &device) != 0) {
192       dev = NULL;
193       goto out;
194    }
195
196    /* Device is not added - error or already present */
197    if (_eglAddDRMDevice(device, &dev) != 0)
198       drmFreeDevice(&device);
199 #else
200    _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device");
201    dev = NULL;
202 #endif
203
204 out:
205    simple_mtx_unlock(_eglGlobal.Mutex);
206    return dev;
207 }
208
209 EGLBoolean
210 _eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
211 {
212    switch (ext) {
213    case _EGL_DEVICE_SOFTWARE:
214       return dev->MESA_device_software;
215    case _EGL_DEVICE_DRM:
216       return dev->EXT_device_drm;
217    case _EGL_DEVICE_DRM_RENDER_NODE:
218       return dev->EXT_device_drm_render_node;
219    default:
220       assert(0);
221       return EGL_FALSE;
222    };
223 }
224
225 /* Ideally we'll have an extension which passes the render node,
226  * instead of the card one + magic.
227  *
228  * Then we can move this in _eglQueryDeviceStringEXT below. Until then
229  * keep it separate.
230  */
231 const char *
232 _eglGetDRMDeviceRenderNode(_EGLDevice *dev)
233 {
234 #ifdef HAVE_LIBDRM
235    return dev->device->nodes[DRM_NODE_RENDER];
236 #else
237    return NULL;
238 #endif
239 }
240
241 EGLBoolean
242 _eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute,
243                          EGLAttrib *value)
244 {
245    switch (attribute) {
246    default:
247       _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT");
248       return EGL_FALSE;
249    }
250 }
251
252 const char *
253 _eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
254 {
255    switch (name) {
256    case EGL_EXTENSIONS:
257       return dev->extensions;
258    case EGL_DRM_DEVICE_FILE_EXT:
259       if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
260          break;
261 #ifdef HAVE_LIBDRM
262       return dev->device->nodes[DRM_NODE_PRIMARY];
263 #else
264       /* This should never happen: we don't yet support EGL_DEVICE_DRM for the
265        * software device, and physical devices are only exposed when libdrm is
266        * available. */
267       assert(0);
268       break;
269 #endif
270    case EGL_DRM_RENDER_NODE_FILE_EXT:
271       if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE))
272          break;
273 #ifdef HAVE_LIBDRM
274       return dev->device ? dev->device->nodes[DRM_NODE_RENDER] : NULL;
275 #else
276       /* Physical devices are only exposed when libdrm is available. */
277       assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
278       return NULL;
279 #endif
280    }
281    _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
282    return NULL;
283 }
284
285 /* Do a fresh lookup for devices.
286  *
287  * Walks through the DeviceList, discarding no longer available ones
288  * and adding new ones as applicable.
289  *
290  * Must be called with the global lock held.
291  */
292 static int
293 _eglRefreshDeviceList(void)
294 {
295    ASSERTED _EGLDevice *dev;
296    int count = 0;
297
298    dev = _eglGlobal.DeviceList;
299
300    /* The first device is always software */
301    assert(dev);
302    assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
303    count++;
304
305 #ifdef HAVE_LIBDRM
306    drmDevicePtr devices[64];
307    int num_devs, ret;
308
309    num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
310    for (int i = 0; i < num_devs; i++) {
311       if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER)))
312          continue;
313
314       ret = _eglAddDRMDevice(devices[i], NULL);
315
316       /* Device is not added - error or already present */
317       if (ret != 0)
318          drmFreeDevice(&devices[i]);
319
320       if (ret >= 0)
321          count++;
322    }
323 #endif
324
325    return count;
326 }
327
328 EGLBoolean
329 _eglQueryDevicesEXT(EGLint max_devices,
330                     _EGLDevice **devices,
331                     EGLint *num_devices)
332 {
333    _EGLDevice *dev, *devs;
334    int i = 0, num_devs;
335
336    if ((devices && max_devices <= 0) || !num_devices)
337       return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
338
339    simple_mtx_lock(_eglGlobal.Mutex);
340
341    num_devs = _eglRefreshDeviceList();
342    devs = _eglGlobal.DeviceList;
343
344    /* bail early if we only care about the count */
345    if (!devices) {
346       *num_devices = num_devs;
347       goto out;
348    }
349
350    /* Push the first device (the software one) to the end of the list.
351     * Sending it to the user only if they've requested the full list.
352     *
353     * By default, the user is likely to pick the first device so having the
354     * software (aka least performant) one is not a good idea.
355     */
356    *num_devices = MIN2(num_devs, max_devices);
357
358    for (i = 0, dev = devs->Next; dev && i < max_devices; i++) {
359       devices[i] = dev;
360       dev = dev->Next;
361    }
362
363    /* User requested the full device list, add the sofware device. */
364    if (max_devices >= num_devs) {
365       assert(_eglDeviceSupports(devs, _EGL_DEVICE_SOFTWARE));
366       devices[num_devs - 1] = devs;
367    }
368
369 out:
370    simple_mtx_unlock(_eglGlobal.Mutex);
371
372    return EGL_TRUE;
373 }