xref: /aosp_15_r20/external/virglrenderer/server/render_client.c (revision bbecb9d118dfdb95f99bd754f8fa9be01f189df3)
1 /*
2  * Copyright 2021 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "render_client.h"
7 
8 #include <unistd.h>
9 #include <vulkan/vulkan.h>
10 
11 #include "render_context.h"
12 #include "render_server.h"
13 #include "render_virgl.h"
14 #include "render_worker.h"
15 
16 /* There is a render_context_record for each worker.
17  *
18  * When the client process destroys a context, it closes the connection to the
19  * worker, which leads to worker termination.  It also sends a
20  * RENDER_CLIENT_OP_DESTROY_CONTEXT to us to remove the record.  Because we
21  * are responsible for cleaning up the worker, we don't care if the worker has
22  * terminated or not.  We always kill, reap, and remove the record.
23  */
24 struct render_context_record {
25    uint32_t ctx_id;
26    struct render_worker *worker;
27 
28    struct list_head head;
29 };
30 
31 static struct render_context_record *
render_client_find_record(struct render_client * client,uint32_t ctx_id)32 render_client_find_record(struct render_client *client, uint32_t ctx_id)
33 {
34    list_for_each_entry (struct render_context_record, rec, &client->context_records,
35                         head) {
36       if (rec->ctx_id == ctx_id)
37          return rec;
38    }
39    return NULL;
40 }
41 
42 static void
render_client_detach_all_records(struct render_client * client)43 render_client_detach_all_records(struct render_client *client)
44 {
45    struct render_server *srv = client->server;
46 
47    /* free all render_workers without killing nor reaping */
48    render_worker_jail_detach_workers(srv->worker_jail);
49 
50    list_for_each_entry_safe (struct render_context_record, rec, &client->context_records,
51                              head)
52       free(rec);
53    list_inithead(&client->context_records);
54 }
55 
56 static void
render_client_remove_record(struct render_client * client,struct render_context_record * rec)57 render_client_remove_record(struct render_client *client,
58                             struct render_context_record *rec)
59 {
60    struct render_server *srv = client->server;
61 
62    render_worker_destroy(srv->worker_jail, rec->worker);
63 
64    list_del(&rec->head);
65    free(rec);
66 }
67 
68 static void
render_client_clear_records(struct render_client * client)69 render_client_clear_records(struct render_client *client)
70 {
71    list_for_each_entry_safe (struct render_context_record, rec, &client->context_records,
72                              head)
73       render_client_remove_record(client, rec);
74 }
75 
76 static void
init_context_args(struct render_context_args * ctx_args,uint32_t init_flags,const struct render_client_op_create_context_request * req,int ctx_fd)77 init_context_args(struct render_context_args *ctx_args,
78                   uint32_t init_flags,
79                   const struct render_client_op_create_context_request *req,
80                   int ctx_fd)
81 {
82    *ctx_args = (struct render_context_args){
83       .valid = true,
84       .init_flags = init_flags,
85       .ctx_id = req->ctx_id,
86       .ctx_fd = ctx_fd,
87    };
88 
89    static_assert(sizeof(ctx_args->ctx_name) == sizeof(req->ctx_name), "");
90    memcpy(ctx_args->ctx_name, req->ctx_name, sizeof(req->ctx_name) - 1);
91 }
92 
93 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
94 
95 static int
render_client_worker_thread(void * thread_data)96 render_client_worker_thread(void *thread_data)
97 {
98    const struct render_context_args *ctx_args = thread_data;
99    return render_context_main(ctx_args) ? 0 : -1;
100 }
101 
102 #endif /* ENABLE_RENDER_SERVER_WORKER_THREAD */
103 
104 static bool
render_client_create_context(struct render_client * client,const struct render_client_op_create_context_request * req,int * out_remote_fd)105 render_client_create_context(struct render_client *client,
106                              const struct render_client_op_create_context_request *req,
107                              int *out_remote_fd)
108 {
109    struct render_server *srv = client->server;
110 
111    struct render_context_record *rec = calloc(1, sizeof(*rec));
112    if (!rec)
113       return false;
114 
115    int socket_fds[2];
116    if (!render_socket_pair(socket_fds)) {
117       free(rec);
118       return false;
119    }
120    int ctx_fd = socket_fds[0];
121    int remote_fd = socket_fds[1];
122 
123    struct render_context_args ctx_args;
124    init_context_args(&ctx_args, client->init_flags, req, ctx_fd);
125 
126 #ifdef ENABLE_RENDER_SERVER_WORKER_THREAD
127    rec->worker = render_worker_create(srv->worker_jail, render_client_worker_thread,
128                                       &ctx_args, sizeof(ctx_args));
129    if (rec->worker)
130       ctx_fd = -1; /* ownership transferred */
131 #else
132    rec->worker = render_worker_create(srv->worker_jail, NULL, NULL, 0);
133 #endif
134    if (!rec->worker) {
135       render_log("failed to create a context worker");
136       close(ctx_fd);
137       close(remote_fd);
138       free(rec);
139       return false;
140    }
141 
142    rec->ctx_id = req->ctx_id;
143    list_addtail(&rec->head, &client->context_records);
144 
145    if (!render_worker_is_record(rec->worker)) {
146       /* this is the child process */
147       srv->state = RENDER_SERVER_STATE_SUBPROCESS;
148       *srv->context_args = ctx_args;
149 
150       render_client_detach_all_records(client);
151 
152       /* ctx_fd ownership transferred */
153       assert(srv->context_args->ctx_fd == ctx_fd);
154 
155       close(remote_fd);
156       *out_remote_fd = -1;
157 
158       return true;
159    }
160 
161    /* this is the parent process */
162    if (ctx_fd >= 0)
163       close(ctx_fd);
164    *out_remote_fd = remote_fd;
165 
166    return true;
167 }
168 
169 static bool
render_client_dispatch_destroy_context(struct render_client * client,const union render_client_op_request * req)170 render_client_dispatch_destroy_context(struct render_client *client,
171                                        const union render_client_op_request *req)
172 {
173    const uint32_t ctx_id = req->destroy_context.ctx_id;
174    struct render_context_record *rec = render_client_find_record(client, ctx_id);
175    if (rec)
176       render_client_remove_record(client, rec);
177 
178    return true;
179 }
180 
181 static bool
render_client_dispatch_create_context(struct render_client * client,const union render_client_op_request * req)182 render_client_dispatch_create_context(struct render_client *client,
183                                       const union render_client_op_request *req)
184 {
185    struct render_server *srv = client->server;
186 
187    int remote_fd;
188    bool ok = render_client_create_context(client, &req->create_context, &remote_fd);
189    if (!ok)
190       return false;
191 
192    if (srv->state == RENDER_SERVER_STATE_SUBPROCESS) {
193       assert(remote_fd < 0);
194       return true;
195    }
196 
197    const struct render_client_op_create_context_reply reply = {
198       .ok = ok,
199    };
200    if (!ok)
201       return render_socket_send_reply(&client->socket, &reply, sizeof(reply));
202 
203    ok = render_socket_send_reply_with_fds(&client->socket, &reply, sizeof(reply),
204                                           &remote_fd, 1);
205    close(remote_fd);
206 
207    return ok;
208 }
209 
210 static bool
render_client_dispatch_reset(struct render_client * client,UNUSED const union render_client_op_request * req)211 render_client_dispatch_reset(struct render_client *client,
212                              UNUSED const union render_client_op_request *req)
213 {
214    render_client_clear_records(client);
215    return true;
216 }
217 
218 static bool
render_client_dispatch_init(struct render_client * client,const union render_client_op_request * req)219 render_client_dispatch_init(struct render_client *client,
220                             const union render_client_op_request *req)
221 {
222    client->init_flags = req->init.flags;
223 
224    /* init now to avoid doing it in each worker, but only when tracing is
225     * disabled because perfetto can get confused
226     */
227 #ifndef ENABLE_TRACING
228    render_virgl_init(client->init_flags);
229 #endif
230 
231    /* this makes the Vulkan loader loads ICDs */
232    uint32_t unused_count;
233    vkEnumerateInstanceExtensionProperties(NULL, &unused_count, NULL);
234 
235    return true;
236 }
237 
238 static bool
render_client_dispatch_nop(UNUSED struct render_client * client,UNUSED const union render_client_op_request * req)239 render_client_dispatch_nop(UNUSED struct render_client *client,
240                            UNUSED const union render_client_op_request *req)
241 {
242    return true;
243 }
244 
245 struct render_client_dispatch_entry {
246    size_t expect_size;
247    bool (*dispatch)(struct render_client *client,
248                     const union render_client_op_request *req);
249 };
250 
251 static const struct render_client_dispatch_entry
252    render_client_dispatch_table[RENDER_CLIENT_OP_COUNT] = {
253 #define RENDER_CLIENT_DISPATCH(NAME, name)                                               \
254    [RENDER_CLIENT_OP_##                                                                  \
255       NAME] = { .expect_size = sizeof(struct render_client_op_##name##_request),         \
256                 .dispatch = render_client_dispatch_##name }
257       RENDER_CLIENT_DISPATCH(NOP, nop),
258       RENDER_CLIENT_DISPATCH(INIT, init),
259       RENDER_CLIENT_DISPATCH(RESET, reset),
260       RENDER_CLIENT_DISPATCH(CREATE_CONTEXT, create_context),
261       RENDER_CLIENT_DISPATCH(DESTROY_CONTEXT, destroy_context),
262 #undef RENDER_CLIENT_DISPATCH
263    };
264 
265 bool
render_client_dispatch(struct render_client * client)266 render_client_dispatch(struct render_client *client)
267 {
268    union render_client_op_request req;
269    size_t req_size;
270    if (!render_socket_receive_request(&client->socket, &req, sizeof(req), &req_size))
271       return false;
272 
273    if (req.header.op >= RENDER_CLIENT_OP_COUNT) {
274       render_log("invalid client op %d", req.header.op);
275       return false;
276    }
277 
278    const struct render_client_dispatch_entry *entry =
279       &render_client_dispatch_table[req.header.op];
280    if (entry->expect_size != req_size) {
281       render_log("invalid request size %zu for client op %d", req_size, req.header.op);
282       return false;
283    }
284 
285    if (!entry->dispatch(client, &req))
286       render_log("failed to dispatch client op %d", req.header.op);
287 
288    return true;
289 }
290 
291 void
render_client_destroy(struct render_client * client)292 render_client_destroy(struct render_client *client)
293 {
294    struct render_server *srv = client->server;
295 
296    if (srv->state == RENDER_SERVER_STATE_SUBPROCESS) {
297       assert(list_is_empty(&client->context_records));
298    } else {
299       render_client_clear_records(client);
300 
301       /* see render_client_dispatch_init */
302 #ifndef ENABLE_TRACING
303       render_virgl_fini();
304 #endif
305    }
306 
307    render_socket_fini(&client->socket);
308    free(client);
309 }
310 
311 struct render_client *
render_client_create(struct render_server * srv,int client_fd)312 render_client_create(struct render_server *srv, int client_fd)
313 {
314    struct render_client *client = calloc(1, sizeof(*client));
315 
316    if (!client)
317       return NULL;
318 
319    client->server = srv;
320    render_socket_init(&client->socket, client_fd);
321 
322    list_inithead(&client->context_records);
323 
324    return client;
325 }
326