1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: osl_BuddyHeap.h 4 5 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 6 7 These coded instructions, statements, and computer programs contain 8 proprietary information of Nintendo of America Inc. and/or Nintendo 9 Company Ltd., and are protected by Federal copyright law. They may 10 not be disclosed to third parties or copied or duplicated in any form, 11 in whole or in part, without the prior written consent of Nintendo. 12 13 $Rev: 15690 $ 14 *---------------------------------------------------------------------------*/ 15 16 #ifndef NN_NET_OSL_BUDDYHEAP_H_ 17 #define NN_NET_OSL_BUDDYHEAP_H_ 18 19 #include <nn/types.h> 20 #include <nn/os/os_MemoryBlock.h> 21 #include <nn/fnd/fnd_HeapBase.h> 22 #include <nn/fnd/fnd_Allocator.h> 23 24 namespace nn { 25 namespace net { 26 namespace osl { 27 28 template<size_t PageSize, s32 MaxOrder> 29 class BuddyHeapBase : public nn::fnd::HeapBase 30 { 31 protected: 32 template <s32 align, typename T> DivUp(T x)33 static T DivUp(T x) 34 { 35 return static_cast<T>( (x + (align - 1)) / align ); 36 } 37 template<size_t alignment> RoundDown(uptr addr)38 static uptr RoundDown(uptr addr) 39 { 40 return (addr / alignment) * alignment; 41 } 42 43 template<size_t alignment> RoundUp(uptr addr)44 static uptr RoundUp(uptr addr) 45 { 46 return RoundDown<alignment>(addr + alignment - 1); 47 } 48 49 public: 50 static const size_t ALIGN = 8; 51 GetNumMaxPages(void)52 static size_t GetNumMaxPages(void) 53 { 54 return 1 << (MAX_ORDER - 1); 55 } 56 GetMaxSize(void)57 static size_t GetMaxSize(void) 58 { 59 return GetNumMaxPages() * PAGE_SIZE; 60 } 61 Initialize(uptr addr,size_t numPages)62 void Initialize(uptr addr, size_t numPages) 63 { 64 NN_POINTER_ASSERT(addr); 65 NN_ASSERT((addr % ALIGN) == 0); 66 67 m_HeapStart = addr; 68 m_HeapSize = PAGE_SIZE * numPages; 69 70 NN_LOG_DEBUG("start %p\n", m_HeapStart); 71 NN_LOG_DEBUG("size %p\n", m_HeapSize); 72 73 const size_t numMaxPages = GetNumMaxPages(); 74 NN_LOG_DEBUG("numMaxPages %d\n", numMaxPages); 75 const size_t maxSize = GetMaxSize(); 76 NN_LOG_DEBUG("maxSize %d\n", maxSize); 77 const size_t numMaxOrders = m_HeapSize / maxSize; 78 NN_LOG_DEBUG("numMaxOrders %d\n", numMaxOrders); 79 80 NN_ASSERT(numMaxOrders > 0); 81 82 // 管理領域の仮初期化 83 m_pPages = reinterpret_cast<Page*>(m_HeapStart); 84 85 for(int i = 0; i < numPages; i++) 86 { 87 m_pPages[i].pNext = NULL; 88 } 89 90 for( int i = 0; i < MAX_ORDER; ++i ) 91 { 92 m_FreeArea[i].Initialize(); 93 } 94 for(int i = 0; i < numMaxOrders; i++) 95 { 96 m_FreeArea[MAX_ORDER - 1].PushBack(&(m_pPages[i * numMaxPages])); 97 } 98 99 // 管理領域を再配置(普通は同じところに配置されるはず) 100 const size_t manageAreaSize = sizeof(Page) * numPages; 101 102 if(m_pPages != reinterpret_cast<Page*>(AllocateByOrder(GetNearestHigherOrder(DivUp<PAGE_SIZE>(manageAreaSize))))) 103 { 104 NN_LOG_WARN("failed to allocate page managing area."); 105 } 106 } Finalize(void)107 void Finalize(void){} 108 AllocateByOrder(s32 order)109 void* AllocateByOrder(s32 order) 110 { 111 NN_ASSERT(order >= 0); 112 113 NN_LOG_DEBUG("demands order %d\n", order); 114 115 116 Page* pPage = GetFreePage(order); 117 NN_LOG_DEBUG("pPage = %08x\n", pPage); 118 119 if(pPage) 120 { 121 uptr addr = GetAddressFromPage(*pPage); 122 123 NN_LOG_DEBUG("returns order %d, address 0x%08X\n", order, addr); 124 125 return reinterpret_cast<void*>(addr); 126 } 127 else 128 { 129 return NULL; 130 } 131 } 132 133 // Free の引数が違うので HeapBase 準拠は無理 Free(void * p,s32 order)134 void Free(void* p, s32 order) 135 { 136 if(!p) 137 { 138 return; 139 } 140 141 NN_ASSERT((reinterpret_cast<uptr>(p) - m_HeapStart) % PAGE_SIZE == 0); 142 143 Page* pPage = GetPageFromAddress(reinterpret_cast<uptr>(p)); 144 145 // ページのインデックスがオーダーで割り切れるか。 146 NN_ASSERT(GetIndexFromPage(*pPage) % (1 << order) == 0); 147 148 NN_LOG_DEBUG("addr = 0x%08x, order = %d, pPage = 0x%08x\n", p, order, pPage); 149 150 JointBuddies(pPage, order); 151 } 152 FreeV(void *)153 virtual void FreeV(void*) { NN_ASSERT(false); } 154 155 /*! 156 @brief このヒープが利用しているメモリ領域の先頭アドレスを返します。 157 */ GetStartAddress()158 virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_HeapStart); } 159 160 /*! 161 @brief このヒープが利用しているメモリ領域のサイズを返します。 162 */ GetTotalSize()163 virtual size_t GetTotalSize() const { return m_HeapSize; } 164 165 /*! 166 @brief デバッグ用にヒープの内容をダンプします。 167 */ Dump()168 virtual void Dump() const{}; HasAddress(const void * addr)169 virtual bool HasAddress(const void* addr) const 170 { 171 return m_HeapStart <= reinterpret_cast<uptr>(addr) 172 && reinterpret_cast<uptr>(addr) < m_HeapStart + m_HeapSize; 173 } 174 GetOrder(size_t size)175 static s32 GetOrder(size_t size) 176 { 177 return GetNearestHigherOrder(DivUp<PAGE_SIZE>(size)); 178 } 179 180 static const size_t PAGE_SIZE = PageSize; 181 static const s32 MAX_ORDER = MaxOrder; 182 183 private: 184 struct Page 185 { 186 Page* pNext; 187 }; 188 189 class PageList 190 { 191 private: 192 Page* m_pFirstPage; 193 Page* m_pLastPage; 194 195 public: Initialize()196 void Initialize() 197 { 198 m_pFirstPage = NULL; 199 m_pLastPage = NULL; 200 } 201 PopFront()202 Page* PopFront() 203 { 204 NN_ASSERT(m_pFirstPage); 205 206 Page* pPage = m_pFirstPage; 207 208 m_pFirstPage = pPage->pNext; 209 pPage->pNext = NULL; 210 211 if(pPage == m_pLastPage) 212 { 213 m_pLastPage = NULL; 214 } 215 216 return pPage; 217 } 218 PushBack(Page * pPage)219 void PushBack(Page* pPage) 220 { 221 NN_ASSERT(!pPage->pNext); 222 223 if(!m_pFirstPage) 224 { 225 m_pFirstPage = pPage; 226 } 227 else 228 { 229 m_pLastPage->pNext = pPage; 230 } 231 232 m_pLastPage = pPage; 233 } 234 Remove(Page * pPage)235 bool Remove(Page* pPage) 236 { 237 if(!m_pFirstPage) 238 { 239 return false; 240 } 241 242 Page* pPrevPage = NULL; 243 Page* page = m_pFirstPage; 244 245 for(;;) 246 { 247 if(page == pPage) 248 { 249 if(page == m_pFirstPage) 250 { 251 m_pFirstPage = page->pNext; 252 } 253 else if(page == m_pLastPage) 254 { 255 m_pLastPage = pPrevPage; 256 m_pLastPage->pNext = NULL; 257 } 258 else 259 { 260 pPrevPage->pNext = page->pNext; 261 } 262 263 page->pNext = NULL; 264 265 return true; 266 } 267 268 if(page->pNext == NULL) 269 { 270 return false; 271 } 272 273 pPrevPage = page; 274 page = page->pNext; 275 } 276 } 277 IsEmpty()278 bool IsEmpty() const 279 { 280 if(m_pFirstPage) 281 { 282 return false; 283 } 284 else 285 { 286 return true; 287 } 288 } 289 }; 290 291 PageList m_FreeArea[MAX_ORDER]; 292 293 uptr m_HeapStart; 294 size_t m_HeapSize; 295 GetFreePage(s32 order)296 Page* GetFreePage(s32 order) 297 { 298 for(int i = order; i < MAX_ORDER; i++) 299 { 300 if(!(m_FreeArea[i].IsEmpty())) 301 { 302 Page* pPage = m_FreeArea[i].PopFront(); 303 304 NN_ASSERT(pPage); 305 306 DivideBuddies(pPage, order, i); 307 308 return pPage; 309 } 310 } 311 312 NN_LOG_WARN("No free pages order %d\n", order); 313 return NULL; 314 } 315 DivideBuddies(Page * pPage,s32 demandedOrder,s32 freeOrder)316 void DivideBuddies(Page* pPage, s32 demandedOrder, s32 freeOrder) 317 { 318 for(int i = freeOrder; demandedOrder < i; i--) 319 { 320 Page* pDividedPage = &pPage[(1 << (i - 1))];; 321 322 NN_LOG_DEBUG("divides buddy address 0x%08X and 0x%08X, order%d\n", GetAddressFromPage(*pPage), GetAddressFromPage(*pDividedPage), i); 323 m_FreeArea[i - 1].PushBack(pDividedPage); 324 } 325 } 326 JointBuddies(Page * pPage,s32 order)327 void JointBuddies(Page* pPage, s32 order) 328 { 329 // 最上位のオーダーの 1つ前まで 330 while(order < MAX_ORDER - 1) 331 { 332 Page* pBuddyPage = GetBuddy(pPage, order); 333 334 // フリーリストにバディがいるか 335 if(m_FreeArea[order].Remove(pBuddyPage)) 336 { 337 // いたら、フリーリストから削除して(=ジョイントした)1つ上のオーダーでジョイントを試みる。 338 339 // ページが1つ上のオーダーでアラインしていなかったら、アドレスを Buddy に。 340 if(!IsAlignedToOrder(pPage, order + 1)) 341 { 342 pPage = pBuddyPage; 343 } 344 345 NN_LOG_DEBUG("joints buddy address 0x%08X and 0x%08X, order %d", GetAddressFromPage(*pPage), GetAddressFromPage(*GetBuddy(pPage, order)), order); 346 347 348 order++; 349 } 350 else 351 { 352 // いなかったら、ジョイントできなかったので、ページをフリーリストに追加する。 353 break; 354 } 355 } 356 357 // Buddy が利用中だったか、最上位オーダーまでジョイントした 358 NN_LOG_DEBUG("adding a free page, address 0x%08X, order %d", GetAddressFromPage(*pPage), order ); 359 m_FreeArea[order].PushBack(pPage); 360 } 361 GetAddressFromPage(const Page & page)362 inline uptr GetAddressFromPage(const Page& page) const 363 { 364 return m_HeapStart + GetIndexFromPage(page) * PAGE_SIZE; 365 } 366 GetPageFromAddress(uptr addr)367 inline Page* GetPageFromAddress(uptr addr) 368 { 369 return &m_pPages[(addr - m_HeapStart) / PAGE_SIZE]; 370 } 371 GetIndexFromPage(const Page & page)372 inline s32 GetIndexFromPage(const Page& page) const 373 { 374 return &page - m_pPages; 375 } 376 GetBuddy(Page * pPage,s32 order)377 inline Page* GetBuddy(Page* pPage, s32 order) 378 { 379 // ページのインデックスが上のオーダーで割り切れるなら、右隣が Buddy 380 if(IsAlignedToOrder(pPage, order + 1)) 381 { 382 return pPage + GetNumPagesByOrder(order); 383 } 384 else // でなければ、左隣が Buddy 385 { 386 return pPage - GetNumPagesByOrder(order); 387 } 388 } 389 IsAlignedToOrder(Page * pPage,s32 order)390 inline bool IsAlignedToOrder(Page* pPage, s32 order) const 391 { 392 return GetIndexFromPage(*pPage) % GetNumPagesByOrder(order) == 0; 393 } 394 GetNumPagesByOrder(s32 order)395 inline int GetNumPagesByOrder(s32 order) const 396 { 397 return (1 << order); 398 } 399 GetNearestHigherOrder(size_t numPages)400 static s32 GetNearestHigherOrder(size_t numPages) 401 { 402 for(int order = 0; order <= MAX_ORDER; order++) 403 { 404 if( numPages <= (1 << order) ) { 405 return order; 406 } 407 } 408 409 NN_LOG_WARN("No nearest higher order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER); 410 return -1; 411 } 412 GetNearestLowerOrder(size_t numPages)413 static s32 GetNearestLowerOrder(size_t numPages) 414 { 415 for(int order = 0; order <= MAX_ORDER; order++) 416 { 417 if( numPages < (1 << order) ) { 418 return order - 1; 419 } 420 } 421 422 NN_LOG_WARN("No nearest lower order: numPages=%d, MAX_ORDER=%d", numPages, MAX_ORDER); 423 return -1; 424 } 425 426 Page* m_pPages; 427 }; 428 429 template <size_t PageSize, s32 MaxOrder, class LockPolicy> 430 class BuddyHeapTemplate : public BuddyHeapBase<PageSize, MaxOrder>, private LockPolicy::LockObject 431 { 432 private: 433 typedef BuddyHeapBase<PageSize, MaxOrder> Base; 434 typedef typename LockPolicy::LockObject LockObject; 435 typedef typename LockPolicy::ScopedLock ScopedLock; 436 437 public: BuddyHeapTemplate()438 BuddyHeapTemplate() {} 439 440 template <class MemoryBlockT> BuddyHeapTemplate(const MemoryBlockT & block)441 explicit BuddyHeapTemplate(const MemoryBlockT& block) 442 { 443 Initialize(block.GetAddress(), block.GetSize()); 444 } 445 Initialize(uptr addr,size_t size)446 void Initialize(uptr addr, size_t size) 447 { 448 uptr addrAligned = Base::RoundUp<Base::ALIGN>(addr); 449 size -= addrAligned - addr; 450 451 Base::Initialize(addrAligned, size / Base::PAGE_SIZE); 452 LockObject::Initialize(); 453 } Initialize(const nn::os::MemoryBlockBase & block)454 void Initialize(const nn::os::MemoryBlockBase& block) 455 { 456 Initialize(block.GetAddress(), block.GetSize()); 457 } 458 Finalize()459 void Finalize() 460 { 461 LockObject::Finalize(); 462 Base::Finalize(); 463 } 464 AllocateByOrder(s32 order)465 void* AllocateByOrder(s32 order) 466 { 467 ScopedLock lk(*this); 468 return Base::AllocateByOrder(order); 469 } 470 471 /*! 472 @brief 拡張ヒープから確保したメモリブロックを解放します。 473 474 @param[in] p 解放するメモリブロックの先頭アドレスを指定します。 475 476 @return 無し。 477 */ Free(void * p,s32 order)478 void Free(void* p, s32 order) 479 { 480 ScopedLock lk(*this); 481 Base::Free(p, order); 482 } 483 484 class Allocator; 485 }; 486 487 template <size_t PageSize, s32 MaxOrder, class LockPolicy> 488 class BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy>::Allocator : public nn::fnd::IAllocator 489 { 490 private: 491 typedef BuddyHeapTemplate<PageSize, MaxOrder, LockPolicy> BuddyHeap; 492 static const size_t ALIGN = BuddyHeap::ALIGN; 493 494 public: Allocator(BuddyHeap & heap)495 Allocator(BuddyHeap& heap) : m_Heap(0) { Initialize(heap); } 496 497 /*! 498 @brief 初期化をしないコンストラクタです。 499 */ Allocator()500 Allocator() : m_Heap(0) {} 501 Initialize(BuddyHeap & heap)502 void Initialize(BuddyHeap& heap) 503 { 504 NN_TASSERT_(!this->m_Heap); 505 this->m_Heap = &heap; 506 } 507 508 /*! 509 @brief アロケータにセットされているヒープを返します。 510 @return アロケータにセットされているヒープ 511 */ GetHeap()512 BuddyHeap* GetHeap() { return m_Heap; } 513 514 /*! 515 @brief アロケータにセットされている拡張ヒープを返します。 516 @return アロケータにセットされている拡張ヒープ 517 */ GetHeap()518 const BuddyHeap* GetHeap() const { return m_Heap; } 519 520 /*! 521 @brief 指定したサイズとアラインメントでメモリ領域を確保します。 522 523 @param[in] size 確保するメモリのサイズ 524 @param[in] alignment 確保するメモリのアラインメント 525 526 @return 確保したメモリ領域の先頭へのポインタ 527 */ 528 virtual void* Allocate(size_t size, s32 alignment = nn::fnd::HeapBase::DEFAULT_ALIGNMENT) 529 { 530 NN_ASSERT(alignment <= ALIGN); 531 if (alignment > ALIGN) 532 { 533 return NULL; 534 } 535 536 s32 order = m_Heap->GetOrder(size + ALIGN); 537 void* p = m_Heap->Allocate(order); 538 if (!p) 539 { 540 return NULL; 541 } 542 NN_LOG_DEBUG("Alloc size %d, order %d\n", size + ALIGN, order); 543 *reinterpret_cast<s32*>(p) = order; 544 return reinterpret_cast<void*>(reinterpret_cast<uptr>(p) + ALIGN); 545 } 546 547 /*! 548 @brief メモリ領域を解放します。 549 @param[in] p 確保されているメモリ領域の先頭へのポインタ 550 */ Free(void * p)551 virtual void Free(void* p) 552 { 553 p = reinterpret_cast<void*>(reinterpret_cast<uptr>(p) - ALIGN); 554 s32 order = *reinterpret_cast<s32*>(p); 555 m_Heap->Free(p, order); 556 } 557 558 private: 559 BuddyHeap* m_Heap; 560 }; 561 562 } 563 } 564 } 565 566 #endif /* NN_NET_OSL_BUDDYHEAP_H_ */ 567