1 /**************************************************************************
2 *
3 * Copyright (C) 2020 Chromium
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 **************************************************************************/
24
25 #include "virgl_resource.h"
26
27 #include <assert.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "util/os_file.h"
34 #include "util/u_hash_table.h"
35 #include "util/u_pointer.h"
36 #include "virgl_util.h"
37 #include "virgl_context.h"
38
39 static struct util_hash_table *virgl_resource_table;
40 static struct virgl_resource_pipe_callbacks pipe_callbacks;
41
42 static void
virgl_resource_destroy_func(void * val)43 virgl_resource_destroy_func(void *val)
44 {
45 struct virgl_resource *res = (struct virgl_resource *)val;
46
47 if (res->pipe_resource)
48 pipe_callbacks.unref(res->pipe_resource, pipe_callbacks.data);
49 if ((res->fd_type != VIRGL_RESOURCE_FD_INVALID) &&
50 (res->fd_type != VIRGL_RESOURCE_OPAQUE_HANDLE))
51 close(res->fd);
52
53 free(res);
54 }
55
56 int
virgl_resource_table_init(const struct virgl_resource_pipe_callbacks * callbacks)57 virgl_resource_table_init(const struct virgl_resource_pipe_callbacks *callbacks)
58 {
59 virgl_resource_table = util_hash_table_create(hash_func_u32,
60 equal_func,
61 virgl_resource_destroy_func);
62 if (!virgl_resource_table)
63 return ENOMEM;
64
65 if (callbacks)
66 pipe_callbacks = *callbacks;
67
68 return 0;
69 }
70
71 void
virgl_resource_table_cleanup(void)72 virgl_resource_table_cleanup(void)
73 {
74 util_hash_table_destroy(virgl_resource_table);
75 virgl_resource_table = NULL;
76 memset(&pipe_callbacks, 0, sizeof(pipe_callbacks));
77 }
78
79 void
virgl_resource_table_reset(void)80 virgl_resource_table_reset(void)
81 {
82 util_hash_table_clear(virgl_resource_table);
83 }
84
85 static struct virgl_resource *
virgl_resource_create(uint32_t res_id)86 virgl_resource_create(uint32_t res_id)
87 {
88 struct virgl_resource *res;
89 enum pipe_error err;
90
91 res = calloc(1, sizeof(*res));
92 if (!res)
93 return NULL;
94
95 err = util_hash_table_set(virgl_resource_table,
96 uintptr_to_pointer(res_id),
97 res);
98 if (err != PIPE_OK) {
99 free(res);
100 return NULL;
101 }
102
103 res->res_id = res_id;
104 res->fd_type = VIRGL_RESOURCE_FD_INVALID;
105 res->fd = -1;
106
107 return res;
108 }
109
110 struct virgl_resource *
virgl_resource_create_from_pipe(uint32_t res_id,struct pipe_resource * pres,const struct iovec * iov,int iov_count)111 virgl_resource_create_from_pipe(uint32_t res_id,
112 struct pipe_resource *pres,
113 const struct iovec *iov,
114 int iov_count)
115 {
116 struct virgl_resource *res;
117
118 res = virgl_resource_create(res_id);
119 if (!res)
120 return NULL;
121
122 /* take ownership */
123 res->pipe_resource = pres;
124
125 res->iov = iov;
126 res->iov_count = iov_count;
127
128 return res;
129 }
130
131 struct virgl_resource *
virgl_resource_create_from_fd(uint32_t res_id,enum virgl_resource_fd_type fd_type,int fd,const struct iovec * iov,int iov_count,const struct virgl_resource_opaque_fd_metadata * opaque_fd_metadata)132 virgl_resource_create_from_fd(uint32_t res_id,
133 enum virgl_resource_fd_type fd_type,
134 int fd,
135 const struct iovec *iov,
136 int iov_count,
137 const struct virgl_resource_opaque_fd_metadata *opaque_fd_metadata)
138 {
139 struct virgl_resource *res;
140
141 assert(fd_type != VIRGL_RESOURCE_FD_INVALID && fd >= 0);
142
143 res = virgl_resource_create(res_id);
144 if (!res)
145 return NULL;
146
147 res->fd_type = fd_type;
148 /* take ownership */
149 res->fd = fd;
150
151 res->iov = iov;
152 res->iov_count = iov_count;
153
154 if (opaque_fd_metadata && fd_type == VIRGL_RESOURCE_FD_OPAQUE)
155 res->opaque_fd_metadata = *opaque_fd_metadata;
156
157 return res;
158 }
159
160 struct virgl_resource *
virgl_resource_create_from_opaque_handle(struct virgl_context * ctx,uint32_t res_id,uint32_t opaque_handle)161 virgl_resource_create_from_opaque_handle(struct virgl_context *ctx,
162 uint32_t res_id,
163 uint32_t opaque_handle)
164 {
165 struct virgl_resource *res;
166
167 res = virgl_resource_create(res_id);
168 if (!res)
169 return NULL;
170
171 res->fd_type = VIRGL_RESOURCE_OPAQUE_HANDLE;
172 res->opaque_handle = opaque_handle;
173
174 /* We need the ctx to get an fd from handle (which we don't want to do
175 * until asked, to avoid file descriptor limits)
176 *
177 * Shareable resources should not use OPAQUE_HANDLE, to avoid lifetime
178 * issues (ie. resource outliving the context which created it).
179 */
180 res->opaque_handle_context_id = ctx->ctx_id;
181
182 return res;
183 }
184
185 struct virgl_resource *
virgl_resource_create_from_iov(uint32_t res_id,const struct iovec * iov,int iov_count)186 virgl_resource_create_from_iov(uint32_t res_id,
187 const struct iovec *iov,
188 int iov_count)
189 {
190 struct virgl_resource *res;
191
192 if (iov_count)
193 assert(iov);
194
195 res = virgl_resource_create(res_id);
196 if (!res)
197 return NULL;
198
199 res->iov = iov;
200 res->iov_count = iov_count;
201
202 return res;
203 }
204
205 void
virgl_resource_remove(uint32_t res_id)206 virgl_resource_remove(uint32_t res_id)
207 {
208 util_hash_table_remove(virgl_resource_table, uintptr_to_pointer(res_id));
209 }
210
virgl_resource_lookup(uint32_t res_id)211 struct virgl_resource *virgl_resource_lookup(uint32_t res_id)
212 {
213 return util_hash_table_get(virgl_resource_table,
214 uintptr_to_pointer(res_id));
215 }
216
217 int
virgl_resource_attach_iov(struct virgl_resource * res,const struct iovec * iov,int iov_count)218 virgl_resource_attach_iov(struct virgl_resource *res,
219 const struct iovec *iov,
220 int iov_count)
221 {
222 if (res->iov)
223 return EINVAL;
224
225 res->iov = iov;
226 res->iov_count = iov_count;
227
228 if (res->pipe_resource) {
229 pipe_callbacks.attach_iov(res->pipe_resource,
230 iov,
231 iov_count,
232 pipe_callbacks.data);
233 }
234
235 return 0;
236 }
237
238 void
virgl_resource_detach_iov(struct virgl_resource * res)239 virgl_resource_detach_iov(struct virgl_resource *res)
240 {
241 if (!res->iov)
242 return;
243
244 if (res->pipe_resource)
245 pipe_callbacks.detach_iov(res->pipe_resource, pipe_callbacks.data);
246
247 res->iov = NULL;
248 res->iov_count = 0;
249 }
250
251 enum virgl_resource_fd_type
virgl_resource_export_fd(struct virgl_resource * res,int * fd)252 virgl_resource_export_fd(struct virgl_resource *res, int *fd)
253 {
254 if (res->fd_type == VIRGL_RESOURCE_OPAQUE_HANDLE) {
255 struct virgl_context *ctx;
256
257 ctx = virgl_context_lookup(res->opaque_handle_context_id);
258 if (!ctx)
259 return VIRGL_RESOURCE_FD_INVALID;
260
261 return ctx->export_opaque_handle(ctx, res, fd);
262 } else if (res->fd_type != VIRGL_RESOURCE_FD_INVALID) {
263 *fd = os_dupfd_cloexec(res->fd);
264 return *fd >= 0 ? res->fd_type : VIRGL_RESOURCE_FD_INVALID;
265 } else if (res->pipe_resource) {
266 return pipe_callbacks.export_fd(res->pipe_resource,
267 fd,
268 pipe_callbacks.data);
269 }
270
271 return VIRGL_RESOURCE_FD_INVALID;
272 }
273
274 uint64_t
virgl_resource_get_size(struct virgl_resource * res)275 virgl_resource_get_size(struct virgl_resource *res)
276 {
277 if (res->map_size)
278 return res->map_size;
279
280 if (res->pipe_resource)
281 return pipe_callbacks.get_size(res->pipe_resource, pipe_callbacks.data);
282
283 return 0;
284 }
285