1 /*
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 #define LOG_TAG "FMQ_EventFlags"
18 
19 #include <linux/futex.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 
25 #include <new>
26 
27 #include <fmq/EventFlag.h>
28 #include <utils/Log.h>
29 #include <utils/SystemClock.h>
30 
31 namespace android {
32 namespace hardware {
33 
createEventFlag(int fd,off_t offset,EventFlag ** flag)34 status_t EventFlag::createEventFlag(int fd, off_t offset, EventFlag** flag) {
35     if (flag == nullptr) {
36         return BAD_VALUE;
37     }
38 
39     status_t status = NO_MEMORY;
40     *flag = nullptr;
41 
42     EventFlag* evFlag = new (std::nothrow) EventFlag(fd, offset, &status);
43     if (evFlag != nullptr) {
44         if (status == NO_ERROR) {
45             *flag = evFlag;
46         } else {
47             delete evFlag;
48         }
49     }
50 
51     return status;
52 }
53 
createEventFlag(std::atomic<uint32_t> * fwAddr,EventFlag ** flag)54 status_t EventFlag::createEventFlag(std::atomic<uint32_t>* fwAddr,
55                                     EventFlag** flag) {
56     if (flag == nullptr) {
57         return BAD_VALUE;
58     }
59 
60     status_t status = NO_MEMORY;
61     *flag  = nullptr;
62 
63     EventFlag* evFlag = new (std::nothrow) EventFlag(fwAddr, &status);
64     if (evFlag != nullptr) {
65         if (status == NO_ERROR) {
66             *flag = evFlag;
67         } else {
68             delete evFlag;
69         }
70     }
71 
72     return status;
73 }
74 
75 /*
76  * mmap memory for the futex word
77  */
EventFlag(int fd,off_t offset,status_t * status)78 EventFlag::EventFlag(int fd, off_t offset, status_t* status) {
79     mEfWordPtr = static_cast<std::atomic<uint32_t>*>(mmap(NULL,
80                                                           sizeof(std::atomic<uint32_t>),
81                                                           PROT_READ | PROT_WRITE,
82                                                           MAP_SHARED, fd, offset));
83     mEfWordNeedsUnmapping = true;
84     if (mEfWordPtr != MAP_FAILED) {
85         *status = NO_ERROR;
86     } else {
87         *status = -errno;
88         ALOGE("Attempt to mmap event flag word failed: %s\n", strerror(errno));
89     }
90 }
91 
92 /*
93  * Use this constructor if we already know where the futex word for
94  * the EventFlag group lives.
95  */
EventFlag(std::atomic<uint32_t> * fwAddr,status_t * status)96 EventFlag::EventFlag(std::atomic<uint32_t>* fwAddr, status_t* status) {
97     *status = NO_ERROR;
98     if (fwAddr == nullptr) {
99         *status = BAD_VALUE;
100     } else {
101         mEfWordPtr = fwAddr;
102     }
103 }
104 
105 /*
106  * Set the specified bits of the futex word here and wake up any
107  * thread waiting on any of the bits.
108  */
wake(uint32_t bitmask)109 status_t EventFlag::wake(uint32_t bitmask) {
110     /*
111      * Return early if there are no set bits in bitmask.
112      */
113     if (bitmask == 0) {
114         return NO_ERROR;
115     }
116 
117     status_t status = NO_ERROR;
118     uint32_t old = std::atomic_fetch_or(mEfWordPtr, bitmask);
119     /*
120      * No need to call FUTEX_WAKE_BITSET if there were deferred wakes
121      * already available for all set bits from bitmask.
122      */
123     if ((~old & bitmask) != 0) {
124         int ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAKE_BITSET,
125                           INT_MAX, NULL, NULL, bitmask);
126         if (ret == -1) {
127             status = -errno;
128             ALOGE("Error in event flag wake attempt: %s\n", strerror(errno));
129         }
130     }
131     return status;
132 }
133 
134 /*
135  * Wait for any of the bits in the bitmask to be set
136  * and return which bits caused the return.
137  */
waitHelper(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds)138 status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
139     /*
140      * Return early if there are no set bits in bitmask.
141      */
142     if (bitmask == 0 || efState == nullptr) {
143         return BAD_VALUE;
144     }
145 
146     status_t status = NO_ERROR;
147     uint32_t old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
148     uint32_t setBits = old & bitmask;
149     /*
150      * If there was a deferred wake available, no need to call FUTEX_WAIT_BITSET.
151      */
152     if (setBits != 0) {
153         *efState = setBits;
154         return status;
155     }
156 
157     uint32_t efWord = old & ~bitmask;
158     /*
159      * The syscall will put the thread to sleep only
160      * if the futex word still contains the expected
161      * value i.e. efWord. If the futex word contents have
162      * changed, it fails with the error EAGAIN; If a timeout
163      * is specified and exceeded the syscall fails with ETIMEDOUT.
164      */
165     int ret = 0;
166     if (timeoutNanoSeconds) {
167         struct timespec waitTimeAbsolute;
168         addNanosecondsToCurrentTime(timeoutNanoSeconds, &waitTimeAbsolute);
169 
170         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET,
171                       efWord, &waitTimeAbsolute, NULL, bitmask);
172     } else {
173         ret = syscall(__NR_futex, mEfWordPtr, FUTEX_WAIT_BITSET, efWord, NULL, NULL, bitmask);
174     }
175     if (ret == -1) {
176         status = -errno;
177         if (status != -EAGAIN && status != -ETIMEDOUT) {
178             ALOGE("Event flag wait was unsuccessful: %s\n", strerror(errno));
179         }
180         *efState = 0;
181     } else {
182         old = std::atomic_fetch_and(mEfWordPtr, ~bitmask);
183         *efState = old & bitmask;
184 
185         if (*efState == 0) {
186             /* Return -EINTR for a spurious wakeup */
187             status = -EINTR;
188         }
189     }
190     return status;
191 }
192 
193 /*
194  * Wait for any of the bits in the bitmask to be set
195  * and return which bits caused the return. If 'retry'
196  * is true, wait again on a spurious wake-up.
197  */
wait(uint32_t bitmask,uint32_t * efState,int64_t timeoutNanoSeconds,bool retry)198 status_t EventFlag::wait(uint32_t bitmask,
199                          uint32_t* efState,
200                          int64_t timeoutNanoSeconds,
201                          bool retry) {
202     if (!retry) {
203         return waitHelper(bitmask, efState, timeoutNanoSeconds);
204     }
205 
206     bool shouldTimeOut = timeoutNanoSeconds != 0;
207     int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
208     status_t status;
209     while (true) {
210         if (shouldTimeOut) {
211             int64_t currentTimeNs = android::elapsedRealtimeNano();
212             /*
213              * Decrement TimeOutNanos to account for the time taken to complete the last
214              * iteration of the while loop.
215              */
216             timeoutNanoSeconds -= currentTimeNs - prevTimeNs;
217             prevTimeNs = currentTimeNs;
218             if (timeoutNanoSeconds <= 0) {
219                 status = -ETIMEDOUT;
220                 *efState = 0;
221                 break;
222             }
223         }
224 
225         status = waitHelper(bitmask, efState, timeoutNanoSeconds);
226         if ((status != -EAGAIN) && (status != -EINTR)) {
227             break;
228         }
229     }
230     return status;
231 }
232 
unmapEventFlagWord(std::atomic<uint32_t> * efWordPtr,bool * efWordNeedsUnmapping)233 status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
234                                        bool* efWordNeedsUnmapping) {
235     status_t status = NO_ERROR;
236     if (*efWordNeedsUnmapping) {
237         int ret = munmap(efWordPtr, sizeof(std::atomic<uint32_t>));
238         if (ret != 0) {
239             status = -errno;
240             ALOGE("Error in deleting event flag group: %s\n", strerror(errno));
241         }
242         *efWordNeedsUnmapping = false;
243     }
244     return status;
245 }
246 
deleteEventFlag(EventFlag ** evFlag)247 status_t EventFlag::deleteEventFlag(EventFlag** evFlag) {
248     if (evFlag == nullptr || *evFlag == nullptr) {
249         return BAD_VALUE;
250     }
251 
252     status_t status = unmapEventFlagWord((*evFlag)->mEfWordPtr,
253                                          &(*evFlag)->mEfWordNeedsUnmapping);
254     delete *evFlag;
255     *evFlag = nullptr;
256 
257     return status;
258 }
259 
addNanosecondsToCurrentTime(int64_t nanoSeconds,struct timespec * waitTime)260 void EventFlag::addNanosecondsToCurrentTime(int64_t nanoSeconds, struct timespec* waitTime) {
261     static constexpr int64_t kNanosPerSecond = 1000000000;
262 
263     clock_gettime(CLOCK_MONOTONIC, waitTime);
264     waitTime->tv_sec += nanoSeconds / kNanosPerSecond;
265     waitTime->tv_nsec += nanoSeconds % kNanosPerSecond;
266 
267     if (waitTime->tv_nsec >= kNanosPerSecond) {
268         waitTime->tv_sec++;
269         waitTime->tv_nsec -= kNanosPerSecond;
270     }
271 }
272 
~EventFlag()273 EventFlag::~EventFlag() {
274     unmapEventFlagWord(mEfWordPtr, &mEfWordNeedsUnmapping);
275 }
276 
277 }  // namespace hardware
278 }  // namespace android
279