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 void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
68
69 if (ptr == MAP_FAILED) {
70 native_handle_close(handle);
71 native_handle_delete(handle);
72 return nullptr;
73 }
74
75 std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
76 syncMem->mInit = true;
77 syncMem->mHandle = o;
78 syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
79 return syncMem;
80 }
81
Create(int fd,size_t size)82 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
83 if (fd < 0 || size == 0) {
84 return nullptr;
85 }
86 HandleSyncMem *handle = new HandleSyncMem(fd, size);
87
88 void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
89 if (ptr == MAP_FAILED) {
90 native_handle_close(handle);
91 native_handle_delete(handle);
92 return nullptr;
93 }
94 memset(ptr, 0, size);
95
96 std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
97 syncMem->mInit = true;
98 syncMem->mHandle = handle;
99 syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
100 return syncMem;
101 }
102
handle()103 native_handle_t *C2SurfaceSyncMemory::handle() {
104 return !mInit ? nullptr : mHandle;
105 }
106
mem()107 C2SyncVariables *C2SurfaceSyncMemory::mem() {
108 return !mInit ? nullptr : mMem;
109 }
110
111 namespace {
112 constexpr int kSpinNumForLock = 100;
113 constexpr int kSpinNumForUnlock = 200;
114
115 enum : uint32_t {
116 FUTEX_UNLOCKED = 0,
117 FUTEX_LOCKED_UNCONTENDED = 1, // user-space locking
118 FUTEX_LOCKED_CONTENDED = 2, // futex locking
119 };
120 }
121
lock()122 int C2SyncVariables::lock() {
123 uint32_t old;
124 for (int i = 0; i < kSpinNumForLock; i++) {
125 old = 0;
126 if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
127 return 0;
128 }
129 sched_yield();
130 }
131
132 if (old == FUTEX_LOCKED_UNCONTENDED)
133 old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
134
135 while (old) {
136 (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
137 old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
138 }
139 return 0;
140 }
141
unlock()142 int C2SyncVariables::unlock() {
143 if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0;
144
145 for (int i = 0; i < kSpinNumForUnlock; i++) {
146 if (mLock.load()) {
147 uint32_t old = FUTEX_LOCKED_UNCONTENDED;
148 mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
149 if (old) {
150 return 0;
151 }
152 }
153 sched_yield();
154 }
155
156 (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
157 return 0;
158 }
159
setInitialDequeueCountLocked(int32_t maxDequeueCount,int32_t curDequeueCount)160 void C2SyncVariables::setInitialDequeueCountLocked(
161 int32_t maxDequeueCount, int32_t curDequeueCount) {
162 mMaxDequeueCount = maxDequeueCount;
163 mCurDequeueCount = curDequeueCount;
164 }
165
getWaitIdLocked()166 uint32_t C2SyncVariables::getWaitIdLocked() {
167 return mCond.load();
168 }
169
isDequeueableLocked(uint32_t * waitId)170 bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
171 if (mMaxDequeueCount <= mCurDequeueCount) {
172 if (waitId) {
173 *waitId = getWaitIdLocked();
174 }
175 return false;
176 }
177 return true;
178 }
179
notifyQueuedLocked(uint32_t * waitId)180 bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId) {
181 // Note. thundering herds may occur. Edge trigged signalling.
182 // But one waiter will guarantee to dequeue. others may wait again.
183 // Minimize futex syscall(trap) for the main use case(one waiter case).
184 if (mMaxDequeueCount == mCurDequeueCount--) {
185 broadcast();
186 return true;
187 }
188
189 if (mCurDequeueCount >= mMaxDequeueCount) {
190 if (waitId) {
191 *waitId = getWaitIdLocked();
192 }
193 ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
194 return false;
195 }
196 return true;
197 }
198
notifyDequeuedLocked()199 void C2SyncVariables::notifyDequeuedLocked() {
200 mCurDequeueCount++;
201 ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
202 }
203
setSyncStatusLocked(SyncStatus status)204 void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
205 mStatus = status;
206 if (mStatus == STATUS_ACTIVE) {
207 broadcast();
208 }
209 }
210
getSyncStatusLocked()211 C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
212 return mStatus;
213 }
214
updateMaxDequeueCountLocked(int32_t maxDequeueCount)215 void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
216 mMaxDequeueCount = maxDequeueCount;
217 if (mStatus == STATUS_ACTIVE) {
218 broadcast();
219 }
220 }
221
waitForChange(uint32_t waitId,c2_nsecs_t timeoutNs)222 c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
223 if (timeoutNs < 0) {
224 timeoutNs = 0;
225 }
226 struct timespec tv;
227 tv.tv_sec = timeoutNs / 1000000000;
228 tv.tv_nsec = timeoutNs % 1000000000;
229
230 int ret = syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
231 if (ret == 0 || ret == EAGAIN) {
232 return C2_OK;
233 }
234 if (ret == EINTR || ret == ETIMEDOUT) {
235 return C2_TIMED_OUT;
236 }
237 return C2_BAD_VALUE;
238 }
239
signal()240 int C2SyncVariables::signal() {
241 mCond++;
242
243 (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
244 return 0;
245 }
246
broadcast()247 int C2SyncVariables::broadcast() {
248 mCond++;
249
250 (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
251 return 0;
252 }
253
wait()254 int C2SyncVariables::wait() {
255 uint32_t old = mCond.load();
256 unlock();
257
258 (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
259 while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
260 (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
261 }
262 return 0;
263 }
264