• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
nouveau_drm_screen_unref(struct nouveau_screen * screen)24 bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
25 {
26 	int ret;
27 	if (screen->refcount == -1)
28 		return true;
29 
30 	mtx_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 	mtx_unlock(&nouveau_screen_mutex);
36 	return ret == 0;
37 }
38 
hash_fd(void * key)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 
compare_fd(void * key1,void * key2)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 *
nouveau_drm_screen_create(int fd)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 	mtx_lock(&nouveau_screen_mutex);
71 	if (!fd_tab) {
72 		fd_tab = util_hash_table_create(hash_fd, compare_fd);
73 		if (!fd_tab) {
74 			mtx_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 		mtx_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 	mtx_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 	mtx_unlock(&nouveau_screen_mutex);
158 	return NULL;
159 }
160