• 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/format/u_format.h"
7 #include "util/os_file.h"
8 #include "util/simple_mtx.h"
9 #include "util/u_memory.h"
10 #include "util/u_inlines.h"
11 #include "util/u_hash_table.h"
12 #include "util/u_pointer.h"
13 #include "util/u_thread.h"
14 
15 #include "nouveau_drm_public.h"
16 
17 #include "nouveau/nouveau_winsys.h"
18 #include "nouveau/nouveau_screen.h"
19 
20 #include <nvif/class.h>
21 #include <nvif/cl0080.h>
22 
23 static struct hash_table *fd_tab = NULL;
24 
25 static simple_mtx_t nouveau_screen_mutex = SIMPLE_MTX_INITIALIZER;
26 
nouveau_drm_screen_unref(struct nouveau_screen * screen)27 bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
28 {
29 	int ret;
30 	if (screen->refcount == -1)
31 		return true;
32 
33 	simple_mtx_lock(&nouveau_screen_mutex);
34 	ret = --screen->refcount;
35 	assert(ret >= 0);
36 	if (ret == 0)
37 		_mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(screen->drm->fd));
38 	simple_mtx_unlock(&nouveau_screen_mutex);
39 	return ret == 0;
40 }
41 
42 PUBLIC struct pipe_screen *
nouveau_drm_screen_create(int fd)43 nouveau_drm_screen_create(int fd)
44 {
45 	struct nouveau_drm *drm = NULL;
46 	struct nouveau_device *dev = NULL;
47 	struct nouveau_screen *(*init)(struct nouveau_device *);
48 	struct nouveau_screen *screen = NULL;
49 	int ret, dupfd;
50 
51 	simple_mtx_lock(&nouveau_screen_mutex);
52 	if (!fd_tab) {
53 		fd_tab = util_hash_table_create_fd_keys();
54 		if (!fd_tab) {
55 			simple_mtx_unlock(&nouveau_screen_mutex);
56 			return NULL;
57 		}
58 	}
59 
60 	screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
61 	if (screen) {
62 		screen->refcount++;
63 		simple_mtx_unlock(&nouveau_screen_mutex);
64 		return &screen->base;
65 	}
66 
67 	/* Since the screen re-use is based on the device node and not the fd,
68 	 * create a copy of the fd to be owned by the device. Otherwise a
69 	 * scenario could occur where two screens are created, and the first
70 	 * one is shut down, along with the fd being closed. The second
71 	 * (identical) screen would now have a reference to the closed fd. We
72 	 * avoid this by duplicating the original fd. Note that
73 	 * nouveau_device_wrap does not close the fd in case of a device
74 	 * creation error.
75 	 */
76 	dupfd = os_dupfd_cloexec(fd);
77 
78 	ret = nouveau_drm_new(dupfd, &drm);
79 	if (ret)
80 		goto err;
81 
82 	ret = nouveau_device_new(&drm->client, NV_DEVICE,
83 				 &(struct nv_device_v0) {
84 					.device = ~0ULL,
85 				 }, sizeof(struct nv_device_v0), &dev);
86 	if (ret)
87 		goto err;
88 
89 	switch (dev->chipset & ~0xf) {
90 	case 0x30:
91 	case 0x40:
92 	case 0x60:
93 		init = nv30_screen_create;
94 		break;
95 	case 0x50:
96 	case 0x80:
97 	case 0x90:
98 	case 0xa0:
99 		init = nv50_screen_create;
100 		break;
101 	case 0xc0:
102 	case 0xd0:
103 	case 0xe0:
104 	case 0xf0:
105 	case 0x100:
106 	case 0x110:
107 	case 0x120:
108 	case 0x130:
109 	case 0x140:
110 	case 0x160:
111 	case 0x170:
112 	case 0x190:
113 		init = nvc0_screen_create;
114 		break;
115 	default:
116 		debug_printf("%s: unknown chipset nv%02x\n", __func__,
117 			     dev->chipset);
118 		goto err;
119 	}
120 
121 	screen = init(dev);
122 	if (!screen || !screen->base.context_create)
123 		goto err;
124 
125 	/* Use dupfd in hash table, to avoid errors if the original fd gets
126 	 * closed by its owner. The hash key needs to live at least as long as
127 	 * the screen.
128 	 */
129 	_mesa_hash_table_insert(fd_tab, intptr_to_pointer(dupfd), screen);
130 	screen->refcount = 1;
131 	simple_mtx_unlock(&nouveau_screen_mutex);
132 	return &screen->base;
133 
134 err:
135 	if (screen) {
136 		screen->base.destroy(&screen->base);
137 	} else {
138 		nouveau_device_del(&dev);
139 		nouveau_drm_del(&drm);
140 		close(dupfd);
141 	}
142 	simple_mtx_unlock(&nouveau_screen_mutex);
143 	return NULL;
144 }
145