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