• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 100;
118     constexpr int kSpinNumForUnlock = 200;
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;
129     for (int i = 0; i < kSpinNumForLock; i++) {
130         old = 0;
131         if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
132             return 0;
133         }
134         sched_yield();
135     }
136 
137     if (old == FUTEX_LOCKED_UNCONTENDED)
138         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
139 
140     while (old) {
141         (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
142         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
143     }
144     return 0;
145 }
146 
unlock()147 int C2SyncVariables::unlock() {
148     if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0;
149 
150     for (int i = 0; i < kSpinNumForUnlock; i++) {
151         if (mLock.load()) {
152             uint32_t old = FUTEX_LOCKED_UNCONTENDED;
153             mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
154             if (old) {
155                 return 0;
156             }
157         }
158         sched_yield();
159     }
160 
161     (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
162     return 0;
163 }
164 
setInitialDequeueCountLocked(int32_t maxDequeueCount,int32_t curDequeueCount)165 void C2SyncVariables::setInitialDequeueCountLocked(
166         int32_t maxDequeueCount, int32_t curDequeueCount) {
167     mMaxDequeueCount = maxDequeueCount;
168     mCurDequeueCount = curDequeueCount;
169 }
170 
getWaitIdLocked()171 uint32_t C2SyncVariables::getWaitIdLocked() {
172     return mCond.load();
173 }
174 
isDequeueableLocked(uint32_t * waitId)175 bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
176     if (mMaxDequeueCount <= mCurDequeueCount) {
177         if (waitId) {
178             *waitId = getWaitIdLocked();
179         }
180         return false;
181     }
182     return true;
183 }
184 
notifyQueuedLocked(uint32_t * waitId,bool notify)185 bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId, bool notify) {
186     // Note. thundering herds may occur. Edge trigged signalling.
187     // But one waiter will guarantee to dequeue. others may wait again.
188     // Minimize futex syscall(trap) for the main use case(one waiter case).
189     if (mMaxDequeueCount == mCurDequeueCount--) {
190         if (notify) {
191             broadcast();
192         }
193         return true;
194     }
195 
196     if (mCurDequeueCount >= mMaxDequeueCount) {
197         if (waitId) {
198             *waitId = getWaitIdLocked();
199         }
200         ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
201         return false;
202     }
203     return true;
204 }
205 
notifyDequeuedLocked()206 void C2SyncVariables::notifyDequeuedLocked() {
207     mCurDequeueCount++;
208     ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
209 }
210 
setSyncStatusLocked(SyncStatus status)211 void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
212     mStatus = status;
213     if (mStatus == STATUS_ACTIVE) {
214         broadcast();
215     }
216 }
217 
getSyncStatusLocked()218 C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
219     return mStatus;
220 }
221 
updateMaxDequeueCountLocked(int32_t maxDequeueCount)222 void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
223     mMaxDequeueCount = maxDequeueCount;
224     if (mStatus == STATUS_ACTIVE) {
225         broadcast();
226     }
227 }
228 
waitForChange(uint32_t waitId,c2_nsecs_t timeoutNs)229 c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
230     if (timeoutNs < 0) {
231         timeoutNs = 0;
232     }
233     struct timespec tv;
234     tv.tv_sec = timeoutNs / 1000000000;
235     tv.tv_nsec = timeoutNs % 1000000000;
236 
237     int ret =  syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
238     if (ret == 0 || errno == EAGAIN) {
239         return C2_OK;
240     }
241     if (errno == EINTR || errno == ETIMEDOUT) {
242         return C2_TIMED_OUT;
243     }
244     return C2_BAD_VALUE;
245 }
246 
signal()247 int C2SyncVariables::signal() {
248     mCond++;
249 
250     (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
251     return 0;
252 }
253 
broadcast()254 int C2SyncVariables::broadcast() {
255     mCond++;
256 
257     (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
258     return 0;
259 }
260 
wait()261 int C2SyncVariables::wait() {
262     uint32_t old = mCond.load();
263     unlock();
264 
265     (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
266     while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
267         (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
268     }
269     return 0;
270 }
271