1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     osl_BuddyHeap.h
4   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law. They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10   $Rev: 15690 $
11  *---------------------------------------------------------------------------
12 
13 
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         // Provisional initialization of management area
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         // Relocate the management area (normally should be located in the same place)
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     // Cannot conform to HeapBase because Free argument is different
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         // Is the page index divisible by the order?
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     /* Please see man pages for details
156 
157     */
GetStartAddress()158     virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_HeapStart); }
159 
160     /* Please see man pages for details
161 
162     */
GetTotalSize()163     virtual size_t GetTotalSize() const { return m_HeapSize; }
164 
165     /* Please see man pages for details
166 
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         // Maximum "order" - 1
330         while(order < MAX_ORDER - 1)
331         {
332             Page* pBuddyPage = GetBuddy(pPage, order);
333 
334             // Is there a buddy on the free list?
335             if(m_FreeArea[order].Remove(pBuddyPage))
336             {
337                 // If so, delete it (jointed) from the free list and attempt joint at (order + 1).
338 
339                 // If the page was not aligned to (order + 1), then set address to 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                 // If not, joint failed. Add page to the free list.
353                 break;
354             }
355         }
356 
357         // Buddy was being used or jointed up to max order
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         // If the page index is divisible by the above order, the next right is Buddy
380         if(IsAlignedToOrder(pPage, order + 1))
381         {
382             return pPage + GetNumPagesByOrder(order);
383         }
384         else // If not, the next left is 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     /* Please see man pages for details
472 
473 
474 
475 
476 
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     /* Please see man pages for details
498 
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     /* Please see man pages for details
509 
510 
511      */
GetHeap()512     BuddyHeap* GetHeap() { return m_Heap; }
513 
514     /* Please see man pages for details
515 
516 
517      */
GetHeap()518     const BuddyHeap* GetHeap() const { return m_Heap; }
519 
520     /* Please see man pages for details
521 
522 
523 
524 
525 
526 
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     /* Please see man pages for details
548 
549 
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