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