blob: 9f5ef31228398e273a0814c44f78158823da2e3a [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.
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -080046 * @param configureEventFlagWord Boolean that specifies if memory should
47 * also be allocated and mapped for an EventFlag word.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080048 */
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -080049 MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord = false);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -080050
51 /**
52 * @return Number of items of type T that can be written into the FMQ
53 * without a read.
54 */
55 size_t availableToWrite() const;
56
57 /**
58 * @return Number of items of type T that are waiting to be read from the
59 * FMQ.
60 */
61 size_t availableToRead() const;
62
63 /**
64 * Returns the size of type T in bytes.
65 *
66 * @param Size of T.
67 */
68 size_t getQuantumSize() const;
69
70 /**
71 * Returns the size of the FMQ in terms of the size of type T.
72 *
73 * @return Number of items of type T that will fit in the FMQ.
74 */
75 size_t getQuantumCount() const;
76
77 /**
78 * @return Whether the FMQ is configured correctly.
79 */
80 bool isValid() const;
81
82 /**
83 * Non-blocking write to FMQ.
84 *
85 * @param data Pointer to the object of type T to be written into the FMQ.
86 *
87 * @return Whether the write was successful.
88 */
89 bool write(const T* data);
90
91 /**
92 * Non-blocking read from FMQ.
93 *
94 * @param data Pointer to the memory where the object read from the FMQ is
95 * copied to.
96 *
97 * @return Whether the read was successful.
98 */
99 bool read(T* data);
100
101 /**
102 * Write some data into the FMQ without blocking.
103 *
104 * @param data Pointer to the array of items of type T.
105 * @param count Number of items in array.
106 *
107 * @return Whether the write was successful.
108 */
109 bool write(const T* data, size_t count);
110
111 /**
112 * Read some data from the FMQ without blocking.
113 *
114 * @param data Pointer to the array to which read data is to be written.
115 * @param count Number of items to be read.
116 *
117 * @return Whether the read was successful.
118 */
119 bool read(T* data, size_t count);
120
121 /**
122 * Get a pointer to the MQDescriptor object that describes this FMQ.
123 *
124 * @return Pointer to the MQDescriptor associated with the FMQ.
125 */
126 const MQDescriptor<flavor>* getDesc() const { return mDesc.get(); }
127
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800128 /**
129 * Get a pointer to the Event Flag word if there is one associated with this FMQ.
130 *
131 * @return Pointer to an EventFlag word, will return nullptr if not configured
132 */
133 std::atomic<uint32_t>* getEventFlagWord() const { return mEvFlagWord; }
134
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800135private:
136 struct region {
137 uint8_t* address;
138 size_t length;
139 };
140 struct transaction {
141 region first;
142 region second;
143 };
144
145 size_t writeBytes(const uint8_t* data, size_t size);
146 transaction beginWrite(size_t nBytesDesired) const;
147 void commitWrite(size_t nBytesWritten);
148
149 size_t readBytes(uint8_t* data, size_t size);
150 transaction beginRead(size_t nBytesDesired) const;
151 void commitRead(size_t nBytesRead);
152
153 size_t availableToWriteBytes() const;
154 size_t availableToReadBytes() const;
155
156 MessageQueue(const MessageQueue& other) = delete;
157 MessageQueue& operator=(const MessageQueue& other) = delete;
158 MessageQueue();
159
160 void* mapGrantorDescr(uint32_t grantorIdx);
161 void unmapGrantorDescr(void* address, uint32_t grantorIdx);
162 void initMemory(bool resetPointers);
163
164 std::unique_ptr<MQDescriptor<flavor>> mDesc;
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800165 uint8_t* mRing = nullptr;
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800166 /*
167 * TODO(b/31550092): Change to 32 bit read and write pointer counters.
168 */
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800169 std::atomic<uint64_t>* mReadPtr = nullptr;
170 std::atomic<uint64_t>* mWritePtr = nullptr;
171
172 std::atomic<uint32_t>* mEvFlagWord = nullptr;
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800173};
174
175template <typename T, MQFlavor flavor>
176void MessageQueue<T, flavor>::initMemory(bool resetPointers) {
177 /*
178 * Verify that the the Descriptor contains the minimum number of grantors
179 * the native_handle is valid and T matches quantum size.
180 */
181 if ((mDesc == nullptr) || !mDesc->isHandleValid() ||
182 (mDesc->countGrantors() < MQDescriptor<flavor>::kMinGrantorCount) ||
183 (mDesc->getQuantum() != sizeof(T))) {
184 return;
185 }
186
187 if (flavor == kSynchronizedReadWrite) {
188 mReadPtr =
189 reinterpret_cast<std::atomic<uint64_t>*>
190 (mapGrantorDescr(MQDescriptor<flavor>::READPTRPOS));
191 } else {
192 /*
193 * The unsynchronized write flavor of the FMQ may have multiple readers
194 * and each reader would have their own read pointer counter.
195 */
196 mReadPtr = new (std::nothrow) std::atomic<uint64_t>;
197 }
198
199 CHECK(mReadPtr != nullptr);
200
201 mWritePtr =
202 reinterpret_cast<std::atomic<uint64_t>*>
203 (mapGrantorDescr(MQDescriptor<flavor>::WRITEPTRPOS));
204 CHECK(mWritePtr != nullptr);
205
206 if (resetPointers) {
207 mReadPtr->store(0, std::memory_order_release);
208 mWritePtr->store(0, std::memory_order_release);
209 } else if (flavor != kSynchronizedReadWrite) {
210 // Always reset the read pointer.
211 mReadPtr->store(0, std::memory_order_release);
212 }
213
214 mRing = reinterpret_cast<uint8_t*>(mapGrantorDescr
215 (MQDescriptor<flavor>::DATAPTRPOS));
216 CHECK(mRing != nullptr);
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800217
218 mEvFlagWord = static_cast<std::atomic<uint32_t>*>(
219 mapGrantorDescr(MQDescriptor<flavor>::EVFLAGWORDPOS));
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800220}
221
222template <typename T, MQFlavor flavor>
223MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<flavor>& Desc, bool resetPointers) {
224 mDesc = std::unique_ptr<MQDescriptor<flavor>>(new (std::nothrow) MQDescriptor<flavor>(Desc));
225 if (mDesc == nullptr) {
226 return;
227 }
228
229 initMemory(resetPointers);
230}
231
232template <typename T, MQFlavor flavor>
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800233MessageQueue<T, flavor>::MessageQueue(size_t numElementsInQueue, bool configureEventFlagWord) {
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800234 /*
235 * The FMQ needs to allocate memory for the ringbuffer as well as for the
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800236 * read and write pointer counters. If an EventFlag word is to be configured,
237 * we also need to allocate memory for the same/
238 */
239 size_t kQueueSizeBytes = numElementsInQueue * sizeof(T);
240 size_t kMetaDataSize = 2 * sizeof(android::hardware::RingBufferPosition);
241
242 if (configureEventFlagWord) {
243 kMetaDataSize+= sizeof(std::atomic<uint32_t>);
244 }
245
246 /*
247 * Ashmem memory region size needs to
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800248 * be specified in page-aligned bytes.
249 */
250 size_t kAshmemSizePageAligned =
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800251 (kQueueSizeBytes + kMetaDataSize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800252
253 /*
254 * Create an ashmem region to map the memory for the ringbuffer,
255 * read counter and write counter.
256 */
257 int ashmemFd = ashmem_create_region("MessageQueue", kAshmemSizePageAligned);
258 ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
259
260 /*
261 * The native handle will contain the fds to be mapped.
262 */
263 native_handle_t* mqHandle =
264 native_handle_create(1 /* numFds */, 0 /* numInts */);
265 if (mqHandle == nullptr) {
266 return;
267 }
268
269 mqHandle->data[0] = ashmemFd;
270 mDesc = std::unique_ptr<MQDescriptor<flavor>>(
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800271 new (std::nothrow) MQDescriptor<flavor>(kQueueSizeBytes,
272 mqHandle,
273 sizeof(T),
274 configureEventFlagWord));
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800275 if (mDesc == nullptr) {
276 return;
277 }
278 initMemory(true);
279}
280
281template <typename T, MQFlavor flavor>
282MessageQueue<T, flavor>::~MessageQueue() {
283 if (flavor == kUnsynchronizedWrite) {
284 delete mReadPtr;
285 } else {
286 unmapGrantorDescr(mReadPtr, MQDescriptor<flavor>::READPTRPOS);
287 }
288 if (mWritePtr) unmapGrantorDescr(mWritePtr,
289 MQDescriptor<flavor>::WRITEPTRPOS);
290 if (mRing) unmapGrantorDescr(mRing, MQDescriptor<flavor>::DATAPTRPOS);
Hridya Valsaraju92b79dc2016-12-19 14:57:44 -0800291 if (mEvFlagWord) unmapGrantorDescr(mEvFlagWord, MQDescriptor<flavor>::EVFLAGWORDPOS);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800292}
293
294template <typename T, MQFlavor flavor>
295bool MessageQueue<T, flavor>::write(const T* data) {
296 return write(data, 1);
297}
298
299template <typename T, MQFlavor flavor>
300bool MessageQueue<T, flavor>::read(T* data) {
301 return read(data, 1);
302}
303
304template <typename T, MQFlavor flavor>
305bool MessageQueue<T, flavor>::write(const T* data, size_t count) {
306 /*
307 * If read/write synchronization is not enabled, data in the queue
308 * will be overwritten by a write operation when full.
309 */
310 if ((flavor == kSynchronizedReadWrite && (availableToWriteBytes() < sizeof(T) * count)) ||
311 (count > getQuantumCount()))
312 return false;
313
314 return (writeBytes(reinterpret_cast<const uint8_t*>(data),
315 sizeof(T) * count) == sizeof(T) * count);
316}
317
318template <typename T, MQFlavor flavor>
319__attribute__((no_sanitize("integer")))
320bool MessageQueue<T, flavor>::read(T* data, size_t count) {
321 if (availableToReadBytes() < sizeof(T) * count) return false;
322 /*
323 * If it is detected that the data in the queue was overwritten
324 * due to the reader process being too slow, the read pointer counter
325 * is set to the same as the write pointer counter to indicate error
326 * and the read returns false;
Hridya Valsaraju04cdd2c2016-12-21 08:38:57 -0800327 * Need acquire/release memory ordering for mWritePtr.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800328 */
Hridya Valsaraju04cdd2c2016-12-21 08:38:57 -0800329 auto writePtr = mWritePtr->load(std::memory_order_acquire);
330 /*
331 * A relaxed load is sufficient for mReadPtr since there will be no
332 * stores to mReadPtr from a different thread.
333 */
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800334 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
335
336 if (writePtr - readPtr > mDesc->getSize()) {
337 mReadPtr->store(writePtr, std::memory_order_release);
338 return false;
339 }
340
341 return readBytes(reinterpret_cast<uint8_t*>(data), sizeof(T) * count) ==
342 sizeof(T) * count;
343}
344
345template <typename T, MQFlavor flavor>
346size_t MessageQueue<T, flavor>::availableToWriteBytes() const {
347 return mDesc->getSize() - availableToReadBytes();
348}
349
350template <typename T, MQFlavor flavor>
351size_t MessageQueue<T, flavor>::availableToWrite() const {
352 return availableToWriteBytes()/sizeof(T);
353}
354
355template <typename T, MQFlavor flavor>
356size_t MessageQueue<T, flavor>::availableToRead() const {
357 return availableToReadBytes()/sizeof(T);
358}
359
360template <typename T, MQFlavor flavor>
361size_t MessageQueue<T, flavor>::writeBytes(const uint8_t* data, size_t size) {
362 transaction tx = beginWrite(size);
363 memcpy(tx.first.address, data, tx.first.length);
364 memcpy(tx.second.address, data + tx.first.length, tx.second.length);
365 size_t result = tx.first.length + tx.second.length;
366 commitWrite(result);
367 return result;
368}
369
370/*
371 * The below method does not check for available space since it was already
372 * checked by write() API which invokes writeBytes() which in turn calls
373 * beginWrite().
374 */
375template <typename T, MQFlavor flavor>
376typename MessageQueue<T, flavor>::transaction MessageQueue<T, flavor>::beginWrite(
377 size_t nBytesDesired) const {
378 transaction result;
379 auto writePtr = mWritePtr->load(std::memory_order_relaxed);
380 size_t writeOffset = writePtr % mDesc->getSize();
381 size_t contiguous = mDesc->getSize() - writeOffset;
382 if (contiguous < nBytesDesired) {
383 result = {{mRing + writeOffset, contiguous},
384 {mRing, nBytesDesired - contiguous}};
385 } else {
386 result = {
387 {mRing + writeOffset, nBytesDesired}, {0, 0},
388 };
389 }
390 return result;
391}
392
393template <typename T, MQFlavor flavor>
394__attribute__((no_sanitize("integer")))
395void MessageQueue<T, flavor>::commitWrite(size_t nBytesWritten) {
396 auto writePtr = mWritePtr->load(std::memory_order_relaxed);
397 writePtr += nBytesWritten;
398 mWritePtr->store(writePtr, std::memory_order_release);
399}
400
401template <typename T, MQFlavor flavor>
402size_t MessageQueue<T, flavor>::availableToReadBytes() const {
403 /*
Hridya Valsaraju04cdd2c2016-12-21 08:38:57 -0800404 * This method is invoked by implementations of both read() and write() and
405 * hence requries a memory_order_acquired load for both mReadPtr and
406 * mWritePtr.
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800407 */
Hridya Valsaraju04cdd2c2016-12-21 08:38:57 -0800408 return mWritePtr->load(std::memory_order_acquire) -
409 mReadPtr->load(std::memory_order_acquire);
Hridya Valsaraju8b0d5a52016-12-16 10:29:03 -0800410}
411
412template <typename T, MQFlavor flavor>
413size_t MessageQueue<T, flavor>::readBytes(uint8_t* data, size_t size) {
414 transaction tx = beginRead(size);
415 memcpy(data, tx.first.address, tx.first.length);
416 memcpy(data + tx.first.length, tx.second.address, tx.second.length);
417 size_t result = tx.first.length + tx.second.length;
418 commitRead(result);
419 return result;
420}
421
422/*
423 * The below method does not check whether nBytesDesired bytes are available
424 * to read because the check is performed in the read() method before
425 * readBytes() is invoked.
426 */
427template <typename T, MQFlavor flavor>
428typename MessageQueue<T, flavor>::transaction MessageQueue<T, flavor>::beginRead(
429 size_t nBytesDesired) const {
430 transaction result;
431 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
432 size_t readOffset = readPtr % mDesc->getSize();
433 size_t contiguous = mDesc->getSize() - readOffset;
434
435 if (contiguous < nBytesDesired) {
436 result = {{mRing + readOffset, contiguous},
437 {mRing, nBytesDesired - contiguous}};
438 } else {
439 result = {
440 {mRing + readOffset, nBytesDesired}, {0, 0},
441 };
442 }
443
444 return result;
445}
446
447template <typename T, MQFlavor flavor>
448__attribute__((no_sanitize("integer")))
449void MessageQueue<T, flavor>::commitRead(size_t nBytesRead) {
450 auto readPtr = mReadPtr->load(std::memory_order_relaxed);
451 readPtr += nBytesRead;
452 mReadPtr->store(readPtr, std::memory_order_release);
453}
454
455template <typename T, MQFlavor flavor>
456size_t MessageQueue<T, flavor>::getQuantumSize() const {
457 return mDesc->getQuantum();
458}
459
460template <typename T, MQFlavor flavor>
461size_t MessageQueue<T, flavor>::getQuantumCount() const {
462 return mDesc->getSize() / mDesc->getQuantum();
463}
464
465template <typename T, MQFlavor flavor>
466bool MessageQueue<T, flavor>::isValid() const {
467 return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
468}
469
470template <typename T, MQFlavor flavor>
471void* MessageQueue<T, flavor>::mapGrantorDescr(uint32_t grantorIdx) {
472 const native_handle_t* handle = mDesc->getNativeHandle()->handle();
473 auto mGrantors = mDesc->getGrantors();
474 if ((handle == nullptr) || (grantorIdx >= mGrantors.size())) {
475 return nullptr;
476 }
477
478 int fdIndex = mGrantors[grantorIdx].fdIndex;
479 /*
480 * Offset for mmap must be a multiple of PAGE_SIZE.
481 */
482 int mapOffset = (mGrantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
483 int mapLength =
484 mGrantors[grantorIdx].offset - mapOffset + mGrantors[grantorIdx].extent;
485
486 void* address = mmap(0, mapLength, PROT_READ | PROT_WRITE, MAP_SHARED,
487 handle->data[fdIndex], mapOffset);
488 return (address == MAP_FAILED)
489 ? nullptr
490 : reinterpret_cast<uint8_t*>(address) +
491 (mGrantors[grantorIdx].offset - mapOffset);
492}
493
494template <typename T, MQFlavor flavor>
495void MessageQueue<T, flavor>::unmapGrantorDescr(void* address,
496 uint32_t grantorIdx) {
497 auto mGrantors = mDesc->getGrantors();
498 if ((address == nullptr) || (grantorIdx >= mGrantors.size())) {
499 return;
500 }
501
502 int mapOffset = (mGrantors[grantorIdx].offset / PAGE_SIZE) * PAGE_SIZE;
503 int mapLength =
504 mGrantors[grantorIdx].offset - mapOffset + mGrantors[grantorIdx].extent;
505 void* baseAddress = reinterpret_cast<uint8_t*>(address) -
506 (mGrantors[grantorIdx].offset - mapOffset);
507 if (baseAddress) munmap(baseAddress, mapLength);
508}
509
510} // namespace hardware
511} // namespace android
512#endif // HIDL_MQ_H