1 /*
2 * Copyright (c) 2009-2024 Broadcom. All Rights Reserved.
3 * The term “Broadcom” refers to Broadcom Inc.
4 * and/or its subsidiaries.
5 * SPDX-License-Identifier: MIT
6 */
7
8
9 #include "vmw_screen.h"
10 #include "vmw_fence.h"
11 #include "vmw_context.h"
12 #include "vmwgfx_drm.h"
13 #include "xf86drm.h"
14
15 #include "util/os_file.h"
16 #include "util/u_memory.h"
17 #include "util/compiler.h"
18 #include "util/u_hash_table.h"
19 #ifdef MAJOR_IN_MKDEV
20 #include <sys/mkdev.h>
21 #endif
22 #ifdef MAJOR_IN_SYSMACROS
23 #include <sys/sysmacros.h>
24 #endif
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30
31 static struct hash_table *dev_hash = NULL;
32
vmw_dev_compare(const void * key1,const void * key2)33 static bool vmw_dev_compare(const void *key1, const void *key2)
34 {
35 return (major(*(dev_t *)key1) == major(*(dev_t *)key2) &&
36 minor(*(dev_t *)key1) == minor(*(dev_t *)key2));
37 }
38
vmw_dev_hash(const void * key)39 static uint32_t vmw_dev_hash(const void *key)
40 {
41 return (major(*(dev_t *) key) << 16) | minor(*(dev_t *) key);
42 }
43
44 #ifdef VMX86_STATS
45 /**
46 * Initializes mksstat TLS store.
47 */
48 static void
vmw_winsys_screen_init_mksstat(struct vmw_winsys_screen * vws)49 vmw_winsys_screen_init_mksstat(struct vmw_winsys_screen *vws)
50 {
51 size_t i;
52
53 for (i = 0; i < ARRAY_SIZE(vws->mksstat_tls); ++i) {
54 vws->mksstat_tls[i].stat_pages = NULL;
55 vws->mksstat_tls[i].stat_id = -1UL;
56 vws->mksstat_tls[i].pid = 0;
57 }
58 }
59
60 /**
61 * Deinits mksstat TLS store.
62 */
63 static void
vmw_winsys_screen_deinit_mksstat(struct vmw_winsys_screen * vws)64 vmw_winsys_screen_deinit_mksstat(struct vmw_winsys_screen *vws)
65 {
66 size_t i;
67
68 for (i = 0; i < ARRAY_SIZE(vws->mksstat_tls); ++i) {
69 uint32_t expected = __atomic_load_n(&vws->mksstat_tls[i].pid, __ATOMIC_ACQUIRE);
70
71 if (expected == -1U) {
72 fprintf(stderr, "%s encountered locked mksstat TLS entry at index %lu.\n", __func__, i);
73 continue;
74 }
75
76 if (expected == 0)
77 continue;
78
79 if (__atomic_compare_exchange_n(&vws->mksstat_tls[i].pid, &expected, 0, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
80 struct drm_vmw_mksstat_remove_arg arg = {
81 .id = vws->mksstat_tls[i].stat_id
82 };
83
84 assert(vws->mksstat_tls[i].stat_pages);
85 assert(vws->mksstat_tls[i].stat_id != -1UL);
86
87 if (drmCommandWrite(vws->ioctl.drm_fd, DRM_VMW_MKSSTAT_REMOVE, &arg, sizeof(arg))) {
88 fprintf(stderr, "%s could not ioctl: %s\n", __func__, strerror(errno));
89 } else if (munmap(vws->mksstat_tls[i].stat_pages, vmw_svga_winsys_stats_len())) {
90 fprintf(stderr, "%s could not munmap: %s\n", __func__, strerror(errno));
91 }
92 } else {
93 fprintf(stderr, "%s encountered volatile mksstat TLS entry at index %lu.\n", __func__, i);
94 }
95 }
96 }
97
98 #endif
99 /* Called from vmw_drm_create_screen(), creates and initializes the
100 * vmw_winsys_screen structure, which is the main entity in this
101 * module.
102 * First, check whether a vmw_winsys_screen object already exists for
103 * this device, and in that case return that one, making sure that we
104 * have our own file descriptor open to DRM.
105 */
106
107 struct vmw_winsys_screen *
vmw_winsys_create(int fd)108 vmw_winsys_create( int fd )
109 {
110 struct vmw_winsys_screen *vws;
111 struct stat stat_buf;
112 const char *getenv_val;
113
114 if (dev_hash == NULL) {
115 dev_hash = _mesa_hash_table_create(NULL, vmw_dev_hash, vmw_dev_compare);
116 if (dev_hash == NULL)
117 return NULL;
118 }
119
120 if (fstat(fd, &stat_buf))
121 return NULL;
122
123 vws = util_hash_table_get(dev_hash, &stat_buf.st_rdev);
124 if (vws) {
125 vws->open_count++;
126 return vws;
127 }
128
129 vws = CALLOC_STRUCT(vmw_winsys_screen);
130 if (!vws)
131 goto out_no_vws;
132
133 vws->device = stat_buf.st_rdev;
134 vws->open_count = 1;
135 vws->ioctl.drm_fd = os_dupfd_cloexec(fd);
136 vws->force_coherent = false;
137 if (!vmw_ioctl_init(vws))
138 goto out_no_ioctl;
139
140 vws->base.have_gb_dma = !vws->force_coherent;
141 vws->base.need_to_rebind_resources = false;
142 vws->base.have_transfer_from_buffer_cmd = vws->base.have_vgpu10;
143 vws->base.have_constant_buffer_offset_cmd =
144 vws->ioctl.have_drm_2_20 && vws->base.have_sm5;
145 vws->base.have_index_vertex_buffer_offset_cmd = false;
146 vws->base.have_rasterizer_state_v2_cmd =
147 vws->ioctl.have_drm_2_20 && vws->base.have_sm5;
148
149 getenv_val = getenv("SVGA_FORCE_KERNEL_UNMAPS");
150 vws->cache_maps = !getenv_val || strcmp(getenv_val, "0") == 0;
151 vws->fence_ops = vmw_fence_ops_create(vws);
152 if (!vws->fence_ops)
153 goto out_no_fence_ops;
154
155 if(!vmw_pools_init(vws))
156 goto out_no_pools;
157
158 if (!vmw_winsys_screen_init_svga(vws))
159 goto out_no_svga;
160
161 #ifdef VMX86_STATS
162 vmw_winsys_screen_init_mksstat(vws);
163 #endif
164 _mesa_hash_table_insert(dev_hash, &vws->device, vws);
165
166 cnd_init(&vws->cs_cond);
167 mtx_init(&vws->cs_mutex, mtx_plain);
168
169 return vws;
170 out_no_svga:
171 vmw_pools_cleanup(vws);
172 out_no_pools:
173 vws->fence_ops->destroy(vws->fence_ops);
174 out_no_fence_ops:
175 vmw_ioctl_cleanup(vws);
176 out_no_ioctl:
177 close(vws->ioctl.drm_fd);
178 FREE(vws);
179 out_no_vws:
180 return NULL;
181 }
182
183 void
vmw_winsys_destroy(struct vmw_winsys_screen * vws)184 vmw_winsys_destroy(struct vmw_winsys_screen *vws)
185 {
186 if (--vws->open_count == 0) {
187 _mesa_hash_table_remove_key(dev_hash, &vws->device);
188 vmw_pools_cleanup(vws);
189 vws->fence_ops->destroy(vws->fence_ops);
190 vmw_ioctl_cleanup(vws);
191 #ifdef VMX86_STATS
192 vmw_winsys_screen_deinit_mksstat(vws);
193 #endif
194 close(vws->ioctl.drm_fd);
195 mtx_destroy(&vws->cs_mutex);
196 cnd_destroy(&vws->cs_cond);
197 FREE(vws);
198 }
199 }
200