xref: /third_party/mesa3d/src/util/libsync.h (revision bf215546)
1/*
2 *  sync abstraction
3 *  Copyright 2015-2016 Collabora Ltd.
4 *
5 *  Based on the implementation from the Android Open Source Project,
6 *
7 *  Copyright 2012 Google, Inc
8 *
9 *  Permission is hereby granted, free of charge, to any person obtaining a
10 *  copy of this software and associated documentation files (the "Software"),
11 *  to deal in the Software without restriction, including without limitation
12 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 *  and/or sell copies of the Software, and to permit persons to whom the
14 *  Software is furnished to do so, subject to the following conditions:
15 *
16 *  The above copyright notice and this permission notice shall be included in
17 *  all copies or substantial portions of the Software.
18 *
19 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 *  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 *  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 *  OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef _LIBSYNC_H
29#define _LIBSYNC_H
30
31#include <assert.h>
32#include <errno.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <string.h>
36#include <sys/ioctl.h>
37#include <sys/poll.h>
38#include <unistd.h>
39#include <time.h>
40
41#if defined(__cplusplus)
42extern "C" {
43#endif
44
45#ifdef ANDROID
46/* On Android, rely on the system's libsync instead of rolling our own
47 * sync_wait() and sync_merge().  This gives us compatibility with pre-4.7
48 * Android kernels.
49 */
50#include <android/sync.h>
51
52/**
53 * Check if the fd represents a valid fence-fd.
54 *
55 * The android variant of this debug helper is implemented on top of the
56 * system's libsync for compatibility with pre-4.7 android kernels.
57 */
58static inline bool
59sync_valid_fd(int fd)
60{
61	/* sync_file_info() only available in SDK 26. */
62#if ANDROID_API_LEVEL >= 26
63	struct sync_file_info *info = sync_file_info(fd);
64	if (!info)
65		return false;
66	sync_file_info_free(info);
67#endif
68	return true;
69}
70#else
71
72#ifndef SYNC_IOC_MERGE
73/* duplicated from linux/sync_file.h to avoid build-time dependency
74 * on new (v4.7) kernel headers.  Once distro's are mostly using
75 * something newer than v4.7 drop this and #include <linux/sync_file.h>
76 * instead.
77 */
78struct sync_merge_data {
79	char	name[32];
80	int32_t	fd2;
81	int32_t	fence;
82	uint32_t	flags;
83	uint32_t	pad;
84};
85
86struct sync_file_info {
87	char	name[32];
88	int32_t	status;
89	uint32_t	flags;
90	uint32_t	num_fences;
91	uint32_t	pad;
92
93	uint64_t	sync_fence_info;
94};
95
96#define SYNC_IOC_MAGIC		'>'
97#define SYNC_IOC_MERGE		_IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
98#define SYNC_IOC_FILE_INFO	_IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
99#endif
100
101
102static inline int sync_wait(int fd, int timeout)
103{
104	struct pollfd fds = {0};
105	int ret;
106	struct timespec poll_start, poll_end;
107
108	fds.fd = fd;
109	fds.events = POLLIN;
110
111	do {
112		clock_gettime(CLOCK_MONOTONIC, &poll_start);
113		ret = poll(&fds, 1, timeout);
114		clock_gettime(CLOCK_MONOTONIC, &poll_end);
115		if (ret > 0) {
116			if (fds.revents & (POLLERR | POLLNVAL)) {
117				errno = EINVAL;
118				return -1;
119			}
120			return 0;
121		} else if (ret == 0) {
122			errno = ETIME;
123			return -1;
124		}
125		timeout -= (poll_end.tv_sec - poll_start.tv_sec) * 1000 +
126			(poll_end.tv_nsec - poll_end.tv_nsec) / 1000000;
127	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
128
129	return ret;
130}
131
132static inline int sync_merge(const char *name, int fd1, int fd2)
133{
134	struct sync_merge_data data = {{0}};
135	int ret;
136
137	data.fd2 = fd2;
138	strncpy(data.name, name, sizeof(data.name));
139
140	do {
141		ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
142	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
143
144	if (ret < 0)
145		return ret;
146
147	return data.fence;
148}
149
150/**
151 * Check if the fd represents a valid fence-fd.
152 */
153static inline bool
154sync_valid_fd(int fd)
155{
156	struct sync_file_info info = {{0}};
157	return ioctl(fd, SYNC_IOC_FILE_INFO, &info) >= 0;
158}
159
160#endif /* !ANDROID */
161
162/* accumulate fd2 into fd1.  If *fd1 is not a valid fd then dup fd2,
163 * otherwise sync_merge() and close the old *fd1.  This can be used
164 * to implement the pattern:
165 *
166 *    init()
167 *    {
168 *       batch.fence_fd = -1;
169 *    }
170 *
171 *    // does *NOT* take ownership of fd
172 *    server_sync(int fd)
173 *    {
174 *       if (sync_accumulate("foo", &batch.fence_fd, fd)) {
175 *          ... error ...
176 *       }
177 *    }
178 */
179static inline int sync_accumulate(const char *name, int *fd1, int fd2)
180{
181	int ret;
182
183	assert(fd2 >= 0);
184
185	if (*fd1 < 0) {
186		*fd1 = dup(fd2);
187		return 0;
188	}
189
190	ret = sync_merge(name, *fd1, fd2);
191	if (ret < 0) {
192		/* leave *fd1 as it is */
193		return ret;
194	}
195
196	close(*fd1);
197	*fd1 = ret;
198
199	return 0;
200}
201
202/* Helper macro to complain if fd is non-negative and not a valid fence fd.
203 * Sprinkle this around to help catch fd lifetime issues.
204 */
205#ifdef DEBUG
206#  include "util/log.h"
207#  define validate_fence_fd(fd) do {                                         \
208      if (((fd) >= 0) && !sync_valid_fd(fd))                                 \
209         mesa_loge("%s:%d: invalid fence fd: %d", __func__, __LINE__, (fd)); \
210   } while (0)
211#else
212#  define validate_fence_fd(fd) do {} while (0)
213#endif
214
215#if defined(__cplusplus)
216}
217#endif
218
219#endif
220