blob: cf957483aef560e9dfd8f25d7db58b743661be28 [file] [log] [blame]
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -08001/*
2 * Copyright (C) 2016 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#ifndef HIDL_MQ_H
18#define HIDL_MQ_H
19
20#include <android-base/logging.h>
21#include <cutils/ashmem.h>
22#include <hidl/MQDescriptor.h>
23#include <sys/mman.h>
24#include <atomic>
25#include <new>
26
27namespace android {
28namespace hardware {
29
30template <typename T, MQFlavor flavor>
31struct MessageQueue {
32 /**
33 * @param Desc MQDescriptor describing the FMQ.
34 * @param resetPointers bool indicating whether the read/write pointers
35 * should be reset or not.
36 */
37 MessageQueue(const MQDescriptor<flavor>& Desc, bool resetPointers = true);
38
39 ~MessageQueue();
40
41 /**
42 * This constructor uses Ashmem shared memory to create an FMQ
43 * that can contain a maximum of numElementsInQueue elements of type T.
44 *
45 * @param numElementsInQueue Capacity of the MessageQueue in terms of T.
46 */
47 MessageQueue(size_t numElementsInQueue);
48
49 /**
50 * @return Number of items of type T that can be written into the FMQ
51 * without a read.
52 */
53 size_t availableToWrite() const;
54
55 /**
56 * @return Number of items of type T that are waiting to be read from the
57 * FMQ.
58 */
59 size_t availableToRead() const;
60
61 /**
62 * Returns the size of type T in bytes.
63 *
64 * @param Size of T.
65 */
66 size_t getQuantumSize() const;
67
68 /**
69 * Returns the size of the FMQ in terms of the size of type T.
70 *
71 * @return Number of items of type T that will fit in the FMQ.
72 */
73 size_t getQuantumCount() const;
74
75 /**
76 * @return Whether the FMQ is configured correctly.
77 */
78 bool isValid() const;
79
80 /**
81 * Non-blocking write to FMQ.
82 *
83 * @param data Pointer to the object of type T to be written into the FMQ.
84 *
85 * @return Whether the write was successful.
86 */
87 bool write(const T* data);
88
89 /**
90 * Non-blocking read from FMQ.
91 *
92 * @param data Pointer to the memory where the object read from the FMQ is
93 * copied to.
94 *
95 * @return Whether the read was successful.
96 */
97 bool read(T* data);
98
99 /**
100 * Write some data into the FMQ without blocking.
101 *
102 * @param data Pointer to the array of items of type T.
103 * @param count Number of items in array.
104 *
105 * @return Whether the write was successful.
106 */
107 bool write(const T* data, size_t count);
108
109 /**
110 * Read some data from the FMQ without blocking.
111 *
112 * @param data Pointer to the array to which read data is to be written.
113 * @param count Number of items to be read.
114 *
115 * @return Whether the read was successful.
116 */
117 bool read(T* data, size_t count);
118
119 /**
120 * Get a pointer to the MQDescriptor object that describes this FMQ.
121 *
122 * @return Pointer to the MQDescriptor associated with the FMQ.
123 */
124 const MQDescriptor<flavor>* getDesc() const { return mDesc.get(); }
125
126private:
127 struct region {
128 uint8_t* address;
129 size_t length;
130 };
131 struct transaction {
132 region first;
133 region second;
134 };
135
136 size_t writeBytes(const uint8_t* data, size_t size);
137 transaction beginWrite(size_t nBytesDesired) const;
138 void commitWrite(size_t nBytesWritten);
139
140 size_t readBytes(uint8_t* data, size_t size);
141 transaction beginRead(size_t nBytesDesired) const;
142 void commitRead(size_t nBytesRead);
143
144 size_t availableToWriteBytes() const;
145 size_t availableToReadBytes() const;
146
147 MessageQueue(const MessageQueue& other) = delete;
148 MessageQueue& operator=(const MessageQueue& other) = delete;
149 MessageQueue();
150
151 void* mapGrantorDescr(uint32_t grantorIdx);
152 void unmapGrantorDescr(void* address, uint32_t grantorIdx);
153 void initMemory(bool resetPointers);
154
155 std::unique_ptr<MQDescriptor<flavor>> mDesc;
156 uint8_t* mRing;
157 /*
158 * TODO(b/31550092): Change to 32 bit read and write pointer counters.
159 */
160 std::atomic<uint64_t>* mReadPtr;
161 std::atomic<uint64_t>* mWritePtr;
162};
163
164template <typename T, MQFlavor flavor>
165void MessageQueue<T, flavor>::initMemory(bool resetPointers) {
166 /*
167 * Verify that the the Descriptor contains the minimum number of grantors
168 * the native_handle is valid and T matches quantum size.
169 */
170 if ((mDesc == nullptr) || !mDesc->isHandleValid() ||
171 (mDesc->countGrantors() < MQDescriptor<flavor>::kMinGrantorCount) ||
172 (mDesc->getQuantum() != sizeof(T))) {
173 return;
174 }
175
176 if (flavor == kSynchronizedReadWrite) {
177 mReadPtr =
178 reinterpret_cast<std::atomic<uint64_t>*>
179 (mapGrantorDescr(MQDescriptor<flavor>::READPTRPOS));
180 } else {
181 /*
182 * The unsynchronized write flavor of the FMQ may have multiple readers
183 * and each reader would have their own read pointer counter.
184 */
185 mReadPtr = new (std::nothrow) std::atomic<uint64_t>;
186 }
187
188 CHECK(mReadPtr != nullptr);
189
190 mWritePtr =
191 reinterpret_cast<std::atomic<uint64_t>*>
192 (mapGrantorDescr(MQDescriptor<flavor>::WRITEPTRPOS));
193 CHECK(mWritePtr != nullptr);
194
195 if (resetPointers) {
196 mReadPtr->store(0, std::memory_order_release);
197 mWritePtr->store(0, std::memory_order_release);
198 } else if (flavor != kSynchronizedReadWrite) {
199 // Always reset the read pointer.
200 mReadPtr->store(0, std::memory_order_release);
201 }
202
203 mRing = reinterpret_cast<uint8_t*>(mapGrantorDescr
204 (MQDescriptor<flavor>::DATAPTRPOS));
205 CHECK(mRing != nullptr);
206}
207
208template <typename T, MQFlavor flavor>
209MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<flavor>& Desc, bool resetPointers) {
210 mDesc = std::unique_ptr<MQDescriptor<flavor>>(new (std::nothrow) MQDescriptor<flavor>(Desc));
211 if (mDesc == nullptr) {
212 return;
213 }
214
215 initMemory(resetPointers);
216}
217
218template <typename T, MQFlavor flavor>
219MessageQueue<T, flavor>::MessageQueue(size_t numElementsInQueue) {
220 size_t kQueueSizeBytes = numElementsInQueue * sizeof(T);
221 /*
222 * The FMQ needs to allocate memory for the ringbuffer as well as for the
223 * read and write pointer counters. Also, Ashmem memory region size needs to
224 * be specified in page-aligned bytes.
225 */
226 size_t kAshmemSizePageAligned =
227 (kQueueSizeBytes + 2 * sizeof(android::hardware::RingBufferPosition) +
228 PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
229
230 /*
231 * Create an ashmem region to map the memory for the ringbuffer,
232 * read counter and write counter.
233 */
234 int ashmemFd = ashmem_create_region("MessageQueue", kAshmemSizePageAligned);
235 ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
236
237 /*
238 * The native handle will contain the fds to be mapped.
239 */
240 native_handle_t* mqHandle =
241 native_handle_create(1 /* numFds */, 0 /* numInts */);
242 if (mqHandle == nullptr) {
243 return;
244 }
245
246 mqHandle->data[0] = ashmemFd;
247 mDesc = std::unique_ptr<MQDescriptor<flavor>>(
248 new (std::nothrow) MQDescriptor<flavor>(kQueueSizeBytes, mqHandle, sizeof(T)));
249 if (mDesc == nullptr) {
250 return;
251 }
252 initMemory(true);
253}
254
255template <typename T, MQFlavor flavor>
256MessageQueue<T, flavor>::~MessageQueue() {
257 if (flavor == kUnsynchronizedWrite) {
258 delete mReadPtr;
259 } else {
260 unmapGrantorDescr(mReadPtr, MQDescriptor<flavor>::READPTRPOS);
261 }
262 if (mWritePtr) unmapGrantorDescr(mWritePtr,
263 MQDescriptor<flavor>::WRITEPTRPOS);
264 if (mRing) unmapGrantorDescr(mRing, MQDescriptor<flavor>::DATAPTRPOS);
265}
266
267template <typename T, MQFlavor flavor>
268bool MessageQueue<T, flavor>::write(const T* data) {
269 return write(data, 1);
270}
271
272template <typename T, MQFlavor flavor>
273bool MessageQueue<T, flavor>::read(T* data) {
274 return read(data, 1);
275}
276
277template <typename T, MQFlavor flavor>
278bool MessageQueue<T, flavor>::write(const T* data, size_t count) {
279 /*
280 * If read/write synchronization is not enabled, data in the queue
281 * will be overwritten by a write operation when full.
282 */
283 if ((flavor == kSynchronizedReadWrite && (availableToWriteBytes() < sizeof(T) * count)) ||
284 (count > getQuantumCount()))
285 return false;
286
287 return (writeBytes(reinterpret_cast<const uint8_t*>(data),
288 sizeof(T) * count) == sizeof(T) * count);
289}
290
291template <typename T, MQFlavor flavor>
292__attribute__((no_sanitize("integer")))
293bool MessageQueue<T, flavor>::read(T* data, size_t count) {
294 if (availableToReadBytes() < sizeof(T) * count) return false;
295 /*
296 * If it is detected that the data in the queue was overwritten
297 * due to the reader process being too slow, the read pointer counter
298 * is set to the same as the write pointer counter to indicate error
299 * and the read returns false;
300 */
301 auto writePtr = mWritePtr->load(std::memory_order_relaxed);
302 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
303
304 if (writePtr - readPtr > mDesc->getSize()) {
305 mReadPtr->store(writePtr, std::memory_order_release);
306 return false;
307 }
308
309 return readBytes(reinterpret_cast<uint8_t*>(data), sizeof(T) * count) ==
310 sizeof(T) * count;
311}
312
313template <typename T, MQFlavor flavor>
314size_t MessageQueue<T, flavor>::availableToWriteBytes() const {
315 return mDesc->getSize() - availableToReadBytes();
316}
317
318template <typename T, MQFlavor flavor>
319size_t MessageQueue<T, flavor>::availableToWrite() const {
320 return availableToWriteBytes()/sizeof(T);
321}
322
323template <typename T, MQFlavor flavor>
324size_t MessageQueue<T, flavor>::availableToRead() const {
325 return availableToReadBytes()/sizeof(T);
326}
327
328template <typename T, MQFlavor flavor>
329size_t MessageQueue<T, flavor>::writeBytes(const uint8_t* data, size_t size) {
330 transaction tx = beginWrite(size);
331 memcpy(tx.first.address, data, tx.first.length);
332 memcpy(tx.second.address, data + tx.first.length, tx.second.length);
333 size_t result = tx.first.length + tx.second.length;
334 commitWrite(result);
335 return result;
336}
337
338/*
339 * The below method does not check for available space since it was already
340 * checked by write() API which invokes writeBytes() which in turn calls
341 * beginWrite().
342 */
343template <typename T, MQFlavor flavor>
344typename MessageQueue<T, flavor>::transaction MessageQueue<T, flavor>::beginWrite(
345 size_t nBytesDesired) const {
346 transaction result;
347 auto writePtr = mWritePtr->load(std::memory_order_relaxed);
348 size_t writeOffset = writePtr % mDesc->getSize();
349 size_t contiguous = mDesc->getSize() - writeOffset;
350 if (contiguous < nBytesDesired) {
351 result = {{mRing + writeOffset, contiguous},
352 {mRing, nBytesDesired - contiguous}};
353 } else {
354 result = {
355 {mRing + writeOffset, nBytesDesired}, {0, 0},
356 };
357 }
358 return result;
359}
360
361template <typename T, MQFlavor flavor>
362__attribute__((no_sanitize("integer")))
363void MessageQueue<T, flavor>::commitWrite(size_t nBytesWritten) {
364 auto writePtr = mWritePtr->load(std::memory_order_relaxed);
365 writePtr += nBytesWritten;
366 mWritePtr->store(writePtr, std::memory_order_release);
367}
368
369template <typename T, MQFlavor flavor>
370size_t MessageQueue<T, flavor>::availableToReadBytes() const {
371 /*
372 * Doing relaxed loads here because these accesses don't carry dependencies.
373 * Dependent accesses won't happen until after a call to beginWrite or
374 * beginRead
375 * which do proper acquire/release.
376 */
377 return mWritePtr->load(std::memory_order_relaxed) -
378 mReadPtr->load(std::memory_order_relaxed);
379}
380
381template <typename T, MQFlavor flavor>
382size_t MessageQueue<T, flavor>::readBytes(uint8_t* data, size_t size) {
383 transaction tx = beginRead(size);
384 memcpy(data, tx.first.address, tx.first.length);
385 memcpy(data + tx.first.length, tx.second.address, tx.second.length);
386 size_t result = tx.first.length + tx.second.length;
387 commitRead(result);
388 return result;
389}
390
391/*
392 * The below method does not check whether nBytesDesired bytes are available
393 * to read because the check is performed in the read() method before
394 * readBytes() is invoked.
395 */
396template <typename T, MQFlavor flavor>
397typename MessageQueue<T, flavor>::transaction MessageQueue<T, flavor>::beginRead(
398 size_t nBytesDesired) const {
399 transaction result;
400 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
401 size_t readOffset = readPtr % mDesc->getSize();
402 size_t contiguous = mDesc->getSize() - readOffset;
403
404 if (contiguous < nBytesDesired) {
405 result = {{mRing + readOffset, contiguous},
406 {mRing, nBytesDesired - contiguous}};
407 } else {
408 result = {
409 {mRing + readOffset, nBytesDesired}, {0, 0},
410 };
411 }
412
413 return result;
414}
415
416template <typename T, MQFlavor flavor>
417__attribute__((no_sanitize("integer")))
418void MessageQueue<T, flavor>::commitRead(size_t nBytesRead) {
419 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
420 readPtr += nBytesRead;
421 mReadPtr->store(readPtr, std::memory_order_release);
422}
423
424template <typename T, MQFlavor flavor>
425size_t MessageQueue<T, flavor>::getQuantumSize() const {
426 return mDesc->getQuantum();
427}
428
429template <typename T, MQFlavor flavor>
430size_t MessageQueue<T, flavor>::getQuantumCount() const {
431 return mDesc->getSize() / mDesc->getQuantum();
432}
433
434template <typename T, MQFlavor flavor>
435bool MessageQueue<T, flavor>::isValid() const {
436 return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
437}
438
439template <typename T, MQFlavor flavor>
440void* MessageQueue<T, flavor>::mapGrantorDescr(uint32_t grantorIdx) {
441 const native_handle_t* handle = mDesc->getNativeHandle()->handle();
442 auto mGrantors = mDesc->getGrantors();
443 if ((handle == nullptr) || (grantorIdx >= mGrantors.size())) {
444 return nullptr;
445 }
446
447 int fdIndex = mGrantors[grantorIdx].fdIndex;
448 /*
449 * Offset for mmap must be a multiple of PAGE_SIZE.
450 */
451 int mapOffset = (mGrantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
452 int mapLength =
453 mGrantors[grantorIdx].offset - mapOffset + mGrantors[grantorIdx].extent;
454
455 void* address = mmap(0, mapLength, PROT_READ | PROT_WRITE, MAP_SHARED,
456 handle->data[fdIndex], mapOffset);
457 return (address == MAP_FAILED)
458 ? nullptr
459 : reinterpret_cast<uint8_t*>(address) +
460 (mGrantors[grantorIdx].offset - mapOffset);
461}
462
463template <typename T, MQFlavor flavor>
464void MessageQueue<T, flavor>::unmapGrantorDescr(void* address,
465 uint32_t grantorIdx) {
466 auto mGrantors = mDesc->getGrantors();
467 if ((address == nullptr) || (grantorIdx >= mGrantors.size())) {
468 return;
469 }
470
471 int mapOffset = (mGrantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
472 int mapLength =
473 mGrantors[grantorIdx].offset - mapOffset + mGrantors[grantorIdx].extent;
474 void* baseAddress = reinterpret_cast<uint8_t*>(address) -
475 (mGrantors[grantorIdx].offset - mapOffset);
476 if (baseAddress) munmap(baseAddress, mapLength);
477}
478
479} // namespace hardware
480} // namespace android
481#endif // HIDL_MQ_H