f7b1e5ec6257fa86538cb83a5c11aa9aa1ee5300
[mesa.git] / src / gallium / winsys / nouveau / drm / nouveau_drm_winsys.c
1 #include <sys/stat.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include "pipe/p_context.h"
5 #include "pipe/p_state.h"
6 #include "util/u_format.h"
7 #include "util/u_memory.h"
8 #include "util/u_inlines.h"
9 #include "util/u_hash_table.h"
10 #include "os/os_thread.h"
11
12 #include "nouveau_drm_public.h"
13
14 #include "nouveau/nouveau_winsys.h"
15 #include "nouveau/nouveau_screen.h"
16
17 #include <nvif/class.h>
18 #include <nvif/cl0080.h>
19
20 static struct util_hash_table *fd_tab = NULL;
21
22 static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP;
23
24 bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
25 {
26 int ret;
27 if (screen->refcount == -1)
28 return true;
29
30 pipe_mutex_lock(nouveau_screen_mutex);
31 ret = --screen->refcount;
32 assert(ret >= 0);
33 if (ret == 0)
34 util_hash_table_remove(fd_tab, intptr_to_pointer(screen->drm->fd));
35 pipe_mutex_unlock(nouveau_screen_mutex);
36 return ret == 0;
37 }
38
39 static unsigned hash_fd(void *key)
40 {
41 int fd = pointer_to_intptr(key);
42 struct stat stat;
43 fstat(fd, &stat);
44
45 return stat.st_dev ^ stat.st_ino ^ stat.st_rdev;
46 }
47
48 static int compare_fd(void *key1, void *key2)
49 {
50 int fd1 = pointer_to_intptr(key1);
51 int fd2 = pointer_to_intptr(key2);
52 struct stat stat1, stat2;
53 fstat(fd1, &stat1);
54 fstat(fd2, &stat2);
55
56 return stat1.st_dev != stat2.st_dev ||
57 stat1.st_ino != stat2.st_ino ||
58 stat1.st_rdev != stat2.st_rdev;
59 }
60
61 PUBLIC struct pipe_screen *
62 nouveau_drm_screen_create(int fd)
63 {
64 struct nouveau_drm *drm = NULL;
65 struct nouveau_device *dev = NULL;
66 struct nouveau_screen *(*init)(struct nouveau_device *);
67 struct nouveau_screen *screen = NULL;
68 int ret, dupfd;
69
70 pipe_mutex_lock(nouveau_screen_mutex);
71 if (!fd_tab) {
72 fd_tab = util_hash_table_create(hash_fd, compare_fd);
73 if (!fd_tab) {
74 pipe_mutex_unlock(nouveau_screen_mutex);
75 return NULL;
76 }
77 }
78
79 screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
80 if (screen) {
81 screen->refcount++;
82 pipe_mutex_unlock(nouveau_screen_mutex);
83 return &screen->base;
84 }
85
86 /* Since the screen re-use is based on the device node and not the fd,
87 * create a copy of the fd to be owned by the device. Otherwise a
88 * scenario could occur where two screens are created, and the first
89 * one is shut down, along with the fd being closed. The second
90 * (identical) screen would now have a reference to the closed fd. We
91 * avoid this by duplicating the original fd. Note that
92 * nouveau_device_wrap does not close the fd in case of a device
93 * creation error.
94 */
95 dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
96
97 ret = nouveau_drm_new(dupfd, &drm);
98 if (ret)
99 goto err;
100
101 ret = nouveau_device_new(&drm->client, NV_DEVICE,
102 &(struct nv_device_v0) {
103 .device = ~0ULL,
104 }, sizeof(struct nv_device_v0), &dev);
105 if (ret)
106 goto err;
107
108 switch (dev->chipset & ~0xf) {
109 case 0x30:
110 case 0x40:
111 case 0x60:
112 init = nv30_screen_create;
113 break;
114 case 0x50:
115 case 0x80:
116 case 0x90:
117 case 0xa0:
118 init = nv50_screen_create;
119 break;
120 case 0xc0:
121 case 0xd0:
122 case 0xe0:
123 case 0xf0:
124 case 0x100:
125 case 0x110:
126 case 0x120:
127 case 0x130:
128 init = nvc0_screen_create;
129 break;
130 default:
131 debug_printf("%s: unknown chipset nv%02x\n", __func__,
132 dev->chipset);
133 goto err;
134 }
135
136 screen = init(dev);
137 if (!screen || !screen->base.context_create)
138 goto err;
139
140 /* Use dupfd in hash table, to avoid errors if the original fd gets
141 * closed by its owner. The hash key needs to live at least as long as
142 * the screen.
143 */
144 util_hash_table_set(fd_tab, intptr_to_pointer(dupfd), screen);
145 screen->refcount = 1;
146 pipe_mutex_unlock(nouveau_screen_mutex);
147 return &screen->base;
148
149 err:
150 if (screen) {
151 screen->base.destroy(&screen->base);
152 } else {
153 nouveau_device_del(&dev);
154 nouveau_drm_del(&drm);
155 close(dupfd);
156 }
157 pipe_mutex_unlock(nouveau_screen_mutex);
158 return NULL;
159 }