1 #pragma once 2 3 /* 4 * Copyright (C) 2017 The Android Open Source Project 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 // Memory layout for byte-oriented circular queues 20 21 #include <atomic> 22 #include <cstdint> 23 #include "common/vsoc/shm/base.h" 24 #include "common/vsoc/shm/lock.h" 25 26 struct iovec; 27 28 namespace vsoc { 29 class RegionSignalingInterface; 30 namespace layout { 31 32 /** 33 * Base classes for all spinlock protected circular queues. 34 * This class should be embedded in the per-region data structure that is used 35 * as the parameter to TypedRegion. 36 */ 37 template <uint32_t SizeLog2> 38 class CircularQueueBase { 39 public: 40 static constexpr size_t layout_size = (1 << SizeLog2) + 12; 41 42 private: 43 CircularQueueBase() = delete; 44 CircularQueueBase(const CircularQueueBase&) = delete; 45 CircularQueueBase& operator=(const CircularQueueBase&) = delete; 46 47 protected: 48 /** 49 * Specifies a part of the queue. Note, the given indexes must be masked 50 * before they can be used against buffer_ 51 */ 52 struct Range { 53 // Points to the first bytes that is part of the range 54 uint32_t start_idx; 55 // Points to the first byte that is not in the range. This is similar to 56 // the STL end iterator. 57 uint32_t end_idx; 58 }; 59 static const uintptr_t BufferSize = (1 << SizeLog2); 60 61 /** 62 * Copy bytes from buffer_in into the part of the queue specified by Range. 63 */ 64 void CopyInRange(const char* buffer_in, const Range& t); 65 66 /** 67 * Copy the bytes specified by range to the given buffer. They caller must 68 * ensure that the buffer is large enough to hold the content of the range. 69 */ 70 void CopyOutRange(const Range& t, char* buffer_out); 71 72 /** 73 * Wait until data becomes available in the queue. The caller must have 74 * called Lock() before invoking this. The caller must call Unlock() 75 * after this returns. 76 */ 77 void WaitForDataLocked(RegionSignalingInterface* r); 78 79 /** 80 * Reserve space in the queue for writing. The caller must have called Lock() 81 * before invoking this. The caller must call Unlock() after this returns. 82 * Indexes pointing to the reserved space will be placed in range. 83 * On success this returns bytes. 84 * On failure a negative errno indicates the problem. -ENOSPC indicates that 85 * bytes > the queue size, -EWOULDBLOCK indicates that the call would block 86 * waiting for space but was requested non bloking. 87 */ 88 intptr_t WriteReserveLocked(RegionSignalingInterface* r, size_t bytes, 89 Range* t, bool non_blocking); 90 RecoverBase()91 bool RecoverBase() { 92 return lock_.Recover(); 93 } 94 95 // Note: Both of these fields may hold values larger than the buffer size, 96 // they should be interpreted modulo the buffer size. This fact along with the 97 // buffer size being a power of two greatly simplyfies the index calculations. 98 // Advances when a reader has finished with buffer space 99 std::atomic<uint32_t> r_released_; 100 // Advances when buffer space is filled and ready for a reader 101 std::atomic<uint32_t> w_pub_; 102 // Spinlock that protects the region. 0 means unlocked 103 SpinLock lock_; 104 // The actual memory in the buffer 105 char buffer_[BufferSize]; 106 }; 107 using CircularQueueBase64k = CircularQueueBase<16>; 108 ASSERT_SHM_COMPATIBLE(CircularQueueBase64k); 109 110 /** 111 * Byte oriented circular queue. Reads will always return some data, but 112 * may return less data than requested. Writes will always write all of the 113 * data or return an error. 114 */ 115 template <uint32_t SizeLog2> 116 class CircularByteQueue : public CircularQueueBase<SizeLog2> { 117 public: 118 static constexpr size_t layout_size = 119 CircularQueueBase<SizeLog2>::layout_size; 120 /** 121 * Read at most max_size bytes from the qeueue, placing them in buffer_out 122 */ 123 intptr_t Read(RegionSignalingInterface* r, char* buffer_out, 124 std::size_t max_size); 125 /** 126 * Write all of the given bytes into the queue. If non_blocking isn't set the 127 * call may block until there is enough available space in the queue. On 128 * success the return value will match bytes. On failure a negative errno is 129 * returned. -ENOSPC: If the queue size is smaller than the number of bytes to 130 * write. -EWOULDBLOCK: If non_blocking is true and there is not enough free 131 * space. 132 */ 133 intptr_t Write(RegionSignalingInterface* r, const char* buffer_in, 134 std::size_t bytes, bool non_blocking = false); 135 Recover()136 bool Recover() { 137 return this->RecoverBase(); 138 } 139 140 protected: 141 using Range = typename CircularQueueBase<SizeLog2>::Range; 142 }; 143 using CircularByteQueue64k = CircularByteQueue<16>; 144 ASSERT_SHM_COMPATIBLE(CircularByteQueue64k); 145 146 /** 147 * Packet oriented circular queue. Reads will either return data or an error. 148 * Each return from read corresponds to a call to write and returns all of the 149 * data from that corresponding Write(). 150 */ 151 template <uint32_t SizeLog2, uint32_t MaxPacketSize> 152 class CircularPacketQueue : public CircularQueueBase<SizeLog2> { 153 public: 154 static constexpr size_t layout_size = 155 CircularQueueBase<SizeLog2>::layout_size; 156 157 /** 158 * Read a single packet from the queue, placing its data into buffer_out. 159 * If max_size indicates that buffer_out cannot hold the entire packet 160 * this function will return -ENOSPC. 161 */ 162 intptr_t Read(RegionSignalingInterface* r, char* buffer_out, 163 std::size_t max_size); 164 165 /** 166 * Writes [buffer_in, buffer_in + bytes) to the queue. 167 * If the number of bytes to be written exceeds the size of the queue 168 * -ENOSPC will be returned. 169 * If non_blocking is true and there is not enough free space on the queue to 170 * write all the data -EWOULDBLOCK will be returned. 171 */ 172 intptr_t Write(RegionSignalingInterface* r, const char* buffer_in, 173 uint32_t bytes, bool non_blocking = false); 174 175 /** 176 * Writes the data referenced by the given iov scatter/gather array to the 177 * queue. 178 * If the number of bytes to be written exceeds the size of the queue 179 * -ENOSPC will be returned. 180 * If non_blocking is true and there is not enough free space on the queue to 181 * write all the data -EWOULDBLOCK will be returned. 182 */ 183 intptr_t Writev( 184 RegionSignalingInterface *r, 185 const iovec *iov, 186 size_t iov_count, 187 bool non_blocking = false); 188 Recover()189 bool Recover() { 190 return this->RecoverBase(); 191 } 192 193 protected: 194 static_assert(CircularQueueBase<SizeLog2>::BufferSize >= MaxPacketSize, 195 "Buffer is too small to hold the maximum sized packet"); 196 using Range = typename CircularQueueBase<SizeLog2>::Range; 197 intptr_t CalculateBufferedSize(size_t payload); 198 }; 199 using CircularPacketQueue64k = CircularPacketQueue<16, 1024>; 200 ASSERT_SHM_COMPATIBLE(CircularPacketQueue64k); 201 202 } // namespace layout 203 } // namespace vsoc 204