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