1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 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 16 #ifndef UTIL_SLAB_HPP 17 #define UTIL_SLAB_HPP 18 19 #include <new> 20 #include <vector> 21 #include <mutex> 22 #ifdef FFRT_BBOX_ENABLE 23 #include <unordered_set> 24 #endif 25 #include <sys/mman.h> 26 #include "sync/sync.h" 27 #include "dfx/log/ffrt_log_api.h" 28 29 namespace ffrt { 30 const std::size_t BatchAllocSize = 32 * 1024; 31 #ifdef FFRT_BBOX_ENABLE 32 constexpr uint32_t ALLOCATOR_DESTRUCT_TIMESOUT = 1000; 33 #endif 34 35 template <typename T, size_t MmapSz = BatchAllocSize> 36 class SimpleAllocator { 37 public: 38 SimpleAllocator(const SimpleAllocator&) = delete; 39 SimpleAllocator(SimpleAllocator&&) = delete; 40 SimpleAllocator& operator=(const SimpleAllocator&) = delete; 41 SimpleAllocator& operator=(SimpleAllocator&&) = delete; 42 fast_mutex lock; 43 44 static SimpleAllocator<T>* Instance(std::size_t size = sizeof(T)) 45 { 46 static SimpleAllocator<T> ins(size); 47 return &ins; 48 } 49 50 // NOTE: call constructor after AllocMem AllocMem()51 static T* AllocMem() 52 { 53 return Instance()->Alloc(); 54 } 55 56 // NOTE: call destructor before FreeMem FreeMem(T * t)57 static void FreeMem(T* t) 58 { 59 t->~T(); 60 // unlock()内部lck记录锁的状态为非持有状态,析构时访问状态变量为非持有状态,则不访问实际持有的mutex 61 // return之前的lck析构不产生UAF问题,因为return之前随着root析构,锁的内存被释放 62 Instance()->free(t); 63 } 64 65 // only used for BBOX getUnfreedMem()66 static std::vector<void *> getUnfreedMem() 67 { 68 return Instance()->getUnfreed(); 69 } 70 LockMem()71 static void LockMem() 72 { 73 return Instance()->SimpleAllocatorLock(); 74 } 75 UnlockMem()76 static void UnlockMem() 77 { 78 return Instance()->SimpleAllocatorUnLock(); 79 } 80 private: 81 std::vector<T*> primaryCache; 82 #ifdef FFRT_BBOX_ENABLE 83 std::unordered_set<T*> secondaryCache; 84 #endif 85 std::size_t TSize; 86 T* basePtr = nullptr; 87 std::size_t count = 0; 88 getUnfreed()89 std::vector<void *> getUnfreed() 90 { 91 std::vector<void *> ret; 92 #ifdef FFRT_BBOX_ENABLE 93 ret.reserve(MmapSz / TSize + secondaryCache.size()); 94 char* p = reinterpret_cast<char*>(basePtr); 95 for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) { 96 if (basePtr != nullptr && 97 std::find(primaryCache.begin(), primaryCache.end(), 98 reinterpret_cast<T*>(p + i)) == primaryCache.end()) { 99 ret.push_back(reinterpret_cast<void *>(p + i)); 100 } 101 } 102 for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) { 103 ret.push_back(reinterpret_cast<void *>(*ite)); 104 } 105 #endif 106 return ret; 107 } 108 SimpleAllocatorLock()109 void SimpleAllocatorLock() 110 { 111 lock.lock(); 112 } 113 SimpleAllocatorUnLock()114 void SimpleAllocatorUnLock() 115 { 116 lock.unlock(); 117 } 118 init()119 void init() 120 { 121 char* p = reinterpret_cast<char*>(operator new(MmapSz)); 122 count = MmapSz / TSize; 123 primaryCache.reserve(count); 124 for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) { 125 primaryCache.push_back(reinterpret_cast<T*>(p + i)); 126 } 127 basePtr = reinterpret_cast<T*>(p); 128 } 129 Alloc()130 T* Alloc() 131 { 132 lock.lock(); 133 T* t = nullptr; 134 if (count == 0) { 135 if (basePtr != nullptr) { 136 t = reinterpret_cast<T*>(::operator new(TSize)); 137 #ifdef FFRT_BBOX_ENABLE 138 secondaryCache.insert(t); 139 #endif 140 lock.unlock(); 141 return t; 142 } 143 init(); 144 } 145 t = primaryCache.back(); 146 primaryCache.pop_back(); 147 count--; 148 lock.unlock(); 149 return t; 150 } 151 free(T * t)152 void free(T* t) 153 { 154 lock.lock(); 155 if (basePtr != nullptr && 156 basePtr <= t && 157 static_cast<size_t>(reinterpret_cast<uintptr_t>(t)) < 158 static_cast<size_t>(reinterpret_cast<uintptr_t>(basePtr)) + MmapSz) { 159 primaryCache.push_back(t); 160 count++; 161 } else { 162 #ifdef FFRT_BBOX_ENABLE 163 secondaryCache.erase(t); 164 #endif 165 ::operator delete(t); 166 } 167 lock.unlock(); 168 } 169 TSize(size)170 SimpleAllocator(std::size_t size = sizeof(T)) : TSize(size) 171 { 172 } ~SimpleAllocator()173 ~SimpleAllocator() 174 { 175 std::unique_lock<decltype(lock)> lck(lock); 176 if (basePtr == nullptr) { 177 return; 178 } 179 #ifdef FFRT_BBOX_ENABLE 180 uint32_t try_cnt = ALLOCATOR_DESTRUCT_TIMESOUT; 181 std::size_t reserved = MmapSz / TSize; 182 while (try_cnt > 0) { 183 if (primaryCache.size() == reserved && secondaryCache.size() == 0) { 184 break; 185 } 186 lck.unlock(); 187 usleep(1000); 188 try_cnt--; 189 lck.lock(); 190 } 191 if (try_cnt == 0) { 192 FFRT_LOGE("clear allocator failed"); 193 } 194 for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) { 195 ::operator delete(*ite); 196 } 197 #endif 198 ::operator delete(basePtr); 199 FFRT_LOGI("destruct SimpleAllocator"); 200 } 201 }; 202 203 template <typename T, std::size_t MmapSz = 8 * 1024 * 1024> 204 class QSimpleAllocator { 205 std::size_t TSize; 206 std::size_t curAllocated; 207 std::size_t maxAllocated; 208 std::mutex lock; 209 std::vector<T*> cache; 210 uint32_t flags = MAP_ANONYMOUS | MAP_PRIVATE; 211 expand()212 bool expand() 213 { 214 const int prot = PROT_READ | PROT_WRITE; 215 char* p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0)); 216 if (p == (char*)MAP_FAILED) { 217 if ((flags & MAP_HUGETLB) != 0) { 218 flags = MAP_ANONYMOUS | MAP_PRIVATE; 219 p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0)); 220 } 221 if (p == (char*)MAP_FAILED) { 222 perror("mmap"); 223 return false; 224 } 225 } 226 for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) { 227 cache.push_back(reinterpret_cast<T*>(p + i)); 228 } 229 return true; 230 } 231 Alloc()232 T* Alloc() 233 { 234 T* p = nullptr; 235 lock.lock(); 236 if (cache.empty()) { 237 if (!expand()) { 238 lock.unlock(); 239 return nullptr; 240 } 241 } 242 p = cache.back(); 243 ++curAllocated; 244 maxAllocated = std::max(curAllocated, maxAllocated); 245 cache.pop_back(); 246 lock.unlock(); 247 return p; 248 } 249 free(T * p)250 void free(T* p) 251 { 252 lock.lock(); 253 --curAllocated; 254 cache.push_back(p); 255 lock.unlock(); 256 } 257 release()258 void release() 259 { 260 T* p = nullptr; 261 lock.lock(); 262 FFRT_LOGD("coroutine release with waterline %d, cur occupied %d, cached size %d", 263 maxAllocated, curAllocated, cache.size()); 264 size_t reservedCnt = maxAllocated - curAllocated + 1; // reserve additional one for robustness 265 maxAllocated = curAllocated; 266 while (cache.size() > reservedCnt) { 267 p = cache.back(); 268 cache.pop_back(); 269 int ret = munmap(p, TSize); 270 if (ret != 0) { 271 FFRT_LOGE("munmap failed with errno: %d", errno); 272 } 273 } 274 lock.unlock(); 275 } 276 QSimpleAllocator()277 QSimpleAllocator() 278 { 279 } 280 281 public: 282 explicit QSimpleAllocator(std::size_t size = sizeof(T)) : curAllocated(0), maxAllocated(0) 283 { 284 std::size_t p_size = static_cast<std::size_t>(getpagesize()); 285 // manually align the size to the page size 286 TSize = (size - 1 + p_size) & -p_size; 287 if (MmapSz % TSize != 0) { 288 FFRT_LOGE("MmapSz is not divisible by TSize which may cause memory leak!"); 289 } 290 } 291 QSimpleAllocator(QSimpleAllocator const&) = delete; 292 void operator=(QSimpleAllocator const&) = delete; 293 Instance(std::size_t size)294 static QSimpleAllocator<T, MmapSz>* Instance(std::size_t size) 295 { 296 static QSimpleAllocator<T, MmapSz> ins(size); 297 return &ins; 298 } 299 300 static T* AllocMem(std::size_t size = sizeof(T)) 301 { 302 return Instance(size)->Alloc(); 303 } 304 305 static void FreeMem(T* p, std::size_t size = sizeof(T)) 306 { 307 Instance(size)->free(p); 308 } 309 310 static void releaseMem(std::size_t size = sizeof(T)) 311 { 312 Instance(size)->release(); 313 } 314 }; 315 } // namespace ffrt 316 #endif /* UTIL_SLAB_H */ 317