xref: /aosp_15_r20/external/mesa3d/src/gfxstream/guest/connection-manager/QemuPipeStream.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2011 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 #include "QemuPipeStream.h"
6 
7 #include <errno.h>
8 #include <qemu_pipe_bp.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 
14 #include "util/log.h"
15 
16 static const size_t kReadSize = 512 * 1024;
17 static const size_t kWriteOffset = kReadSize;
18 
QemuPipeStream(size_t bufSize)19 QemuPipeStream::QemuPipeStream(size_t bufSize)
20     : IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
21 
QemuPipeStream(QEMU_PIPE_HANDLE sock,size_t bufSize)22 QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize)
23     : IOStream(bufSize), m_sock(sock), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
24 
~QemuPipeStream()25 QemuPipeStream::~QemuPipeStream() {
26     if (valid()) {
27         flush();
28         qemu_pipe_close((QEMU_PIPE_HANDLE)m_sock);
29     }
30     if (m_buf != NULL) {
31         free(m_buf);
32     }
33 }
34 
connect(const char * serviceName)35 int QemuPipeStream::connect(const char* serviceName) {
36     m_sock = (int)qemu_pipe_open("opengles");
37     if (!valid()) {
38         mesa_loge("%s: failed to connect to opengles pipe", __FUNCTION__);
39         qemu_pipe_print_error(m_sock);
40         return -1;
41     }
42     return 0;
43 }
44 
processPipeInit()45 uint64_t QemuPipeStream::processPipeInit() {
46     QEMU_PIPE_HANDLE processPipe = qemu_pipe_open("GLProcessPipe");
47 
48     uint64_t procUID = 0;
49     if (!qemu_pipe_valid(processPipe)) {
50         processPipe = 0;
51         mesa_logi("Process pipe failed");
52         return 0;
53     }
54 
55     // Send a confirmation int to the host
56     int32_t confirmInt = 100;
57     if (qemu_pipe_write_fully(processPipe, &confirmInt, sizeof(confirmInt))) {  // failed
58         qemu_pipe_close(processPipe);
59         processPipe = 0;
60         mesa_logi("Process pipe failed");
61         return 0;
62     }
63 
64     // Ask the host for per-process unique ID
65     if (qemu_pipe_read_fully(processPipe, &procUID, sizeof(procUID))) {
66         qemu_pipe_close(processPipe);
67         processPipe = 0;
68         procUID = 0;
69         mesa_logi("Process pipe failed");
70         return 0;
71     }
72 
73     return procUID;
74 }
75 
allocBuffer(size_t minSize)76 void* QemuPipeStream::allocBuffer(size_t minSize) {
77     // Add dedicated read buffer space at the front of the buffer.
78     minSize += kReadSize;
79 
80     size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
81     if (!m_buf) {
82         m_buf = (unsigned char*)malloc(allocSize);
83     } else if (m_bufsize < allocSize) {
84         unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
85         if (p != NULL) {
86             m_buf = p;
87             m_bufsize = allocSize;
88         } else {
89             mesa_loge("realloc (%zu) failed\n", allocSize);
90             free(m_buf);
91             m_buf = NULL;
92             m_bufsize = 0;
93         }
94     }
95 
96     return m_buf + kWriteOffset;
97 };
98 
commitBuffer(size_t size)99 int QemuPipeStream::commitBuffer(size_t size) {
100     if (size == 0) return 0;
101     return writeFully(m_buf + kWriteOffset, size);
102 }
103 
writeFully(const void * buf,size_t len)104 int QemuPipeStream::writeFully(const void* buf, size_t len) {
105     return qemu_pipe_write_fully(m_sock, buf, len);
106 }
107 
readFully(void * buf,size_t len)108 const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
109     return commitBufferAndReadFully(0, buf, len);
110 }
111 
commitBufferAndReadFully(size_t writeSize,void * userReadBufPtr,size_t totalReadSize)112 const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
113                                                               void* userReadBufPtr,
114                                                               size_t totalReadSize) {
115     unsigned char* userReadBuf = static_cast<unsigned char*>(userReadBufPtr);
116 
117     if (!valid()) return NULL;
118 
119     if (!userReadBuf) {
120         if (totalReadSize > 0) {
121             mesa_loge(
122                 "QemuPipeStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize "
123                 "%zu, lethal"
124                 " error, exiting.",
125                 totalReadSize);
126             abort();
127         }
128         if (!writeSize) {
129             return NULL;
130         }
131     }
132 
133     // Advance buffered read if not yet consumed.
134     size_t remaining = totalReadSize;
135     size_t bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
136     if (bufferedReadSize) {
137         memcpy(userReadBuf, m_buf + (m_read - m_readLeft), bufferedReadSize);
138         remaining -= bufferedReadSize;
139         m_readLeft -= bufferedReadSize;
140     }
141 
142     // Early out if nothing left to do.
143     if (!writeSize && !remaining) {
144         return userReadBuf;
145     }
146 
147     writeFully(m_buf + kWriteOffset, writeSize);
148 
149     // Now done writing. Early out if no reading left to do.
150     if (!remaining) {
151         return userReadBuf;
152     }
153 
154     // Read up to kReadSize bytes if all buffered read has been consumed.
155     size_t maxRead = m_readLeft ? 0 : kReadSize;
156 
157     ssize_t actual = 0;
158 
159     if (maxRead) {
160         actual = qemu_pipe_read(m_sock, m_buf, maxRead);
161         // Updated buffered read size.
162         if (actual > 0) {
163             m_read = m_readLeft = actual;
164         }
165 
166         if (actual == 0) {
167             mesa_logi("%s: end of pipe", __FUNCTION__);
168             return NULL;
169         }
170     }
171 
172     // Consume buffered read and read more if necessary.
173     while (remaining) {
174         bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
175         if (bufferedReadSize) {
176             memcpy(userReadBuf + (totalReadSize - remaining), m_buf + (m_read - m_readLeft),
177                    bufferedReadSize);
178             remaining -= bufferedReadSize;
179             m_readLeft -= bufferedReadSize;
180             continue;
181         }
182 
183         actual = qemu_pipe_read(m_sock, m_buf, kReadSize);
184 
185         if (actual == 0) {
186             mesa_logi("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
187             return NULL;
188         }
189 
190         if (actual > 0) {
191             m_read = m_readLeft = actual;
192             continue;
193         }
194 
195         if (!qemu_pipe_try_again(actual)) {
196             mesa_logi("%s: Error reading from pipe: %d", __FUNCTION__, errno);
197             return NULL;
198         }
199     }
200 
201     return userReadBuf;
202 }
203 
read(void * buf,size_t * inout_len)204 const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
205     if (!valid()) return NULL;
206     if (!buf) {
207         mesa_loge("QemuPipeStream::read failed, buf=NULL");
208         return NULL;  // do not allow NULL buf in that implementation
209     }
210 
211     int n = recv(buf, *inout_len);
212 
213     if (n > 0) {
214         *inout_len = n;
215         return (const unsigned char*)buf;
216     }
217 
218     return NULL;
219 }
220 
valid()221 bool QemuPipeStream::valid() { return qemu_pipe_valid(m_sock); }
222 
recv(void * buf,size_t len)223 int QemuPipeStream::recv(void* buf, size_t len) {
224     if (!valid()) return int(ERR_INVALID_SOCKET);
225     char* p = (char*)buf;
226     int ret = 0;
227     while (len > 0) {
228         int res = qemu_pipe_read(m_sock, p, len);
229         if (res > 0) {
230             p += res;
231             ret += res;
232             len -= res;
233             continue;
234         }
235         if (res == 0) { /* EOF */
236             break;
237         }
238         if (qemu_pipe_try_again(res)) {
239             continue;
240         }
241 
242         /* A real error */
243         if (ret == 0) ret = -1;
244         break;
245     }
246     return ret;
247 }
248