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