1 /*
2 * Copyright (c) 2017, Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22 //!
23 //! \file     memory_block_manager.h
24 //! \brief    Manages the memory blocks belonging to all heaps.
25 //! \details  Ensures that all memory is accounted for per heap for each block.
26 //!           Determines when blocks are no longer being processed and automatically
27 //!           reclaims free space in the state heap. Manages the state transitions of
28 //!           the memory blocks. The client has not direct access to the memory block
29 //!           manager.
30 //!
31 
32 #ifndef __MEMORY_BLOCK_MANAGER_H__
33 #define __MEMORY_BLOCK_MANAGER_H__
34 
35 #include <list>
36 #include <vector>
37 #include <memory>
38 #include "heap.h"
39 #include "memory_block.h"
40 
41 class FrameTrackerProducer;
42 
43 //! \brief Used by the heap manager to allow the client to access the heap memory.
44 class MemoryBlockManager
45 {
46     friend class HeapManager;
47 
48 public:
49     //! \brief Used by the client to acquire space in heap memory.
50     class AcquireParams
51     {
52     public:
53         //! \brief initializes AcquireParams with a tracker ID
AcquireParams(uint32_t trackerId,std::vector<uint32_t> & blockSizes)54         AcquireParams(
55             uint32_t trackerId,
56             std::vector<uint32_t> &blockSizes) : m_blockSizes(blockSizes)
57         {
58             m_trackerId = trackerId;
59         }
~AcquireParams()60         virtual ~AcquireParams() {}
61 
62         //! \brief Block sizes to allocate
63         std::vector<uint32_t>   &m_blockSizes;
64         //! \brief Requested block alignment
65         uint32_t                m_alignment = 0;
66         //! \brief Index to the frame tracker
67         uint32_t                m_trackerIndex = 0;
68         //! \brief Must be valid, used for determining whether or not a the block is still in use. \see m_trackerData.
69         uint32_t                m_trackerId = MemoryBlockInternal::m_invalidTrackerId;
70         //! \brief Zero memory blocks after allocation
71         bool                    m_zeroAssignedBlock = false;
72         //! \brief Block allocations are flaged as static are expected to be controlled by the client. \see HeapManager::Behavior::clientControlled.
73         bool                    m_staticBlock = false;
74     };
75 
MemoryBlockManager()76     MemoryBlockManager() { HEAP_FUNCTION_ENTER; }
77     virtual ~MemoryBlockManager();
78 
79 protected:
80     //!
81     //! \brief  Acquires space within the heap(s)
82     //! \param  [in] params
83     //!         Parameters describing the requested space
84     //! \param  [out] blocks
85     //!         A vector containing the memory blocks allocated
86     //! \param  [out] spaceNeeded
87     //!         Amount of space that the heap(s) are short of to complete space acquisition
88     //! \return MOS_STATUS
89     //!         MOS_STATUS_SUCCESS if success, else fail reason
90     //!
91     virtual MOS_STATUS AcquireSpace(
92         AcquireParams &params,
93         std::vector<MemoryBlock> &blocks,
94         uint32_t &spaceNeeded);
95 
96     //!
97     //! \brief  Indicates that the client is done editing the blocks
98     //! \param  [in] blocks
99     //!         Blocks to be submitted
100     //! \return MOS_STATUS
101     //!         MOS_STATUS_SUCCESS if success, else fail reason
102     //!
103     virtual MOS_STATUS SubmitBlocks(std::vector<MemoryBlock> &blocks);
104 
105     //!
106     //! \brief   Either directly adds a block to the free list, or prepares it to be added
107     //! \param   [in] block
108     //!          Block to be removed
109     //! \return  MOS_STATUS
110     //!          MOS_STATUS_SUCCESS if success, else fail reason
111     //!
112     virtual MOS_STATUS ClearSpace(MemoryBlock &block);
113 
114     //! \brief   Reclaims free memory based on whether or not blocks are no longer in use \see m_trackerData
115     //! \param   [out] blocksUpdated
116     //!          If true blocks have been updated, if false, no blocks were updated
117     //! \return  MOS_STATUS
118     //!          MOS_STATUS_SUCCESS if success, else fail reason
119     //!
120     virtual MOS_STATUS RefreshBlockStates(bool &blocksUpdated);
121 
122     //!
123     //! \brief  Stores heap and initializes memory blocks for future use.
124     //! \param  [in] heapId
125     //!         ID for the heap to be created
126     //! \param  [in] size
127     //!         Size of the heap to be created.
128     //! \return MOS_STATUS
129     //!         MOS_STATUS_SUCCESS if success, else fail reason
130     //!
131     virtual MOS_STATUS RegisterHeap(uint32_t heapId, uint32_t size, bool hwWriteOnly = false);
132 
133     //!
134     //! \brief  Removes the specified heap from the block manager, this occurs when the
135     //!         heap is in the process of being deleted. Ensures heap is not in use.
136     //! \param  [in] heapId
137     //!         ID for heap to be removed.
138     //!
139     virtual MOS_STATUS UnregisterHeap(uint32_t heapId);
140 
141     //!
142     //! \brief  Completes heap deletion for all heaps with unused space in the deleted heaps list.
143     //! \return MOS_STATUS
144     //!         MOS_STATUS_SUCCESS if success, else fail reason
145     //!
146     MOS_STATUS CompleteHeapDeletion();
147 
148     //!
149     //! \brief  Registers the tracker data to be used for determining whether a
150     //!         memory block is available.
151     //! \param  [in] trackerData
152     //!         Must be valid. See description in \see MemoryBlockManager::m_trackerData.
153     //! \return MOS_STATUS
154     //!         MOS_STATUS_SUCCESS if success, else fail reason
155     //!
156     virtual MOS_STATUS RegisterTrackerResource(uint32_t *trackerData);
157 
158     //!
159     //! \brief  Registers the tracker producer to be used for determining whether a
160     //!         memory block is available. This function has a higher priority than
161     //!         RegisterTrackerResource, so if it is called, the trackerResource will
162     //!         be useless.
163     //! \param  [in] trackerProducer
164     //!         Must be valid; pointer to trackerProducer.
165     //! \return MOS_STATUS
166     //!         MOS_STATUS_SUCCESS if success, else fail reason
167     //!
168     virtual MOS_STATUS RegisterTrackerProducer(FrameTrackerProducer *trackerProducer);
169 
170     //!
171     //! \brief  Gets the size of the all heaps
172     //! \return The size of all heaps managed \see m_size
173     //!
GetSize()174     uint32_t GetSize() { return m_totalSizeOfHeaps; }
175 
176     //!
177     //! \brief  Determines whether a valid tracker data has been registered
178     //! \return True if the pointer is valid, false otherwise
179     //!
IsTrackerDataValid()180     bool IsTrackerDataValid()
181     {
182         return (!m_useProducer && m_trackerData != nullptr)
183             || (m_useProducer && m_trackerProducer != nullptr);
184     }
185 
186     //!
187     //! \brief   All heaps allocated are locked and kept locked for their lifetimes
188     //! \details May only be set before any heaps are allocated.
189     //! \return  MOS_STATUS
190     //!          MOS_STATUS_SUCCESS if success, else fail reason
191     //!
LockHeapsOnAllocate()192     MOS_STATUS LockHeapsOnAllocate()
193     {
194         HEAP_FUNCTION_ENTER;
195         if (m_totalSizeOfHeaps != 0)
196         {
197             HEAP_ASSERTMESSAGE("Locking heaps on allocate may only be set before heaps are allocated");
198             return MOS_STATUS_UNKNOWN;
199         }
200         m_lockHeapsOnAllocate = true;
201         return MOS_STATUS_SUCCESS;
202     }
203 
204 private:
205     //! \brief Describes the information managed by this class for each heap registered
206     struct HeapWithAdjacencyBlockList
207     {
HeapWithAdjacencyBlockListHeapWithAdjacencyBlockList208         HeapWithAdjacencyBlockList()
209         {
210             HEAP_FUNCTION_ENTER;
211         }
212 
~HeapWithAdjacencyBlockListHeapWithAdjacencyBlockList213         virtual ~HeapWithAdjacencyBlockList()
214         {
215             HEAP_FUNCTION_ENTER;
216             MOS_Delete(m_heap);
217             auto curr = m_adjacencyListBegin;
218             MemoryBlockInternal *next = nullptr;
219 
220             while (curr != nullptr)
221             {
222                 next = curr->GetNext();
223                 MOS_Delete(curr);
224                 curr = next;
225             }
226         }
227 
228         //! \brief Heap which all other members of this struct define.
229         Heap *m_heap = nullptr;
230         //! \brief The list of blocks in the heap arranged by offset in the heap. The head is a dummy block.
231         MemoryBlockInternal *m_adjacencyListBegin = nullptr;
232         //! \brief The size of the heap
233         uint32_t m_size = 0;
234     };
235 
236     //!
237     //! \brief  Stores the provided OS interface in the heap
238     //! \param  [in] osInterface
239     //!         Must be valid
240     //! \return MOS_STATUS
241     //!         MOS_STATUS_SUCCESS if success, else fail reason
242     //!
243     virtual MOS_STATUS RegisterOsInterface(PMOS_INTERFACE osInterface);
244 
245     //!
246     //! \brief  Determines whether or not space is available, if not enough space returns
247     //!         the amount short in \a spaceNeeded
248     //! \param  [in] sortedSizes
249     //!         Requested block sizes sorted in decending order.
250     //! \param  [in] params
251     //!         Parameters describing the requested space
252     //! \param  [out] spaceNeeded
253     //!         Amount of space that the heap(s) are short of to complete space acquisition
254     //! \return MOS_STATUS
255     //!         MOS_STATUS_SUCCESS if success, else fail reason
256     //!
257     MOS_STATUS IsSpaceAvailable(
258         AcquireParams &params,
259         uint32_t &spaceNeeded);
260 
261     //!
262     //! \brief  Sets up memory blocks for the requested space
263     //! \param  [in] sortedSizes
264     //!         Requested block sizes sorted in decending order with original index saved from params.
265     //! \param  [in] params
266     //!         Parameters describing the requested space
267     //! \param  [out] blocks
268     //!         A vector containing the memory blocks allocated
269     //! \return MOS_STATUS
270     //!         MOS_STATUS_SUCCESS if success, else fail reason
271     //!
272     MOS_STATUS AllocateSpace(
273         AcquireParams &params,
274         std::vector<MemoryBlock> &blocks);
275 
276     //!
277     //! \brief  Sets up memory blocks for the requested space
278     //! \param  [in] alignedSize
279     //!         Aligned size of the memory requested
280     //! \param  [in] trackerId
281     //!         Tracker ID to be used for the allocated block
282     //! \param  [in] staticBlock
283     //!         Indicates that the newly created block is expected to be static
284     //! \param  [out] freeBlock
285     //!         Free block that will be used for the allocation of the new block
286     //! \return MOS_STATUS
287     //!         MOS_STATUS_SUCCESS if success, else fail reason
288     //!
289     MOS_STATUS AllocateBlock(
290         uint32_t alignedSize,
291         uint32_t trackerId,
292         bool staticBlock,
293         MemoryBlockInternal *freeBlock);
294 
295     //!
296     //! \brief  Sets up memory blocks for the requested space
297     //! \param  [in] alignedSize
298     //!         Aligned size of the memory requested
299     //! \param  [in] trackerIndex
300     //!         Tracker index to be used for the allocated block
301     //! \param  [in] trackerId
302     //!         Tracker ID to be used for the allocated block
303     //! \param  [in] staticBlock
304     //!         Indicates that the newly created block is expected to be static
305     //! \param  [out] freeBlock
306     //!         Free block that will be used for the allocation of the new block
307     //! \return MOS_STATUS
308     //!         MOS_STATUS_SUCCESS if success, else fail reason
309     //!
310     MOS_STATUS AllocateBlock(
311         uint32_t alignedSize,
312         uint32_t trackerIndex,
313         uint32_t trackerId,
314         bool staticBlock,
315         MemoryBlockInternal *freeBlock);
316 
317     //!
318     //! \brief  Sets up memory blocks for the requested space
319     //! \param  [in] block
320     //!         Block to be added to the sorted pool of type \a state
321     //! \param  [in] state
322     //!         Type of pool to be added to
323     //! \return MOS_STATUS
324     //!         MOS_STATUS_SUCCESS if success, else fail reason
325     //!
326     MOS_STATUS AddBlockToSortedList(
327         MemoryBlockInternal *block,
328         MemoryBlockInternal::State state);
329 
330     //!
331     //! \brief  Sets up memory blocks for the requested space
332     //! \param  [in] block
333     //!         Block to be added to the sorted pool of type \a state
334     //! \param  [in] state
335     //!         Type of pool to be added to, may not be of type MemoryBlockInternal::State::pool since a separate function is dedicated to that case. \see GetBlockFromPool
336     //! \return MOS_STATUS
337     //!         MOS_STATUS_SUCCESS if success, else fail reason
338     //!
339     MOS_STATUS RemoveBlockFromSortedList(
340         MemoryBlockInternal *block,
341         MemoryBlockInternal::State state);
342 
343     //!
344     //! \brief  Gets a pool type block from the sorted block pool, if pool is empty allocates a new one
345     //!         \see m_sortedBlockList[MemoryBlockInternal::State::pool]
346     //! \return MemoryBlockInternal*
347     //!         Valid pointer if success, nullptr if fail
348     //!
349     MemoryBlockInternal* GetBlockFromPool();
350 
351     //!
352     //! \brief  Removes all blocks with heaps matching \a heapId from the sorted block pool for \a state. \see m_sortedBlockList
353     //! \param  [in] heapId
354     //!         Heap whose blocks should be removed
355     //! \return MOS_STATUS
356     //!         MOS_STATUS_SUCCESS if success, else fail reason
357     //!
358     MOS_STATUS RemoveHeapFromSortedBlockList(
359         uint32_t heapId);
360 
361     //!
362     //! \brief  Merges two contiguous blocks into one.
363     //! \param  [in,out] blockCombined
364     //!         Block into which \a blockRelease will be merged in to, this block is the output of the merge
365     //! \param  [in] blockRelease
366     //!         Block to be merged into \a blockCombined, this block will be added back to the pool after the merge
367     //! \return MOS_STATUS
368     //!         MOS_STATUS_SUCCESS if success, else fail reason
369     //!
370     MOS_STATUS MergeBlocks(
371         MemoryBlockInternal *blockCombined,
372         MemoryBlockInternal *blockRelease);
373 
374     //!
375     //! \brief  Temporary function while MOS utilities function is added for smart pointers
376     //! \return std::shared_ptr<T>
377     //!         Valid pointer if success, nullptr if fail
378     //!
379     template <class T, class... Args>
MakeShared(Args &&...args)380     std::shared_ptr<T> MakeShared(Args&&... args)
381     {
382         std::shared_ptr<T> block = nullptr;
383         try
384         {
385             block = std::make_shared<T>(args...);
386         }
387         catch (const std::bad_alloc& e)
388         {
389             HEAP_ASSERTMESSAGE("Allocation of memory block failed!");
390             return nullptr;
391         }
392         return block;
393     }
394 
395 
396     //! \brief  Used to retain information about the requested block sizes after those
397     //!         sizes are sorted for the return of blocks.\see AcquireParams::m_blockSizes \see AcquireSpace
398     struct SortedSizePair
399     {
400         //! \brief Constructs SortedSizePair
SortedSizePairSortedSizePair401         SortedSizePair() {}
SortedSizePairSortedSizePair402         SortedSizePair(uint32_t originalIdx, uint32_t blockSize)
403             : m_originalIdx(originalIdx), m_blockSize(blockSize) {}
404         uint32_t m_originalIdx = 0; //!< Original index of the requested block size \see AcquireParams::m_blockSizes
405         uint32_t m_blockSize = 0;   //!< Aligned block size
406     };
407 
408     //! \brief Alignment for blocks in heap, currently fixed at a cacheline
409     static const uint16_t m_blockAlignment = 64;
410     //! \brief Alignment for heap, currently fixed at a page
411     static const uint16_t m_heapAlignment = MOS_PAGE_SIZE;
412     //! \brief Number of submissions before a refresh, currently fixed
413     static const uint16_t m_numSubmissionsForRefresh = 128;
414 
415     //! \brief Total size of all managed heaps.
416     uint32_t m_totalSizeOfHeaps = 0;
417     //! \brief The list of block pools per heap
418     std::list<std::shared_ptr<HeapWithAdjacencyBlockList>> m_heaps;
419     //! \brief List of block pools per heap for heaps in deletion process
420     std::list<std::shared_ptr<HeapWithAdjacencyBlockList>> m_deletedHeaps;
421     //! \brief Pools of memory blocks sorted by their states based on the state indicated
422     //!        by the latest TrackerId. The free pool is sorted in ascending order.
423     MemoryBlockInternal *m_sortedBlockList[MemoryBlockInternal::State::stateCount] = {nullptr};
424     //! \brief Number of entries in each sorted block list.
425     uint32_t m_sortedBlockListNumEntries[MemoryBlockInternal::State::stateCount] = {0};
426     //! \brief Sizes of each block pool.
427     //! \brief MemoryBlockInternal::State::pool type blocks have no size, and thus that pool also is expected to be size 0.
428     uint32_t m_sortedBlockListSizes[MemoryBlockInternal::State::stateCount] = {0};
429     //! \brief   Used to compare to a memory block's tracker ID. If the value is greater
430     //!          than the tracker ID, then the block is no longer in use and may be reclaimed
431     //!          as free space, or if the block is static transitioned to allocated state so
432     //!          it may be re-used. \see MemoryBlockInternal::m_trackerId
433     //! \details The client is expected to manage this tracker; it should be valid for the
434     //!          life of the heap manager. The tracker may be updated at whatever granularity is
435     //!          appropriate--per workload, per frame, etc. It is expected that when the
436     //!          tracker data indicates that all blocks with tracker IDs less than or equal
437     //!          to the udpated value may be re-used.
438     uint32_t *m_trackerData = nullptr;
439     PMOS_INTERFACE m_osInterface = nullptr; //!< OS interface used for managing graphics resources
440     bool m_lockHeapsOnAllocate = false;             //!< All heaps allocated with the keep locked flag.
441 
442     //! \brief Persistent storage for the sorted sizes used during AcquireSpace()
443     std::list<SortedSizePair> m_sortedSizes;
444     //! \brief TrackerProducer
445     FrameTrackerProducer *m_trackerProducer = nullptr;
446     //! \bried Whether trackerProducer is set
447     bool m_useProducer = false;
448 };
449 #endif // __MEMORY_BLOCK_MANAGER_H__
450