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