xref: /aosp_15_r20/external/mesa3d/src/gfxstream/guest/connection-manager/VirtioGpuPipeStream.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2018 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "VirtioGpuPipeStream.h"
7 
8 #include <errno.h>
9 #include <sys/mman.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 
13 #include <cstring>
14 #include <string>
15 
16 #include "VirtGpu.h"
17 #include "util/log.h"
18 
19 static const size_t kTransferBufferSize = (1048576);
20 
21 static const size_t kReadSize = 512 * 1024;
22 static const size_t kWriteOffset = kReadSize;
23 
VirtioGpuPipeStream(size_t bufSize,int32_t descriptor)24 VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize, int32_t descriptor)
25     : IOStream(bufSize),
26       m_fd(descriptor),
27       m_virtio_mapped(nullptr),
28       m_bufsize(bufSize),
29       m_buf(nullptr),
30       m_writtenPos(0) {}
31 
~VirtioGpuPipeStream()32 VirtioGpuPipeStream::~VirtioGpuPipeStream() { free(m_buf); }
33 
valid()34 bool VirtioGpuPipeStream::valid() { return m_device != nullptr; }
35 
getRendernodeFd()36 int VirtioGpuPipeStream::getRendernodeFd() {
37     if (m_device == nullptr) {
38         return -1;
39     }
40     return m_device->getDeviceHandle();
41 }
42 
connect(const char * serviceName)43 int VirtioGpuPipeStream::connect(const char* serviceName) {
44     if (!m_device) {
45         m_device.reset(createPlatformVirtGpuDevice(kCapsetNone, m_fd));
46         if (!m_device) {
47             mesa_loge("Failed to create VirtioGpuPipeStream VirtGpuDevice.");
48             return -1;
49         }
50 
51         m_resource = m_device->createResource(/*width=*/kTransferBufferSize,
52                                               /*height=*/1,
53                                               /*stride=*/kTransferBufferSize,
54                                               /*size=*/kTransferBufferSize, VIRGL_FORMAT_R8_UNORM,
55                                               PIPE_BUFFER, VIRGL_BIND_CUSTOM);
56         if (!m_resource) {
57             mesa_loge("Failed to create VirtioGpuPipeStream resource.");
58             return -1;
59         }
60 
61         m_resourceMapping = m_resource->createMapping();
62         if (!m_resourceMapping) {
63             mesa_loge("Failed to create VirtioGpuPipeStream resource mapping.");
64             return -1;
65         }
66 
67         m_virtio_mapped = m_resourceMapping->asRawPtr();
68         if (!m_virtio_mapped) {
69             mesa_loge("Failed to create VirtioGpuPipeStream resource mapping ptr.");
70             return -1;
71         }
72     }
73 
74     wait();
75 
76     if (serviceName) {
77         writeFully(serviceName, strlen(serviceName) + 1);
78     } else {
79         static const char kPipeString[] = "pipe:opengles";
80         std::string pipeStr(kPipeString);
81         writeFully(kPipeString, sizeof(kPipeString));
82     }
83 
84     return 0;
85 }
86 
processPipeInit()87 uint64_t VirtioGpuPipeStream::processPipeInit() {
88     connect("pipe:GLProcessPipe");
89     int32_t confirmInt = 100;
90     writeFully(&confirmInt, sizeof(confirmInt));
91     uint64_t res;
92     readFully(&res, sizeof(res));
93     return res;
94 }
95 
allocBuffer(size_t minSize)96 void* VirtioGpuPipeStream::allocBuffer(size_t minSize) {
97     size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
98     if (!m_buf) {
99         m_buf = (unsigned char*)malloc(allocSize);
100     } else if (m_bufsize < allocSize) {
101         unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
102         if (p != NULL) {
103             m_buf = p;
104             m_bufsize = allocSize;
105         } else {
106             mesa_loge("realloc (%zu) failed\n", allocSize);
107             free(m_buf);
108             m_buf = NULL;
109             m_bufsize = 0;
110         }
111     }
112 
113     return m_buf;
114 }
115 
commitBuffer(size_t size)116 int VirtioGpuPipeStream::commitBuffer(size_t size) {
117     if (size == 0) return 0;
118     return writeFully(m_buf, size);
119 }
120 
writeFully(const void * buf,size_t len)121 int VirtioGpuPipeStream::writeFully(const void* buf, size_t len) {
122     // DBG(">> VirtioGpuPipeStream::writeFully %d\n", len);
123     if (!valid()) return -1;
124     if (!buf) {
125         if (len > 0) {
126             // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
127             // in a corrupted state, which is lethal for the emulator.
128             mesa_loge(
129                 "VirtioGpuPipeStream::writeFully failed, buf=NULL, len %zu,"
130                 " lethal error, exiting",
131                 len);
132             abort();
133         }
134         return 0;
135     }
136 
137     size_t res = len;
138     int retval = 0;
139 
140     while (res > 0) {
141         ssize_t stat = transferToHost((const char*)(buf) + (len - res), res);
142         if (stat > 0) {
143             res -= stat;
144             continue;
145         }
146         if (stat == 0) { /* EOF */
147             mesa_loge("VirtioGpuPipeStream::writeFully failed: premature EOF\n");
148             retval = -1;
149             break;
150         }
151         if (errno == EAGAIN) {
152             continue;
153         }
154         retval = stat;
155         mesa_loge("VirtioGpuPipeStream::writeFully failed: %s, lethal error, exiting.\n",
156                   strerror(errno));
157         abort();
158     }
159     // DBG("<< VirtioGpuPipeStream::writeFully %d\n", len );
160     return retval;
161 }
162 
readFully(void * buf,size_t len)163 const unsigned char* VirtioGpuPipeStream::readFully(void* buf, size_t len) {
164     flush();
165 
166     if (!valid()) return NULL;
167     if (!buf) {
168         if (len > 0) {
169             // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
170             // in a corrupted state, which is lethal for the emulator.
171             mesa_loge(
172                 "VirtioGpuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
173                 " error, exiting.",
174                 len);
175             abort();
176         }
177     }
178 
179     size_t res = len;
180     while (res > 0) {
181         ssize_t stat = transferFromHost((char*)(buf) + len - res, res);
182         if (stat == 0) {
183             // client shutdown;
184             return NULL;
185         } else if (stat < 0) {
186             if (errno == EAGAIN) {
187                 continue;
188             } else {
189                 mesa_loge(
190                     "VirtioGpuPipeStream::readFully failed (buf %p, len %zu"
191                     ", res %zu): %s, lethal error, exiting.",
192                     buf, len, res, strerror(errno));
193                 abort();
194             }
195         } else {
196             res -= stat;
197         }
198     }
199     // DBG("<< VirtioGpuPipeStream::readFully %d\n", len);
200     return (const unsigned char*)buf;
201 }
202 
commitBufferAndReadFully(size_t writeSize,void * userReadBufPtr,size_t totalReadSize)203 const unsigned char* VirtioGpuPipeStream::commitBufferAndReadFully(size_t writeSize,
204                                                                    void* userReadBufPtr,
205                                                                    size_t totalReadSize) {
206     return commitBuffer(writeSize) ? nullptr : readFully(userReadBufPtr, totalReadSize);
207 }
208 
read(void * buf,size_t * inout_len)209 const unsigned char* VirtioGpuPipeStream::read(void* buf, size_t* inout_len) {
210     // DBG(">> VirtioGpuPipeStream::read %d\n", *inout_len);
211     if (!valid()) return NULL;
212     if (!buf) {
213         mesa_loge("VirtioGpuPipeStream::read failed, buf=NULL");
214         return NULL;  // do not allow NULL buf in that implementation
215     }
216 
217     int n = recv(buf, *inout_len);
218 
219     if (n > 0) {
220         *inout_len = n;
221         return (const unsigned char*)buf;
222     }
223 
224     // DBG("<< VirtioGpuPipeStream::read %d\n", *inout_len);
225     return NULL;
226 }
227 
recv(void * buf,size_t len)228 int VirtioGpuPipeStream::recv(void* buf, size_t len) {
229     if (!valid()) return -EINVAL;
230     char* p = (char*)buf;
231     int ret = 0;
232     while (len > 0) {
233         int res = transferFromHost(p, len);
234         if (res > 0) {
235             p += res;
236             ret += res;
237             len -= res;
238             continue;
239         }
240         if (res == 0) { /* EOF */
241             break;
242         }
243         if (errno != EAGAIN) {
244             continue;
245         }
246 
247         /* A real error */
248         if (ret == 0) ret = -1;
249         break;
250     }
251     return ret;
252 }
253 
wait()254 void VirtioGpuPipeStream::wait() {
255     int ret = m_resource->wait();
256     if (ret) {
257         mesa_loge("VirtioGpuPipeStream: DRM_IOCTL_VIRTGPU_WAIT failed with %d (%s)\n", errno,
258                   strerror(errno));
259     }
260 
261     m_writtenPos = 0;
262 }
263 
transferToHost(const void * buffer,size_t len)264 ssize_t VirtioGpuPipeStream::transferToHost(const void* buffer, size_t len) {
265     size_t todo = len;
266     size_t done = 0;
267     int ret = EAGAIN;
268 
269     unsigned char* virtioPtr = m_virtio_mapped;
270 
271     const unsigned char* readPtr = reinterpret_cast<const unsigned char*>(buffer);
272 
273     while (done < len) {
274         size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
275 
276         if (toXfer > (kTransferBufferSize - m_writtenPos)) {
277             wait();
278         }
279 
280         memcpy(virtioPtr + m_writtenPos, readPtr, toXfer);
281 
282         ret = m_resource->transferToHost(m_writtenPos, toXfer);
283         if (ret) {
284             mesa_loge("VirtioGpuPipeStream: failed to transferToHost() with errno %d (%s)\n", errno,
285                       strerror(errno));
286             return (ssize_t)ret;
287         }
288 
289         done += toXfer;
290         readPtr += toXfer;
291         todo -= toXfer;
292         m_writtenPos += toXfer;
293     }
294 
295     return len;
296 }
297 
transferFromHost(void * buffer,size_t len)298 ssize_t VirtioGpuPipeStream::transferFromHost(void* buffer, size_t len) {
299     size_t todo = len;
300     size_t done = 0;
301     int ret = EAGAIN;
302 
303     const unsigned char* virtioPtr = m_virtio_mapped;
304     unsigned char* readPtr = reinterpret_cast<unsigned char*>(buffer);
305 
306     if (m_writtenPos) {
307         wait();
308     }
309 
310     while (done < len) {
311         size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
312 
313         ret = m_resource->transferFromHost(0, toXfer);
314         if (ret) {
315             mesa_loge("VirtioGpuPipeStream: failed to transferFromHost() with errno %d (%s)\n",
316                       errno, strerror(errno));
317             return (ssize_t)ret;
318         }
319 
320         wait();
321 
322         memcpy(readPtr, virtioPtr, toXfer);
323 
324         done += toXfer;
325         readPtr += toXfer;
326         todo -= toXfer;
327     }
328 
329     return len;
330 }
331