1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
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 "System/Debug.hpp"
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <string.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22 
23 #ifndef __APPLE__
24 #	error "This file is for macOS only!"
25 #endif  // __APPLE__
26 
27 #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12
28 #	include <mach/mach_time.h>
29 #endif  // __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12
30 
31 namespace {
32 
GetTime()33 struct timespec GetTime()
34 {
35 	struct timespec tv;
36 
37 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12
38 	clock_gettime(CLOCK_REALTIME, &tv);
39 #else
40 	mach_timebase_info_data_t timebase;
41 	mach_timebase_info(&timebase);
42 	uint64_t time;
43 	time = mach_absolute_time();
44 
45 	double convert_ratio = (double)timebase.numer / (double)timebase.denom;
46 	uint64_t secs = (uint64_t)((double)time * convert_ratio / 1e-9);
47 	uint64_t usecs = (uint64_t)((double)time * convert_ratio - secs * 1e9);
48 	tv.tv_sec = secs;
49 	tv.tv_nsec = usecs;
50 #endif
51 	return tv;
52 }
53 
54 }  // namespace
55 
56 // An implementation of OpaqueFdExternalMemory that relies on shm_open().
57 // Useful on OS X which do not have Linux memfd regions.
58 class OpaqueFdExternalMemory : public vk::DeviceMemory::ExternalBase
59 {
60 public:
61 	static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
62 
SupportsAllocateInfo(const VkMemoryAllocateInfo * pAllocateInfo)63 	static bool SupportsAllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo)
64 	{
65 		OpaqueFdAllocateInfo info(pAllocateInfo);
66 		return info.importFd || info.exportFd;
67 	}
68 
OpaqueFdExternalMemory(const VkMemoryAllocateInfo * pAllocateInfo)69 	explicit OpaqueFdExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo)
70 	    : allocateInfo(pAllocateInfo)
71 	{
72 	}
73 
~OpaqueFdExternalMemory()74 	~OpaqueFdExternalMemory()
75 	{
76 		if(shm_fd_ >= 0)
77 		{
78 			::close(shm_fd_);
79 			shm_fd_ = -1;
80 		}
81 	}
82 
allocate(size_t size,void ** pBuffer)83 	VkResult allocate(size_t size, void **pBuffer) override
84 	{
85 		if(allocateInfo.importFd)
86 		{
87 			shm_fd_ = allocateInfo.fd;
88 			if(shm_fd_ < 0)
89 			{
90 				return VK_ERROR_INVALID_EXTERNAL_HANDLE;
91 			}
92 		}
93 		else
94 		{
95 			ASSERT(allocateInfo.exportFd);
96 			// Create shared memory region with shm_open() and a randomly-generated region name.
97 			static const char kPrefix[] = "/SwiftShader-";
98 			const size_t kPrefixSize = sizeof(kPrefix) - 1;
99 			const size_t kRandomSize = 8;
100 
101 			char name[kPrefixSize + kRandomSize + 1u];
102 			memcpy(name, kPrefix, kPrefixSize);
103 
104 			int fd = -1;
105 			for(int tries = 0; tries < 6; ++tries)
106 			{
107 				struct timespec tv = GetTime();
108 				uint64_t r = (uint64_t)tv.tv_sec + (uint64_t)tv.tv_nsec;
109 				for(size_t pos = 0; pos < kRandomSize; ++pos, r /= 8)
110 				{
111 					name[kPrefixSize + pos] = '0' + (r % 8);
112 				}
113 				name[kPrefixSize + kRandomSize] = '\0';
114 
115 				fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
116 				if(fd >= 0)
117 					break;
118 
119 				if(errno != EEXIST)
120 				{
121 					TRACE("shm_open() failed with: %s", strerror(errno));
122 					break;
123 				}
124 			}
125 
126 			// Unlink the name since it's not needed anymore.
127 			if(fd >= 0)
128 			{
129 				if(shm_unlink(name) == -1)
130 				{
131 					TRACE("shm_unlink() failed with: %s", strerror(errno));
132 					close(fd);
133 					fd = -1;
134 				}
135 			}
136 
137 			// Ensure there is enough space.
138 			if(fd >= 0 && size > 0)
139 			{
140 				if(::ftruncate(fd, size) < 0)
141 				{
142 					TRACE("ftruncate() failed with: %s", strerror(errno));
143 					close(fd);
144 					fd = -1;
145 				}
146 			}
147 
148 			if(fd < 0)
149 			{
150 				TRACE("Could not allocate shared memory region");
151 				return VK_ERROR_OUT_OF_DEVICE_MEMORY;
152 			}
153 
154 			shm_fd_ = fd;
155 		}
156 
157 		void *addr = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
158 		                    shm_fd_, 0);
159 
160 		if(addr == MAP_FAILED)
161 		{
162 			return VK_ERROR_MEMORY_MAP_FAILED;
163 		}
164 		*pBuffer = addr;
165 		return VK_SUCCESS;
166 	}
167 
deallocate(void * buffer,size_t size)168 	void deallocate(void *buffer, size_t size) override
169 	{
170 		::munmap(buffer, size);
171 	}
172 
getFlagBit() const173 	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
174 	{
175 		return typeFlagBit;
176 	}
177 
exportFd(int * pFd) const178 	VkResult exportFd(int *pFd) const override
179 	{
180 		int fd = dup(shm_fd_);
181 		if(fd < 0)
182 		{
183 			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
184 		}
185 
186 		// Set the clo-on-exec flag.
187 		int flags = ::fcntl(fd, F_GETFD);
188 		::fcntl(fd, F_SETFL, flags | FD_CLOEXEC);
189 
190 		*pFd = fd;
191 		return VK_SUCCESS;
192 	}
193 
194 private:
195 	int shm_fd_ = -1;
196 	OpaqueFdAllocateInfo allocateInfo;
197 };
198