/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AddressSpaceStream.h" #if PLATFORM_SDK_VERSION < 26 #include #else #include #endif #include #include #include #include #include static const size_t kReadSize = 512 * 1024; static const size_t kWriteOffset = kReadSize; AddressSpaceStream* createAddressSpaceStream(size_t ignored_bufSize) { // Ignore incoming ignored_bufSize (void)ignored_bufSize; auto handle = goldfish_address_space_open(); address_space_handle_t child_device_handle; if (!goldfish_address_space_set_subdevice_type(handle, GoldfishAddressSpaceSubdeviceType::Graphics, &child_device_handle)) { ALOGE("AddressSpaceStream::create failed (initial device create)\n"); goldfish_address_space_close(handle); return nullptr; } struct address_space_ping request; request.metadata = ASG_GET_RING; if (!goldfish_address_space_ping(child_device_handle, &request)) { ALOGE("AddressSpaceStream::create failed (get ring)\n"); goldfish_address_space_close(child_device_handle); return nullptr; } uint64_t ringOffset = request.metadata; request.metadata = ASG_GET_BUFFER; if (!goldfish_address_space_ping(child_device_handle, &request)) { ALOGE("AddressSpaceStream::create failed (get buffer)\n"); goldfish_address_space_close(child_device_handle); return nullptr; } uint64_t bufferOffset = request.metadata; uint64_t bufferSize = request.size; if (!goldfish_address_space_claim_shared( child_device_handle, ringOffset, sizeof(asg_ring_storage))) { ALOGE("AddressSpaceStream::create failed (claim ring storage)\n"); goldfish_address_space_close(child_device_handle); return nullptr; } if (!goldfish_address_space_claim_shared( child_device_handle, bufferOffset, bufferSize)) { ALOGE("AddressSpaceStream::create failed (claim buffer storage)\n"); goldfish_address_space_unclaim_shared(child_device_handle, ringOffset); goldfish_address_space_close(child_device_handle); return nullptr; } char* ringPtr = (char*)goldfish_address_space_map( child_device_handle, ringOffset, sizeof(struct asg_ring_storage)); if (!ringPtr) { ALOGE("AddressSpaceStream::create failed (map ring storage)\n"); goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset); goldfish_address_space_unclaim_shared(child_device_handle, ringOffset); goldfish_address_space_close(child_device_handle); return nullptr; } char* bufferPtr = (char*)goldfish_address_space_map( child_device_handle, bufferOffset, bufferSize); if (!bufferPtr) { ALOGE("AddressSpaceStream::create failed (map buffer storage)\n"); goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage)); goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset); goldfish_address_space_unclaim_shared(child_device_handle, ringOffset); goldfish_address_space_close(child_device_handle); return nullptr; } struct asg_context context = asg_context_create( ringPtr, bufferPtr, bufferSize); request.metadata = ASG_SET_VERSION; request.size = 1; // version 1 if (!goldfish_address_space_ping(child_device_handle, &request)) { ALOGE("AddressSpaceStream::create failed (get buffer)\n"); goldfish_address_space_unmap(bufferPtr, bufferSize); goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage)); goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset); goldfish_address_space_unclaim_shared(child_device_handle, ringOffset); goldfish_address_space_close(child_device_handle); return nullptr; } uint32_t version = request.size; context.ring_config->transfer_mode = 1; context.ring_config->host_consumed_pos = 0; context.ring_config->guest_write_pos = 0; struct address_space_ops ops = { .open = goldfish_address_space_open, .close = goldfish_address_space_close, .claim_shared = goldfish_address_space_claim_shared, .unclaim_shared = goldfish_address_space_unclaim_shared, .map = goldfish_address_space_map, .unmap = goldfish_address_space_unmap, .set_subdevice_type = goldfish_address_space_set_subdevice_type, .ping = goldfish_address_space_ping, }; AddressSpaceStream* res = new AddressSpaceStream( child_device_handle, version, context, ringOffset, bufferOffset, false /* not virtio */, ops); return res; } #if defined(HOST_BUILD) || defined(__Fuchsia__) AddressSpaceStream* createVirtioGpuAddressSpaceStream(size_t ignored_bufSize) { // Ignore incoming ignored_bufSize (void)ignored_bufSize; return nullptr; } #else AddressSpaceStream* createVirtioGpuAddressSpaceStream(size_t ignored_bufSize) { // Ignore incoming ignored_bufSize (void)ignored_bufSize; auto handle = virtgpu_address_space_open(); if (handle == reinterpret_cast(-1)) { ALOGE("AddressSpaceStream::create failed (open device)\n"); return nullptr; } struct address_space_virtgpu_info virtgpu_info; ALOGD("%s: create subdevice and get resp\n", __func__); if (!virtgpu_address_space_create_context_with_subdevice( handle, GoldfishAddressSpaceSubdeviceType::VirtioGpuGraphics, &virtgpu_info)) { ALOGE("AddressSpaceStream::create failed (create subdevice)\n"); virtgpu_address_space_close(handle); return nullptr; } ALOGD("%s: create subdevice and get resp (done)\n", __func__); struct address_space_ping request; uint32_t ringSize = 0; uint32_t bufferSize = 0; request.metadata = ASG_GET_RING; if (!virtgpu_address_space_ping_with_response( &virtgpu_info, &request)) { ALOGE("AddressSpaceStream::create failed (get ring version)\n"); virtgpu_address_space_close(handle); return nullptr; } ringSize = request.size; request.metadata = ASG_GET_BUFFER; if (!virtgpu_address_space_ping_with_response( &virtgpu_info, &request)) { ALOGE("AddressSpaceStream::create failed (get ring version)\n"); virtgpu_address_space_close(handle); return nullptr; } bufferSize = request.size; request.metadata = ASG_SET_VERSION; request.size = 1; // version 1 if (!virtgpu_address_space_ping_with_response( &virtgpu_info, &request)) { ALOGE("AddressSpaceStream::create failed (set version)\n"); virtgpu_address_space_close(handle); return nullptr; } ALOGD("%s: ping returned. context ring and buffer sizes %u %u\n", __func__, ringSize, bufferSize); uint64_t hostmem_id = request.metadata; uint32_t version = request.size; size_t hostmem_alloc_size = (size_t)(ringSize + bufferSize); ALOGD("%s: hostmem size: %zu\n", __func__, hostmem_alloc_size); struct address_space_virtgpu_hostmem_info hostmem_info; if (!virtgpu_address_space_allocate_hostmem( handle, hostmem_alloc_size, hostmem_id, &hostmem_info)) { ALOGE("AddressSpaceStream::create failed (alloc hostmem)\n"); virtgpu_address_space_close(handle); return nullptr; } request.metadata = ASG_GET_CONFIG; if (!virtgpu_address_space_ping_with_response( &virtgpu_info, &request)) { ALOGE("AddressSpaceStream::create failed (get config)\n"); virtgpu_address_space_close(handle); return nullptr; } char* ringPtr = (char*)hostmem_info.ptr; char* bufferPtr = ((char*)hostmem_info.ptr) + sizeof(struct asg_ring_storage); struct asg_context context = asg_context_create( (char*)ringPtr, (char*)bufferPtr, bufferSize); context.ring_config->transfer_mode = 1; context.ring_config->host_consumed_pos = 0; context.ring_config->guest_write_pos = 0; struct address_space_ops ops = { .open = virtgpu_address_space_open, .close = virtgpu_address_space_close, .ping = virtgpu_address_space_ping, .allocate_hostmem = virtgpu_address_space_allocate_hostmem, .ping_with_response = virtgpu_address_space_ping_with_response, }; AddressSpaceStream* res = new AddressSpaceStream( handle, version, context, 0, 0, true /* is virtio */, ops); return res; } #endif // HOST_BUILD || __Fuchsia__ AddressSpaceStream::AddressSpaceStream( address_space_handle_t handle, uint32_t version, struct asg_context context, uint64_t ringOffset, uint64_t writeBufferOffset, bool virtioMode, struct address_space_ops ops) : IOStream(context.ring_config->flush_interval), m_virtioMode(virtioMode), m_ops(ops), m_tmpBuf(0), m_tmpBufSize(0), m_tmpBufXferSize(0), m_usingTmpBuf(0), m_readBuf(0), m_read(0), m_readLeft(0), m_handle(handle), m_version(version), m_context(context), m_ringOffset(ringOffset), m_writeBufferOffset(writeBufferOffset), m_writeBufferSize(context.ring_config->buffer_size), m_writeBufferMask(m_writeBufferSize - 1), m_buf((unsigned char*)context.buffer), m_writeStart(m_buf), m_writeStep(context.ring_config->flush_interval), m_notifs(0), m_written(0) { // We'll use this in the future, but at the moment, // it's a potential compile Werror. (void)m_version; } AddressSpaceStream::~AddressSpaceStream() { if (!m_virtioMode) { m_ops.unmap(m_context.to_host, sizeof(struct asg_ring_storage)); m_ops.unmap(m_context.buffer, m_writeBufferSize); m_ops.unclaim_shared(m_handle, m_ringOffset); m_ops.unclaim_shared(m_handle, m_writeBufferOffset); } m_ops.close(m_handle); if (m_readBuf) free(m_readBuf); if (m_tmpBuf) free(m_tmpBuf); } size_t AddressSpaceStream::idealAllocSize(size_t len) { if (len > m_writeStep) return len; return m_writeStep; } void *AddressSpaceStream::allocBuffer(size_t minSize) { if (!m_readBuf) { m_readBuf = (unsigned char*)malloc(kReadSize); } size_t allocSize = (m_writeStep < minSize ? minSize : m_writeStep); if (m_writeStep < allocSize) { if (!m_tmpBuf) { m_tmpBufSize = allocSize * 2; m_tmpBuf = (unsigned char*)malloc(m_tmpBufSize); } if (m_tmpBufSize < allocSize) { m_tmpBufSize = allocSize * 2; m_tmpBuf = (unsigned char*)realloc(m_tmpBuf, m_tmpBufSize); } if (!m_usingTmpBuf) { flush(); } m_usingTmpBuf = true; m_tmpBufXferSize = allocSize; return m_tmpBuf; } else { if (m_usingTmpBuf) { writeFully(m_tmpBuf, m_tmpBufXferSize); m_usingTmpBuf = false; m_tmpBufXferSize = 0; } return m_writeStart; } }; int AddressSpaceStream::commitBuffer(size_t size) { if (size == 0) return 0; if (m_usingTmpBuf) { writeFully(m_tmpBuf, size); m_tmpBufXferSize = 0; m_usingTmpBuf = false; return 0; } else { int res = type1Write(m_writeStart - m_buf, size); advanceWrite(); return res; } } const unsigned char *AddressSpaceStream::readFully(void *ptr, size_t totalReadSize) { unsigned char* userReadBuf = static_cast(ptr); if (!userReadBuf) { if (totalReadSize > 0) { ALOGE("AddressSpaceStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize %zu, lethal" " error, exiting.", totalReadSize); abort(); } return nullptr; } // Advance buffered read if not yet consumed. size_t remaining = totalReadSize; size_t bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining; if (bufferedReadSize) { memcpy(userReadBuf, m_readBuf + (m_read - m_readLeft), bufferedReadSize); remaining -= bufferedReadSize; m_readLeft -= bufferedReadSize; } if (!remaining) return userReadBuf; // Read up to kReadSize bytes if all buffered read has been consumed. size_t maxRead = m_readLeft ? 0 : kReadSize; ssize_t actual = 0; if (maxRead) { actual = speculativeRead(m_readBuf, maxRead); // Updated buffered read size. if (actual > 0) { m_read = m_readLeft = actual; } if (actual == 0) { ALOGD("%s: end of pipe", __FUNCTION__); return NULL; } } // Consume buffered read and read more if necessary. while (remaining) { bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining; if (bufferedReadSize) { memcpy(userReadBuf + (totalReadSize - remaining), m_readBuf + (m_read - m_readLeft), bufferedReadSize); remaining -= bufferedReadSize; m_readLeft -= bufferedReadSize; continue; } actual = speculativeRead(m_readBuf, kReadSize); if (actual == 0) { ALOGD("%s: Failed reading from pipe: %d", __FUNCTION__, errno); return NULL; } if (actual > 0) { m_read = m_readLeft = actual; continue; } } return userReadBuf; } const unsigned char *AddressSpaceStream::read(void *buf, size_t *inout_len) { unsigned char* dst = (unsigned char*)buf; size_t wanted = *inout_len; ssize_t actual = speculativeRead(dst, wanted); if (actual >= 0) { *inout_len = actual; } else { return nullptr; } return (const unsigned char*)dst; } int AddressSpaceStream::writeFully(const void *buf, size_t size) { ensureConsumerFinishing(); ensureType3Finished(); ensureType1Finished(); m_context.ring_config->transfer_size = size; m_context.ring_config->transfer_mode = 3; size_t sent = 0; size_t quarterRingSize = m_writeBufferSize / 4; size_t chunkSize = size < quarterRingSize ? size : quarterRingSize; const uint8_t* bufferBytes = (const uint8_t*)buf; while (sent < size) { size_t remaining = size - sent; size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize; long sentChunks = ring_buffer_view_write( m_context.to_host_large_xfer.ring, &m_context.to_host_large_xfer.view, bufferBytes + sent, sendThisTime, 1); if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) { notifyAvailable(); } if (sentChunks == 0) { ring_buffer_yield(); } sent += sentChunks * sendThisTime; if (isInError()) { return -1; } } ensureType3Finished(); m_context.ring_config->transfer_mode = 1; m_written += size; return 0; } const unsigned char *AddressSpaceStream::commitBufferAndReadFully( size_t writeSize, void *userReadBufPtr, size_t totalReadSize) { if (m_usingTmpBuf) { writeFully(m_tmpBuf, writeSize); m_usingTmpBuf = false; m_tmpBufXferSize = 0; return readFully(userReadBufPtr, totalReadSize); } else { commitBuffer(writeSize); return readFully(userReadBufPtr, totalReadSize); } } bool AddressSpaceStream::isInError() const { return 1 == m_context.ring_config->in_error; } ssize_t AddressSpaceStream::speculativeRead(unsigned char* readBuffer, size_t trySize) { ensureConsumerFinishing(); ensureType3Finished(); ensureType1Finished(); size_t actuallyRead = 0; size_t readIters = 0; size_t backedOffIters = 0; const size_t kSpeculativeReadBackoffIters = 10000000ULL; while (!actuallyRead) { ++readIters; uint32_t readAvail = ring_buffer_available_read( m_context.from_host_large_xfer.ring, &m_context.from_host_large_xfer.view); if (!readAvail) { ring_buffer_yield(); continue; } if (readAvail && readIters > kSpeculativeReadBackoffIters) { usleep(10); ++backedOffIters; } uint32_t toRead = readAvail > trySize ? trySize : readAvail; long stepsRead = ring_buffer_view_read( m_context.from_host_large_xfer.ring, &m_context.from_host_large_xfer.view, readBuffer, toRead, 1); actuallyRead += stepsRead * toRead; if (isInError()) { return -1; } } if (backedOffIters > 0) { ALOGW("%s: backed off %zu times due to host slowness.\n", __func__, backedOffIters); } return actuallyRead; } void AddressSpaceStream::notifyAvailable() { struct address_space_ping request; request.metadata = ASG_NOTIFY_AVAILABLE; m_ops.ping(m_handle, &request); ++m_notifs; } uint32_t AddressSpaceStream::getRelativeBufferPos(uint32_t pos) { return pos & m_writeBufferMask; } void AddressSpaceStream::advanceWrite() { m_writeStart += m_context.ring_config->flush_interval; if (m_writeStart == m_buf + m_context.ring_config->buffer_size) { m_writeStart = m_buf; } } void AddressSpaceStream::ensureConsumerFinishing() { uint32_t currAvailRead = ring_buffer_available_read(m_context.to_host, 0); while (currAvailRead) { ring_buffer_yield(); uint32_t nextAvailRead = ring_buffer_available_read(m_context.to_host, 0); if (nextAvailRead != currAvailRead) { break; } if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) { notifyAvailable(); break; } } } void AddressSpaceStream::ensureType1Finished() { ensureConsumerFinishing(); uint32_t currAvailRead = ring_buffer_available_read(m_context.to_host, 0); while (currAvailRead) { ring_buffer_yield(); currAvailRead = ring_buffer_available_read(m_context.to_host, 0); if (isInError()) { return; } } } void AddressSpaceStream::ensureType3Finished() { uint32_t availReadLarge = ring_buffer_available_read( m_context.to_host_large_xfer.ring, &m_context.to_host_large_xfer.view); while (availReadLarge) { ring_buffer_yield(); availReadLarge = ring_buffer_available_read( m_context.to_host_large_xfer.ring, &m_context.to_host_large_xfer.view); if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) { notifyAvailable(); } if (isInError()) { return; } } } int AddressSpaceStream::type1Write(uint32_t bufferOffset, size_t size) { size_t sent = 0; size_t sizeForRing = sizeof(struct asg_type1_xfer); struct asg_type1_xfer xfer = { bufferOffset, (uint32_t)size, }; uint8_t* writeBufferBytes = (uint8_t*)(&xfer); uint32_t maxOutstanding = 1; uint32_t maxSteps = m_context.ring_config->buffer_size / m_context.ring_config->flush_interval; if (maxSteps > 1) maxOutstanding = maxSteps >> 1; uint32_t ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0); while (ringAvailReadNow >= maxOutstanding) { ensureConsumerFinishing(); ring_buffer_yield(); ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0); } while (sent < sizeForRing) { long sentChunks = ring_buffer_write( m_context.to_host, writeBufferBytes + sent, sizeForRing - sent, 1); if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME) { notifyAvailable(); } if (sentChunks == 0) { ring_buffer_yield(); } sent += sentChunks * (sizeForRing - sent); if (isInError()) { return -1; } } ensureConsumerFinishing(); m_written += size; float mb = (float)m_written / 1048576.0f; if (mb > 100.0f) { ALOGD("%s: %f mb in %d notifs. %f mb/notif\n", __func__, mb, m_notifs, m_notifs ? mb / (float)m_notifs : 0.0f); m_notifs = 0; m_written = 0; } return 0; }