1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <cerrno>
16 #include <climits>
17 #include <cstring>
18 #include <ctime>
19 #include <datetime_ex.h>
20 #include <hdf_base.h>
21 #include <hdf_log.h>
22 #include <base/hdi_smq_syncer.h>
23 #include <sys/syscall.h>
24 #include <unistd.h>
25 #include <linux/futex.h>
26
27 #define HDF_LOG_TAG smq_syncer
28
29 namespace OHOS {
30 namespace HDI {
31 namespace Base {
SharedMemQueueSyncer(std::atomic<uint32_t> * syncerPtr)32 SharedMemQueueSyncer::SharedMemQueueSyncer(std::atomic<uint32_t> *syncerPtr) : syncAddr_(syncerPtr)
33 {
34 }
35
Wait(uint32_t bitset,int64_t timeoutNanoSec)36 int SharedMemQueueSyncer::Wait(uint32_t bitset, int64_t timeoutNanoSec)
37 {
38 int ret;
39 while (true) {
40 ret = FutexWait(bitset, timeoutNanoSec);
41 if (ret == -EINTR || ret == -EAGAIN) {
42 HDF_LOGE("wait smq futex %{public}d,try again", ret);
43 continue;
44 }
45 break;
46 }
47
48 return ret;
49 }
50
FutexWait(uint32_t bitset,int64_t timeoutNanoSec)51 int SharedMemQueueSyncer::FutexWait(uint32_t bitset, int64_t timeoutNanoSec)
52 {
53 // clean wait bit
54 uint32_t syncWordOld = std::atomic_fetch_and(syncAddr_, ~bitset);
55 // if sync bit already set, not nedd futex wait
56 if (syncWordOld & bitset) {
57 return HDF_SUCCESS;
58 }
59 // futex will check sync word equal this expected val or not. If equal, sleep to wait, else return EAGAIN.
60 uint32_t valParm = syncWordOld & (~bitset);
61 int status;
62 if (timeoutNanoSec > 0) {
63 struct timespec waitTime;
64 TimeoutToRealtime(timeoutNanoSec, waitTime);
65 status = syscall(__NR_futex, syncAddr_, FUTEX_WAIT_BITSET, valParm, &waitTime, NULL, bitset);
66 } else {
67 status = syscall(__NR_futex, syncAddr_, FUTEX_WAIT_BITSET, valParm, NULL, NULL, bitset);
68 }
69 if (status == 0) {
70 syncWordOld = std::atomic_fetch_and(syncAddr_, ~bitset);
71 if ((syncWordOld & bitset) == 0) {
72 return -EINTR;
73 }
74
75 return status;
76 }
77 status = -errno;
78 if (status != ETIMEDOUT) {
79 HDF_LOGE("failed to wait smq futex, %{publich}d", status);
80 }
81 return status;
82 }
83
Wake(uint32_t bitset)84 int SharedMemQueueSyncer::Wake(uint32_t bitset)
85 {
86 uint32_t syncWordOld = std::atomic_fetch_or(syncAddr_, bitset);
87 // if sync bit already set, not nedd futex wake
88 if (syncWordOld & bitset) {
89 return HDF_SUCCESS;
90 }
91
92 int ret = syscall(__NR_futex, syncAddr_, FUTEX_WAKE_BITSET, INT_MAX, 0, 0, bitset);
93 if (ret < 0) {
94 HDF_LOGE("failed to wakeup smq futex, %{public}d", errno);
95 return -errno;
96 }
97
98 return HDF_SUCCESS;
99 }
100
TimeoutToRealtime(int64_t timeout,struct timespec & realtime)101 void SharedMemQueueSyncer::TimeoutToRealtime(int64_t timeout, struct timespec &realtime)
102 {
103 constexpr int64_t nano = SEC_TO_NANOSEC;
104
105 clock_gettime(CLOCK_MONOTONIC, &realtime);
106 int64_t timeoutNanoSec = timeout % nano;
107 int64_t timeoutSec = timeout / nano;
108
109 if (timeoutNanoSec + realtime.tv_nsec >= nano) {
110 realtime.tv_nsec = (timeoutNanoSec + realtime.tv_nsec) - nano;
111 realtime.tv_sec += timeoutSec + 1;
112 } else {
113 realtime.tv_nsec += timeoutNanoSec;
114 realtime.tv_sec += timeoutSec;
115 }
116 }
117 } // namespace Base
118 } // namespace HDI
119 } // namespace OHOS