1 /*
2 * Copyright (C) 2021 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_NDEBUG 0
18 #define LOG_TAG "C2SurfaceSyncObj"
19 #include <limits.h>
20 #include <linux/futex.h>
21 #include <sys/mman.h>
22 #include <sys/syscall.h>
23 #include <sys/time.h>
24 #include <utils/Log.h>
25
26 #include <chrono>
27 #include <C2SurfaceSyncObj.h>
28
29 const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
30 C2SurfaceSyncMemory::HandleSyncMem::version,
31 C2SurfaceSyncMemory::HandleSyncMem::numFds,
32 C2SurfaceSyncMemory::HandleSyncMem::numInts,
33 {}
34 };
35
isValid(const native_handle_t * const o)36 bool C2SurfaceSyncMemory::HandleSyncMem::isValid(const native_handle_t * const o) {
37 if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
38 return false;
39 }
40
41 const HandleSyncMem *other = static_cast<const HandleSyncMem*>(o);
42 return other->mInts.mMagic == kMagic;
43 }
44
C2SurfaceSyncMemory()45 C2SurfaceSyncMemory::C2SurfaceSyncMemory()
46 : mInit(false), mHandle(nullptr), mMem(nullptr) {}
47
~C2SurfaceSyncMemory()48 C2SurfaceSyncMemory::~C2SurfaceSyncMemory() {
49 if (mInit) {
50 if (mMem) {
51 munmap(static_cast<void *>(mMem), mHandle->size());
52 }
53 if (mHandle) {
54 native_handle_close(mHandle);
55 native_handle_delete(mHandle);
56 }
57 }
58 }
59
Import(native_handle_t * handle)60 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Import(
61 native_handle_t *handle) {
62 if (!HandleSyncMem::isValid(handle)) {
63 return nullptr;
64 }
65
66 HandleSyncMem *o = static_cast<HandleSyncMem*>(handle);
67 if (o->size() < sizeof(C2SyncVariables)) {
68 android_errorWriteLog(0x534e4554, "240140929");
69 return nullptr;
70 }
71
72 void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
73
74 if (ptr == MAP_FAILED) {
75 native_handle_close(handle);
76 native_handle_delete(handle);
77 return nullptr;
78 }
79
80 std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
81 syncMem->mInit = true;
82 syncMem->mHandle = o;
83 syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
84 return syncMem;
85 }
86
Create(int fd,size_t size)87 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
88 if (fd < 0 || size == 0) {
89 return nullptr;
90 }
91 HandleSyncMem *handle = new HandleSyncMem(fd, size);
92
93 void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
94 if (ptr == MAP_FAILED) {
95 native_handle_close(handle);
96 native_handle_delete(handle);
97 return nullptr;
98 }
99 memset(ptr, 0, size);
100
101 std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
102 syncMem->mInit = true;
103 syncMem->mHandle = handle;
104 syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
105 return syncMem;
106 }
107
handle()108 native_handle_t *C2SurfaceSyncMemory::handle() {
109 return !mInit ? nullptr : mHandle;
110 }
111
mem()112 C2SyncVariables *C2SurfaceSyncMemory::mem() {
113 return !mInit ? nullptr : mMem;
114 }
115
116 namespace {
117 constexpr int kSpinNumForLock = 0;
118 constexpr int kSpinNumForUnlock = 0;
119
120 enum : uint32_t {
121 FUTEX_UNLOCKED = 0,
122 FUTEX_LOCKED_UNCONTENDED = 1, // user-space locking
123 FUTEX_LOCKED_CONTENDED = 2, // futex locking
124 };
125 }
126
lock()127 int C2SyncVariables::lock() {
128 uint32_t old = FUTEX_UNLOCKED;
129
130 // see if we can lock uncontended immediately (if previously unlocked)
131 if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
132 return 0;
133 }
134
135 // spin to see if we can get it with a short wait without involving kernel
136 for (int i = 0; i < kSpinNumForLock; i++) {
137 sched_yield();
138
139 old = FUTEX_UNLOCKED;
140 if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
141 return 0;
142 }
143 }
144
145 // still locked, if other side thinks it was uncontended, now it is contended, so let them
146 // know that they need to wake us up.
147 if (old == FUTEX_LOCKED_UNCONTENDED) {
148 old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
149 // It is possible that the other holder released the lock at this very moment (and old
150 // becomes UNLOCKED), If so, we will not involve the kernel to wait for the lock to be
151 // released, but are still marking our lock contended (even though we are the only
152 // holders.)
153 }
154
155 // while the futex is still locked by someone else
156 while (old != FUTEX_UNLOCKED) {
157 // wait until other side releases the lock (and still contented)
158 (void)syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
159 // try to relock
160 old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
161 }
162 return 0;
163 }
164
unlock()165 int C2SyncVariables::unlock() {
166 // TRICKY: here we assume that we are holding this lock
167
168 // unlock the lock immediately (since we were holding it)
169 // If it is (still) locked uncontested, we are done (no need to involve the kernel)
170 if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) {
171 return 0;
172 }
173
174 // We don't need to spin for unlock as here we know already we have a waiter who we need to
175 // wake up. This code was here in case someone just happened to lock this lock (uncontested)
176 // before we would wake up other waiters to avoid a syscall. It is unsure if this ever gets
177 // exercised or if this is the behavior we want. (Note that if this code is removed, the same
178 // situation is still handled in lock() by the woken up waiter that realizes that the lock is
179 // now taken.)
180 for (int i = 0; i < kSpinNumForUnlock; i++) {
181 // here we seem to check if someone relocked this lock, and if they relocked uncontested,
182 // we up it to contested (since there are other waiters.)
183 if (mLock.load() != FUTEX_UNLOCKED) {
184 uint32_t old = FUTEX_LOCKED_UNCONTENDED;
185 mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
186 // this is always true here so we return immediately
187 if (old) {
188 return 0;
189 }
190 }
191 sched_yield();
192 }
193
194 // wake up one waiter
195 (void)syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
196 return 0;
197 }
198
setInitialDequeueCountLocked(int32_t maxDequeueCount,int32_t curDequeueCount)199 void C2SyncVariables::setInitialDequeueCountLocked(
200 int32_t maxDequeueCount, int32_t curDequeueCount) {
201 mMaxDequeueCount = maxDequeueCount;
202 mCurDequeueCount = curDequeueCount;
203 }
204
getWaitIdLocked()205 uint32_t C2SyncVariables::getWaitIdLocked() {
206 return mCond.load();
207 }
208
isDequeueableLocked(uint32_t * waitId)209 bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
210 if (mMaxDequeueCount <= mCurDequeueCount) {
211 if (waitId) {
212 *waitId = getWaitIdLocked();
213 }
214 return false;
215 }
216 return true;
217 }
218
notifyQueuedLocked(uint32_t * waitId,bool notify)219 bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId, bool notify) {
220 // Note. thundering herds may occur. Edge trigged signalling.
221 // But one waiter will guarantee to dequeue. others may wait again.
222 // Minimize futex syscall(trap) for the main use case(one waiter case).
223 if (mMaxDequeueCount == mCurDequeueCount--) {
224 if (notify) {
225 broadcast();
226 }
227 return true;
228 }
229
230 if (mCurDequeueCount >= mMaxDequeueCount) {
231 if (waitId) {
232 *waitId = getWaitIdLocked();
233 }
234 ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
235 return false;
236 }
237 return true;
238 }
239
notifyDequeuedLocked()240 void C2SyncVariables::notifyDequeuedLocked() {
241 mCurDequeueCount++;
242 ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
243 }
244
setSyncStatusLocked(SyncStatus status)245 void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
246 mStatus = status;
247 if (mStatus == STATUS_ACTIVE) {
248 broadcast();
249 }
250 }
251
getSyncStatusLocked()252 C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
253 return mStatus;
254 }
255
updateMaxDequeueCountLocked(int32_t maxDequeueCount)256 void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
257 mMaxDequeueCount = maxDequeueCount;
258 if (mStatus == STATUS_ACTIVE) {
259 broadcast();
260 }
261 }
262
waitForChange(uint32_t waitId,c2_nsecs_t timeoutNs)263 c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
264 if (timeoutNs < 0) {
265 timeoutNs = 0;
266 }
267 struct timespec tv;
268 tv.tv_sec = timeoutNs / 1000000000;
269 tv.tv_nsec = timeoutNs % 1000000000;
270
271 int ret = syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
272 if (ret == 0 || errno == EAGAIN) {
273 return C2_OK;
274 }
275 if (errno == EINTR || errno == ETIMEDOUT) {
276 return C2_TIMED_OUT;
277 }
278 return C2_BAD_VALUE;
279 }
280
notifyAll()281 void C2SyncVariables::notifyAll() {
282 this->lock();
283 this->broadcast();
284 this->unlock();
285 }
286
signal()287 int C2SyncVariables::signal() {
288 mCond++;
289
290 (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
291 return 0;
292 }
293
broadcast()294 int C2SyncVariables::broadcast() {
295 mCond++;
296
297 (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
298 return 0;
299 }
300
wait()301 int C2SyncVariables::wait() {
302 uint32_t old = mCond.load();
303 unlock();
304
305 (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
306 while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
307 (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
308 }
309 return 0;
310 }
311