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