1/*
2 * Copyright (c) 2021-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
16#include "sync_fence.h"
17
18#include <cstddef>
19#include <cstdint>
20#include <cstdlib>
21#include <unistd.h>
22#include <errno.h>
23#include <limits>
24#include <securec.h>
25#include <fcntl.h>
26#include <sys/poll.h>
27#include <linux/sync_file.h>
28#include <sys/ioctl.h>
29#include "hilog/log.h"
30
31namespace OHOS {
32using namespace OHOS::HiviewDFX;
33
34namespace {
35#undef LOG_DOMAIN
36#define LOG_DOMAIN 0xD001400
37#undef LOG_TAG
38#define LOG_TAG "SyncFence"
39
40#define B_CPRINTF(func, fmt, ...) \
41    func(LOG_CORE, "<%{public}d>%{public}s: " fmt, \
42        __LINE__, __func__, ##__VA_ARGS__)
43
44#define UTILS_LOGD(fmt, ...) B_CPRINTF(HILOG_DEBUG, fmt, ##__VA_ARGS__)
45#define UTILS_LOGI(fmt, ...) B_CPRINTF(HILOG_INFO, fmt, ##__VA_ARGS__)
46#define UTILS_LOGW(fmt, ...) B_CPRINTF(HILOG_WARN, fmt, ##__VA_ARGS__)
47#define UTILS_LOGE(fmt, ...) B_CPRINTF(HILOG_ERROR, fmt, ##__VA_ARGS__)
48
49constexpr int32_t INVALID_FD = -1;
50constexpr uint32_t MAX_FENCE_NUM = 65535;
51}  // namespace
52
53const sptr<SyncFence> SyncFence::INVALID_FENCE = sptr<SyncFence>(new SyncFence(INVALID_FD));
54const ns_sec_t SyncFence::INVALID_TIMESTAMP = -1;
55const ns_sec_t SyncFence::FENCE_PENDING_TIMESTAMP = INT64_MAX;
56
57SyncFence::SyncFence(int32_t fenceFd) : fenceFd_(fenceFd)
58{
59}
60
61SyncFence::~SyncFence()
62{
63}
64
65int32_t SyncFence::Wait(uint32_t timeout)
66{
67    int retCode = -1;
68    if (fenceFd_ < 0) {
69        return retCode;
70    }
71
72    struct pollfd pollfds = {0};
73    pollfds.fd = fenceFd_;
74    pollfds.events = POLLIN;
75
76    do {
77        retCode = poll(&pollfds, 1, timeout);
78    } while (retCode == -1 && (errno == EINTR || errno == EAGAIN));
79
80    if (retCode == 0) {
81        retCode = -1;
82        errno = ETIME;
83    } else if (retCode > 0) {
84        retCode = 0;
85        if (pollfds.revents & (POLLERR | POLLNVAL)) {
86            retCode = -1;
87            errno = EINVAL;
88        }
89    }
90
91    return retCode < 0 ? -errno : 0;
92}
93
94int32_t SyncFence::SyncMerge(const char *name, int32_t fd1, int32_t fd2, int32_t &newFenceFd)
95{
96    struct sync_merge_data syncMergeData = {};
97    syncMergeData.fd2 = fd2;
98    if (strcpy_s(syncMergeData.name, sizeof(syncMergeData.name), name)) {
99        UTILS_LOGE("SyncMerge ctrcpy fence name failed.");
100        return -1;
101    }
102    int32_t retCode = ioctl(fd1, SYNC_IOC_MERGE, &syncMergeData);
103    if (retCode < 0) {
104        errno = EINVAL;
105        UTILS_LOGE("Fence merge failed, errno: %{public}d, ret: %{public}d.", errno, retCode);
106        return -1;
107    }
108
109    newFenceFd = syncMergeData.fence;
110    return 0;
111}
112
113sptr<SyncFence> SyncFence::MergeFence(const std::string &name,
114                                      const sptr<SyncFence>& fence1, const sptr<SyncFence>& fence2)
115{
116    int32_t newFenceFd = INVALID_FD;
117    int32_t fenceFd1 = fence1->fenceFd_;
118    int32_t fenceFd2 = fence2->fenceFd_;
119
120    if (fenceFd1 >= 0 && fenceFd2 >= 0) {
121        (void)SyncFence::SyncMerge(name.c_str(), fenceFd1, fenceFd2, newFenceFd);
122    } else if (fenceFd1 >= 0) {
123        (void)SyncFence::SyncMerge(name.c_str(), fenceFd1, fenceFd1, newFenceFd);
124    } else if (fenceFd2 >= 0) {
125        (void)SyncFence::SyncMerge(name.c_str(), fenceFd2, fenceFd2, newFenceFd);
126    } else {
127        return INVALID_FENCE;
128    }
129
130    if (newFenceFd == INVALID_FD) {
131        UTILS_LOGE("sync_merge(%{public}s) failed, error: %{public}s (%{public}d)",
132                     name.c_str(), strerror(errno), errno);
133        return INVALID_FENCE;
134    }
135
136    return sptr<SyncFence>(new SyncFence(newFenceFd));
137}
138
139ns_sec_t SyncFence::SyncFileReadTimestamp()
140{
141    std::vector<SyncPointInfo> ptInfos = GetFenceInfo();
142    if (ptInfos.empty()) {
143        return FENCE_PENDING_TIMESTAMP;
144    }
145    size_t signalFenceCount = 0;
146    for (const auto &info : ptInfos) {
147        if (info.status == SIGNALED) {
148            signalFenceCount++;
149        }
150    }
151    if (signalFenceCount == ptInfos.size()) {
152        // fence signaled
153        uint64_t timestamp = 0;
154        for (const auto &ptInfo : ptInfos) {
155            if (ptInfo.timestampNs > timestamp) {
156                timestamp = ptInfo.timestampNs;
157            }
158        }
159        return static_cast<ns_sec_t>(timestamp);
160    } else {
161        // fence still active
162        return FENCE_PENDING_TIMESTAMP;
163    }
164}
165
166std::vector<SyncPointInfo> SyncFence::GetFenceInfo()
167{
168    struct sync_file_info arg;
169    struct sync_file_info *infoPtr = nullptr;
170
171    errno_t retCode = memset_s(&arg, sizeof(struct sync_file_info), 0, sizeof(struct sync_file_info));
172    if (retCode != EOK) {
173        UTILS_LOGE("memset_s error, retCode = %{public}d", retCode);
174        return {};
175    }
176    int32_t ret = ioctl(fenceFd_, SYNC_IOC_FILE_INFO, &arg);
177    if (ret < 0) {
178        UTILS_LOGD("GetFenceInfo SYNC_IOC_FILE_INFO ioctl failed, ret: %{public}d", ret);
179        return {};
180    }
181    if (arg.num_fences <= 0 || arg.num_fences > MAX_FENCE_NUM) {
182        UTILS_LOGD("GetFenceInfo arg.num_fences failed, num_fences: %{public}d", arg.num_fences);
183        return {};
184    }
185    auto sizeMax = std::numeric_limits<size_t>::max();
186    if ((sizeMax - sizeof(struct sync_file_info)) / sizeof(struct sync_fence_info) < arg.num_fences) {
187        UTILS_LOGE("GetFenceInfo overflow, num_fences: %{public}d", arg.num_fences);
188        return {};
189    }
190    size_t syncFileInfoMemSize = sizeof(struct sync_file_info) + sizeof(struct sync_fence_info) * arg.num_fences;
191    infoPtr = (struct sync_file_info *)malloc(syncFileInfoMemSize);
192    if (infoPtr == nullptr) {
193        UTILS_LOGD("GetFenceInfo malloc failed oom");
194        return {};
195    }
196    retCode = memset_s(infoPtr, syncFileInfoMemSize, 0, syncFileInfoMemSize);
197    if (retCode != 0) {
198        UTILS_LOGE("memset_s error, retCode = %{public}d", retCode);
199        free(infoPtr);
200        return {};
201    }
202    infoPtr->num_fences = arg.num_fences;
203    infoPtr->sync_fence_info = static_cast<uint64_t>(uintptr_t(infoPtr + 1));
204    ret = ioctl(fenceFd_, SYNC_IOC_FILE_INFO, infoPtr);
205    if (ret < 0) {
206        UTILS_LOGE("GetTotalFenceInfo SYNC_IOC_FILE_INFO ioctl failed, ret: %{public}d", ret);
207        free(infoPtr);
208        return {};
209    }
210    std::vector<SyncPointInfo> infos;
211    const auto fenceInfos = (struct sync_fence_info *)(uintptr_t)(infoPtr->sync_fence_info);
212    for (uint32_t i = 0; i < infoPtr->num_fences; i++) {
213        infos.push_back(SyncPointInfo { fenceInfos[i].timestamp_ns, static_cast<FenceStatus>(fenceInfos[i].status) });
214    }
215    free(infoPtr);
216    return infos;
217}
218
219int32_t SyncFence::Dup() const
220{
221    return ::dup(fenceFd_);
222}
223
224FenceStatus SyncFence::GetStatus()
225{
226    if (fenceFd_ < 0) {
227        return ERROR;
228    }
229    std::vector<SyncPointInfo> ptInfos = GetFenceInfo();
230    if (ptInfos.empty()) {
231        return ERROR;
232    }
233    size_t signalFenceCount = 0;
234    for (const auto &info : ptInfos) {
235        if (info.status == SIGNALED) {
236            signalFenceCount++;
237        }
238    }
239    if (signalFenceCount == ptInfos.size()) {
240        return SIGNALED;
241    } else {
242        return ACTIVE;
243    }
244}
245
246int32_t SyncFence::Get() const
247{
248    return fenceFd_;
249}
250
251bool SyncFence::IsValid() const
252{
253    return fenceFd_ != -1;
254}
255
256sptr<SyncFence> SyncFence::ReadFromMessageParcel(MessageParcel &parcel)
257{
258    int32_t fence = parcel.ReadInt32();
259    if (fence < 0) {
260        return INVALID_FENCE;
261    }
262
263    fence = parcel.ReadFileDescriptor();
264
265    return sptr<SyncFence>(new SyncFence(fence));
266}
267
268sptr<SyncFence> SyncFence::InvalidFence()
269{
270    return sptr<SyncFence>(new SyncFence(-1));
271}
272
273bool SyncFence::WriteToMessageParcel(MessageParcel &parcel)
274{
275    int32_t fence = fenceFd_;
276    if (fence >= 0 && fcntl(fence, F_GETFL) == -1 && errno == EBADF) {
277        fence = -1;
278    }
279
280    if (!parcel.WriteInt32(fence)) {
281        return false;
282    }
283
284    if (fence < 0) {
285        UTILS_LOGD("WriteToMessageParcel fence is invalid : %{public}d", fence);
286        return true;
287    }
288
289    if (!parcel.WriteFileDescriptor(fence)) {
290        return false;
291    }
292    return true;
293}
294
295} // namespace OHOS
296