gallium/util: replace pipe_mutex_lock() with mtx_lock()
[mesa.git] / src / gallium / winsys / nouveau / drm / nouveau_drm_winsys.c
index 7e88eae34450fea00df65371809e4315cce56c8e..a2a9fd630f3ac7a49425b9fb5152ed63c3e577c7 100644 (file)
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include "pipe/p_context.h"
 #include "pipe/p_state.h"
 #include "util/u_format.h"
 #include "util/u_memory.h"
 #include "util/u_inlines.h"
+#include "util/u_hash_table.h"
+#include "os/os_thread.h"
 
 #include "nouveau_drm_public.h"
 
 #include "nouveau/nouveau_winsys.h"
 #include "nouveau/nouveau_screen.h"
 
-struct pipe_screen *
+#include <nvif/class.h>
+#include <nvif/cl0080.h>
+
+static struct util_hash_table *fd_tab = NULL;
+
+static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP;
+
+bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
+{
+       int ret;
+       if (screen->refcount == -1)
+               return true;
+
+       mtx_lock(&nouveau_screen_mutex);
+       ret = --screen->refcount;
+       assert(ret >= 0);
+       if (ret == 0)
+               util_hash_table_remove(fd_tab, intptr_to_pointer(screen->drm->fd));
+       pipe_mutex_unlock(nouveau_screen_mutex);
+       return ret == 0;
+}
+
+static unsigned hash_fd(void *key)
+{
+    int fd = pointer_to_intptr(key);
+    struct stat stat;
+    fstat(fd, &stat);
+
+    return stat.st_dev ^ stat.st_ino ^ stat.st_rdev;
+}
+
+static int compare_fd(void *key1, void *key2)
+{
+    int fd1 = pointer_to_intptr(key1);
+    int fd2 = pointer_to_intptr(key2);
+    struct stat stat1, stat2;
+    fstat(fd1, &stat1);
+    fstat(fd2, &stat2);
+
+    return stat1.st_dev != stat2.st_dev ||
+           stat1.st_ino != stat2.st_ino ||
+           stat1.st_rdev != stat2.st_rdev;
+}
+
+PUBLIC struct pipe_screen *
 nouveau_drm_screen_create(int fd)
 {
+       struct nouveau_drm *drm = NULL;
        struct nouveau_device *dev = NULL;
-       struct pipe_screen *(*init)(struct nouveau_device *);
-       int ret;
+       struct nouveau_screen *(*init)(struct nouveau_device *);
+       struct nouveau_screen *screen = NULL;
+       int ret, dupfd;
+
+       mtx_lock(&nouveau_screen_mutex);
+       if (!fd_tab) {
+               fd_tab = util_hash_table_create(hash_fd, compare_fd);
+               if (!fd_tab) {
+                       pipe_mutex_unlock(nouveau_screen_mutex);
+                       return NULL;
+               }
+       }
+
+       screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
+       if (screen) {
+               screen->refcount++;
+               pipe_mutex_unlock(nouveau_screen_mutex);
+               return &screen->base;
+       }
 
-       ret = nouveau_device_wrap(fd, 0, &dev);
+       /* Since the screen re-use is based on the device node and not the fd,
+        * create a copy of the fd to be owned by the device. Otherwise a
+        * scenario could occur where two screens are created, and the first
+        * one is shut down, along with the fd being closed. The second
+        * (identical) screen would now have a reference to the closed fd. We
+        * avoid this by duplicating the original fd. Note that
+        * nouveau_device_wrap does not close the fd in case of a device
+        * creation error.
+        */
+       dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+
+       ret = nouveau_drm_new(dupfd, &drm);
+       if (ret)
+               goto err;
+
+       ret = nouveau_device_new(&drm->client, NV_DEVICE,
+                                &(struct nv_device_v0) {
+                                       .device = ~0ULL,
+                                }, sizeof(struct nv_device_v0), &dev);
        if (ret)
-               return NULL;
+               goto err;
 
-       switch (dev->chipset & 0xf0) {
+       switch (dev->chipset & ~0xf) {
        case 0x30:
        case 0x40:
        case 0x60:
@@ -36,13 +121,39 @@ nouveau_drm_screen_create(int fd)
        case 0xd0:
        case 0xe0:
        case 0xf0:
+       case 0x100:
+       case 0x110:
+       case 0x120:
+       case 0x130:
                init = nvc0_screen_create;
                break;
        default:
                debug_printf("%s: unknown chipset nv%02x\n", __func__,
                             dev->chipset);
-               return NULL;
+               goto err;
        }
 
-       return init(dev);
+       screen = init(dev);
+       if (!screen || !screen->base.context_create)
+               goto err;
+
+       /* Use dupfd in hash table, to avoid errors if the original fd gets
+        * closed by its owner. The hash key needs to live at least as long as
+        * the screen.
+        */
+       util_hash_table_set(fd_tab, intptr_to_pointer(dupfd), screen);
+       screen->refcount = 1;
+       pipe_mutex_unlock(nouveau_screen_mutex);
+       return &screen->base;
+
+err:
+       if (screen) {
+               screen->base.destroy(&screen->base);
+       } else {
+               nouveau_device_del(&dev);
+               nouveau_drm_del(&drm);
+               close(dupfd);
+       }
+       pipe_mutex_unlock(nouveau_screen_mutex);
+       return NULL;
 }