blob: 8784f68502b7f977e817c5c022893325abff554b [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file memory_block_manager.h
//! \brief Manages the memory blocks belonging to all heaps.
//! \details Ensures that all memory is accounted for per heap for each block.
//! Determines when blocks are no longer being processed and automatically
//! reclaims free space in the state heap. Manages the state transitions of
//! the memory blocks. The client has not direct access to the memory block
//! manager.
//!
#ifndef __MEMORY_BLOCK_MANAGER_H__
#define __MEMORY_BLOCK_MANAGER_H__
#include <list>
#include <vector>
#include <memory>
#include "heap.h"
#include "memory_block.h"
class FrameTrackerProducer;
//! \brief Used by the heap manager to allow the client to access the heap memory.
class MemoryBlockManager
{
friend class HeapManager;
public:
//! \brief Used by the client to acquire space in heap memory.
class AcquireParams
{
public:
//! \brief initializes AcquireParams with a tracker ID
AcquireParams(
uint32_t trackerId,
std::vector<uint32_t> &blockSizes) : m_blockSizes(blockSizes)
{
m_trackerId = trackerId;
}
virtual ~AcquireParams() {}
//! \brief Block sizes to allocate
std::vector<uint32_t> &m_blockSizes;
//! \brief Requested block alignment
uint32_t m_alignment = 0;
//! \brief Index to the frame tracker
uint32_t m_trackerIndex = 0;
//! \brief Must be valid, used for determining whether or not a the block is still in use. \see m_trackerData.
uint32_t m_trackerId = MemoryBlockInternal::m_invalidTrackerId;
//! \brief Zero memory blocks after allocation
bool m_zeroAssignedBlock = false;
//! \brief Block allocations are flaged as static are expected to be controlled by the client. \see HeapManager::Behavior::clientControlled.
bool m_staticBlock = false;
};
MemoryBlockManager() { HEAP_FUNCTION_ENTER; }
virtual ~MemoryBlockManager();
protected:
//!
//! \brief Acquires space within the heap(s)
//! \param [in] params
//! Parameters describing the requested space
//! \param [out] blocks
//! A vector containing the memory blocks allocated
//! \param [out] spaceNeeded
//! Amount of space that the heap(s) are short of to complete space acquisition
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS AcquireSpace(
AcquireParams &params,
std::vector<MemoryBlock> &blocks,
uint32_t &spaceNeeded);
//!
//! \brief Indicates that the client is done editing the blocks
//! \param [in] blocks
//! Blocks to be submitted
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS SubmitBlocks(std::vector<MemoryBlock> &blocks);
//!
//! \brief Either directly adds a block to the free list, or prepares it to be added
//! \param [in] block
//! Block to be removed
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS ClearSpace(MemoryBlock &block);
//! \brief Reclaims free memory based on whether or not blocks are no longer in use \see m_trackerData
//! \param [out] blocksUpdated
//! If true blocks have been updated, if false, no blocks were updated
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RefreshBlockStates(bool &blocksUpdated);
//!
//! \brief Stores heap and initializes memory blocks for future use.
//! \param [in] heapId
//! ID for the heap to be created
//! \param [in] size
//! Size of the heap to be created.
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RegisterHeap(uint32_t heapId, uint32_t size, bool hwWriteOnly = false);
//!
//! \brief Removes the specified heap from the block manager, this occurs when the
//! heap is in the process of being deleted. Ensures heap is not in use.
//! \param [in] heapId
//! ID for heap to be removed.
//!
MOS_STATUS UnregisterHeap(uint32_t heapId);
//!
//! \brief Completes heap deletion for all heaps with unused space in the deleted heaps list.
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS CompleteHeapDeletion();
//!
//! \brief Registers the tracker data to be used for determining whether a
//! memory block is available.
//! \param [in] trackerData
//! Must be valid. See description in \see MemoryBlockManager::m_trackerData.
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RegisterTrackerResource(uint32_t *trackerData);
//!
//! \brief Registers the tracker producer to be used for determining whether a
//! memory block is available. This function has a higher priority than
//! RegisterTrackerResource, so if it is called, the trackerResource will
//! be useless.
//! \param [in] trackerProducer
//! Must be valid; pointer to trackerProducer.
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RegisterTrackerProducer(FrameTrackerProducer *trackerProducer);
//!
//! \brief Gets the size of the all heaps
//! \return The size of all heaps managed \see m_size
//!
uint32_t GetSize() { return m_totalSizeOfHeaps; }
//!
//! \brief Determines whether a valid tracker data has been registered
//! \return True if the pointer is valid, false otherwise
//!
bool IsTrackerDataValid()
{
return (!m_useProducer && m_trackerData != nullptr)
|| (m_useProducer && m_trackerProducer != nullptr);
}
//!
//! \brief All heaps allocated are locked and kept locked for their lifetimes
//! \details May only be set before any heaps are allocated.
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS LockHeapsOnAllocate()
{
HEAP_FUNCTION_ENTER;
if (m_totalSizeOfHeaps != 0)
{
HEAP_ASSERTMESSAGE("Locking heaps on allocate may only be set before heaps are allocated");
return MOS_STATUS_UNKNOWN;
}
m_lockHeapsOnAllocate = true;
return MOS_STATUS_SUCCESS;
}
private:
//! \brief Describes the information managed by this class for each heap registered
struct HeapWithAdjacencyBlockList
{
HeapWithAdjacencyBlockList()
{
HEAP_FUNCTION_ENTER;
}
virtual ~HeapWithAdjacencyBlockList()
{
HEAP_FUNCTION_ENTER;
MOS_Delete(m_heap);
auto curr = m_adjacencyListBegin;
MemoryBlockInternal *next = nullptr;
while (curr != nullptr)
{
next = curr->GetNext();
MOS_Delete(curr);
curr = next;
}
}
//! \brief Heap which all other members of this struct define.
Heap *m_heap = nullptr;
//! \brief The list of blocks in the heap arranged by offset in the heap. The head is a dummy block.
MemoryBlockInternal *m_adjacencyListBegin = nullptr;
//! \brief The size of the heap
uint32_t m_size = 0;
};
//!
//! \brief Stores the provided OS interface in the heap
//! \param [in] osInterface
//! Must be valid
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RegisterOsInterface(PMOS_INTERFACE osInterface);
//!
//! \brief Determines whether or not space is available, if not enough space returns
//! the amount short in \a spaceNeeded
//! \param [in] sortedSizes
//! Requested block sizes sorted in decending order.
//! \param [in] params
//! Parameters describing the requested space
//! \param [out] spaceNeeded
//! Amount of space that the heap(s) are short of to complete space acquisition
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS IsSpaceAvailable(
AcquireParams &params,
uint32_t &spaceNeeded);
//!
//! \brief Sets up memory blocks for the requested space
//! \param [in] sortedSizes
//! Requested block sizes sorted in decending order with original index saved from params.
//! \param [in] params
//! Parameters describing the requested space
//! \param [out] blocks
//! A vector containing the memory blocks allocated
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS AllocateSpace(
AcquireParams &params,
std::vector<MemoryBlock> &blocks);
//!
//! \brief Sets up memory blocks for the requested space
//! \param [in] alignedSize
//! Aligned size of the memory requested
//! \param [in] trackerId
//! Tracker ID to be used for the allocated block
//! \param [in] staticBlock
//! Indicates that the newly created block is expected to be static
//! \param [out] freeBlock
//! Free block that will be used for the allocation of the new block
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS AllocateBlock(
uint32_t alignedSize,
uint32_t trackerId,
bool staticBlock,
MemoryBlockInternal *freeBlock);
//!
//! \brief Sets up memory blocks for the requested space
//! \param [in] alignedSize
//! Aligned size of the memory requested
//! \param [in] trackerIndex
//! Tracker index to be used for the allocated block
//! \param [in] trackerId
//! Tracker ID to be used for the allocated block
//! \param [in] staticBlock
//! Indicates that the newly created block is expected to be static
//! \param [out] freeBlock
//! Free block that will be used for the allocation of the new block
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS AllocateBlock(
uint32_t alignedSize,
uint32_t trackerIndex,
uint32_t trackerId,
bool staticBlock,
MemoryBlockInternal *freeBlock);
//!
//! \brief Sets up memory blocks for the requested space
//! \param [in] block
//! Block to be added to the sorted pool of type \a state
//! \param [in] state
//! Type of pool to be added to
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS AddBlockToSortedList(
MemoryBlockInternal *block,
MemoryBlockInternal::State state);
//!
//! \brief Sets up memory blocks for the requested space
//! \param [in] block
//! Block to be added to the sorted pool of type \a state
//! \param [in] state
//! 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
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RemoveBlockFromSortedList(
MemoryBlockInternal *block,
MemoryBlockInternal::State state);
//!
//! \brief Gets a pool type block from the sorted block pool, if pool is empty allocates a new one
//! \see m_sortedBlockList[MemoryBlockInternal::State::pool]
//! \return MemoryBlockInternal*
//! Valid pointer if success, nullptr if fail
//!
MemoryBlockInternal* GetBlockFromPool();
//!
//! \brief Removes all blocks with heaps matching \a heapId from the sorted block pool for \a state. \see m_sortedBlockList
//! \param [in] heapId
//! Heap whose blocks should be removed
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS RemoveHeapFromSortedBlockList(
uint32_t heapId);
//!
//! \brief Merges two contiguous blocks into one.
//! \param [in,out] blockCombined
//! Block into which \a blockRelease will be merged in to, this block is the output of the merge
//! \param [in] blockRelease
//! Block to be merged into \a blockCombined, this block will be added back to the pool after the merge
//! \return MOS_STATUS
//! MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS MergeBlocks(
MemoryBlockInternal *blockCombined,
MemoryBlockInternal *blockRelease);
//!
//! \brief Temporary function while MOS utilities function is added for smart pointers
//! \return std::shared_ptr<T>
//! Valid pointer if success, nullptr if fail
//!
template <class T, class... Args>
std::shared_ptr<T> MakeShared(Args&&... args)
{
std::shared_ptr<T> block = nullptr;
try
{
block = std::make_shared<T>(args...);
}
catch (const std::bad_alloc& e)
{
HEAP_ASSERTMESSAGE("Allocation of memory block failed!");
return nullptr;
}
return block;
}
//! \brief Used to retain information about the requested block sizes after those
//! sizes are sorted for the return of blocks.\see AcquireParams::m_blockSizes \see AcquireSpace
struct SortedSizePair
{
//! \brief Constructs SortedSizePair
SortedSizePair() {}
SortedSizePair(uint32_t originalIdx, uint32_t blockSize)
: m_originalIdx(originalIdx), m_blockSize(blockSize) {}
uint32_t m_originalIdx = 0; //!< Original index of the requested block size \see AcquireParams::m_blockSizes
uint32_t m_blockSize = 0; //!< Aligned block size
};
//! \brief Alignment for blocks in heap, currently fixed at a cacheline
static const uint16_t m_blockAlignment = 64;
//! \brief Alignment for heap, currently fixed at a page
static const uint16_t m_heapAlignment = MOS_PAGE_SIZE;
//! \brief Number of submissions before a refresh, currently fixed
static const uint16_t m_numSubmissionsForRefresh = 128;
//! \brief Total size of all managed heaps.
uint32_t m_totalSizeOfHeaps = 0;
//! \brief The list of block pools per heap
std::list<std::shared_ptr<HeapWithAdjacencyBlockList>> m_heaps;
//! \brief List of block pools per heap for heaps in deletion process
std::list<std::shared_ptr<HeapWithAdjacencyBlockList>> m_deletedHeaps;
//! \brief Pools of memory blocks sorted by their states based on the state indicated
//! by the latest TrackerId. The free pool is sorted in ascending order.
MemoryBlockInternal *m_sortedBlockList[MemoryBlockInternal::State::stateCount] = {nullptr};
//! \brief Number of entries in each sorted block list.
uint32_t m_sortedBlockListNumEntries[MemoryBlockInternal::State::stateCount] = {0};
//! \brief Sizes of each block pool.
//! \brief MemoryBlockInternal::State::pool type blocks have no size, and thus that pool also is expected to be size 0.
uint32_t m_sortedBlockListSizes[MemoryBlockInternal::State::stateCount] = {0};
//! \brief Used to compare to a memory block's tracker ID. If the value is greater
//! than the tracker ID, then the block is no longer in use and may be reclaimed
//! as free space, or if the block is static transitioned to allocated state so
//! it may be re-used. \see MemoryBlockInternal::m_trackerId
//! \details The client is expected to manage this tracker; it should be valid for the
//! life of the heap manager. The tracker may be updated at whatever granularity is
//! appropriate--per workload, per frame, etc. It is expected that when the
//! tracker data indicates that all blocks with tracker IDs less than or equal
//! to the udpated value may be re-used.
uint32_t *m_trackerData = nullptr;
PMOS_INTERFACE m_osInterface = nullptr; //!< OS interface used for managing graphics resources
bool m_lockHeapsOnAllocate = false; //!< All heaps allocated with the keep locked flag.
//! \brief Persistent storage for the sorted sizes used during AcquireSpace()
std::list<SortedSizePair> m_sortedSizes;
//! \brief TrackerProducer
FrameTrackerProducer *m_trackerProducer = nullptr;
//! \bried Whether trackerProducer is set
bool m_useProducer = false;
};
#endif // __MEMORY_BLOCK_MANAGER_H__