mirror of https://github.com/acidanthera/audk.git
ArmPkg/UncachedMemoryAllocationLib: Track uncached memory allocations
Keeping track of uncached memory allocations prevents doing expensive cache operations (eg: clean & invalidate) on newly allocated regions by reusing regions where possible Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16205 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
9180ab73e6
commit
1a70a690ea
|
@ -77,6 +77,9 @@
|
||||||
gArmTokenSpaceGuid.PcdVFPEnabled|0|UINT32|0x00000024
|
gArmTokenSpaceGuid.PcdVFPEnabled|0|UINT32|0x00000024
|
||||||
|
|
||||||
gArmTokenSpaceGuid.PcdArmUncachedMemoryMask|0x0000000080000000|UINT64|0x00000002
|
gArmTokenSpaceGuid.PcdArmUncachedMemoryMask|0x0000000080000000|UINT64|0x00000002
|
||||||
|
# This PCD will free the unallocated buffers if their size reach this threshold.
|
||||||
|
# We set the default value to 512MB.
|
||||||
|
gArmTokenSpaceGuid.PcdArmFreeUncachedMemorySizeThreshold|0x20000000|UINT64|0x00000043
|
||||||
gArmTokenSpaceGuid.PcdArmCacheOperationThreshold|1024|UINT32|0x00000003
|
gArmTokenSpaceGuid.PcdArmCacheOperationThreshold|1024|UINT32|0x00000003
|
||||||
gArmTokenSpaceGuid.PcdCpuVectorBaseAddress|0xffff0000|UINT32|0x00000004
|
gArmTokenSpaceGuid.PcdCpuVectorBaseAddress|0xffff0000|UINT32|0x00000004
|
||||||
gArmTokenSpaceGuid.PcdCpuResetAddress|0x00000000|UINT32|0x00000005
|
gArmTokenSpaceGuid.PcdCpuResetAddress|0x00000000|UINT32|0x00000005
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
a buffer.
|
a buffer.
|
||||||
|
|
||||||
Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
|
Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
|
||||||
|
Copyright (c) 2014, AMR Ltd. All rights reserved.<BR>
|
||||||
|
|
||||||
This program and the accompanying materials
|
This program and the accompanying materials
|
||||||
are licensed and made available under the terms and conditions of the BSD License
|
are licensed and made available under the terms and conditions of the BSD License
|
||||||
|
@ -46,60 +47,228 @@ UncachedInternalAllocateAlignedPages (
|
||||||
UINT64 gAttributes;
|
UINT64 gAttributes;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
EFI_PHYSICAL_ADDRESS Base;
|
||||||
VOID *Allocation;
|
VOID *Allocation;
|
||||||
UINTN Pages;
|
UINTN Pages;
|
||||||
|
EFI_MEMORY_TYPE MemoryType;
|
||||||
|
BOOLEAN Allocated;
|
||||||
LIST_ENTRY Link;
|
LIST_ENTRY Link;
|
||||||
} FREE_PAGE_NODE;
|
} FREE_PAGE_NODE;
|
||||||
|
|
||||||
LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);
|
STATIC LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);
|
||||||
|
// Track the size of the non-allocated buffer in the linked-list
|
||||||
|
STATIC UINTN mFreedBufferSize = 0;
|
||||||
|
|
||||||
VOID
|
/**
|
||||||
AddPagesToList (
|
* This function firstly checks if the requested allocation can fit into one
|
||||||
IN VOID *Allocation,
|
* of the previously allocated buffer.
|
||||||
UINTN Pages
|
* If the requested allocation does not fit in the existing pool then
|
||||||
|
* the function makes a new allocation.
|
||||||
|
*
|
||||||
|
* @param MemoryType Type of memory requested for the new allocation
|
||||||
|
* @param Pages Number of requested page
|
||||||
|
* @param Alignment Required alignment
|
||||||
|
* @param Allocation Address of the newly allocated buffer
|
||||||
|
*
|
||||||
|
* @return EFI_SUCCESS If the function manage to allocate a buffer
|
||||||
|
* @return !EFI_SUCCESS If the function did not manage to allocate a buffer
|
||||||
|
*/
|
||||||
|
STATIC
|
||||||
|
EFI_STATUS
|
||||||
|
AllocatePagesFromList (
|
||||||
|
IN EFI_MEMORY_TYPE MemoryType,
|
||||||
|
IN UINTN Pages,
|
||||||
|
IN UINTN Alignment,
|
||||||
|
OUT VOID **Allocation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
LIST_ENTRY *Link;
|
||||||
|
FREE_PAGE_NODE *Node;
|
||||||
FREE_PAGE_NODE *NewNode;
|
FREE_PAGE_NODE *NewNode;
|
||||||
|
UINTN AlignmentMask;
|
||||||
|
EFI_PHYSICAL_ADDRESS Memory;
|
||||||
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
|
||||||
|
|
||||||
NewNode = AllocatePool (sizeof (LIST_ENTRY));
|
// Alignment must be a power of two or zero.
|
||||||
|
ASSERT ((Alignment & (Alignment - 1)) == 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Look in our list for the smallest page that could satisfy the new allocation
|
||||||
|
//
|
||||||
|
NewNode = NULL;
|
||||||
|
for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
|
||||||
|
Node = BASE_CR (Link, FREE_PAGE_NODE, Link);
|
||||||
|
if ((Node->Allocated == FALSE) && (Node->MemoryType == MemoryType)) {
|
||||||
|
// We have a node that fits our requirements
|
||||||
|
if (((UINTN)Node->Base & (Alignment - 1)) == 0) {
|
||||||
|
// We found a page that matches the page size
|
||||||
|
if (Node->Pages == Pages) {
|
||||||
|
Node->Allocated = TRUE;
|
||||||
|
Node->Allocation = (VOID*)(UINTN)Node->Base;
|
||||||
|
*Allocation = Node->Allocation;
|
||||||
|
|
||||||
|
// Update the size of the freed buffer
|
||||||
|
mFreedBufferSize -= Pages * EFI_PAGE_SIZE;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
} else if (Node->Pages > Pages) {
|
||||||
if (NewNode == NULL) {
|
if (NewNode == NULL) {
|
||||||
ASSERT (FALSE);
|
// It is the first node that could contain our new allocation
|
||||||
return;
|
NewNode = Node;
|
||||||
|
} else if (NewNode->Pages > Node->Pages) {
|
||||||
|
// This node offers a smaller number of page.
|
||||||
|
NewNode = Node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if we have found a node that could contain our new allocation
|
||||||
|
if (NewNode != NULL) {
|
||||||
|
NewNode->Allocated = TRUE;
|
||||||
|
Node->Allocation = (VOID*)(UINTN)Node->Base;
|
||||||
|
*Allocation = Node->Allocation;
|
||||||
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
NewNode->Allocation = Allocation;
|
//
|
||||||
|
// Otherwise, we need to allocate a new buffer
|
||||||
|
//
|
||||||
|
|
||||||
|
// We do not want to over-allocate in case the alignment requirement does not
|
||||||
|
// require extra pages
|
||||||
|
if (Alignment > EFI_PAGE_SIZE) {
|
||||||
|
AlignmentMask = Alignment - 1;
|
||||||
|
Pages += EFI_SIZE_TO_PAGES (Alignment);
|
||||||
|
} else {
|
||||||
|
AlignmentMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
// We are making an assumption that all of memory has the same default attributes
|
||||||
|
gAttributes = Descriptor.Attributes;
|
||||||
|
} else {
|
||||||
|
gBS->FreePages (Memory, Pages);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
gBS->FreePages (Memory, Pages);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewNode = AllocatePool (sizeof (FREE_PAGE_NODE));
|
||||||
|
if (NewNode == NULL) {
|
||||||
|
ASSERT (FALSE);
|
||||||
|
gBS->FreePages (Memory, Pages);
|
||||||
|
return EFI_OUT_OF_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewNode->Base = Memory;
|
||||||
|
NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & ~AlignmentMask);
|
||||||
NewNode->Pages = Pages;
|
NewNode->Pages = Pages;
|
||||||
|
NewNode->Allocated = TRUE;
|
||||||
|
NewNode->MemoryType = MemoryType;
|
||||||
|
|
||||||
InsertTailList (&mPageList, &NewNode->Link);
|
InsertTailList (&mPageList, &NewNode->Link);
|
||||||
|
|
||||||
|
*Allocation = NewNode->Allocation;
|
||||||
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the memory allocation
|
||||||
|
*
|
||||||
|
* This function will actually try to find the allocation in the linked list.
|
||||||
|
* And it will then mark the entry as freed.
|
||||||
|
*
|
||||||
|
* @param Allocation Base address of the buffer to free
|
||||||
|
*
|
||||||
|
* @return EFI_SUCCESS The allocation has been freed
|
||||||
|
* @return EFI_NOT_FOUND The allocation was not found in the pool.
|
||||||
|
* @return EFI_INVALID_PARAMETER If Allocation is NULL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
STATIC
|
||||||
|
EFI_STATUS
|
||||||
|
FreePagesFromList (
|
||||||
|
IN VOID *Allocation
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LIST_ENTRY *Link;
|
||||||
|
FREE_PAGE_NODE *Node;
|
||||||
|
|
||||||
VOID
|
if (Allocation == NULL) {
|
||||||
RemovePagesFromList (
|
return EFI_INVALID_PARAMETER;
|
||||||
OUT VOID *Allocation,
|
}
|
||||||
OUT UINTN *Pages
|
|
||||||
|
for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
|
||||||
|
Node = BASE_CR (Link, FREE_PAGE_NODE, Link);
|
||||||
|
if ((UINTN)Node->Allocation == (UINTN)Allocation) {
|
||||||
|
Node->Allocated = FALSE;
|
||||||
|
|
||||||
|
// Update the size of the freed buffer
|
||||||
|
mFreedBufferSize += Node->Pages * EFI_PAGE_SIZE;
|
||||||
|
|
||||||
|
// If the size of the non-allocated reaches the threshold we raise a warning.
|
||||||
|
// It might be an expected behaviour in some cases.
|
||||||
|
// We might device to free some of these buffers later on.
|
||||||
|
if (mFreedBufferSize > PcdGet64 (PcdArmFreeUncachedMemorySizeThreshold)) {
|
||||||
|
DEBUG ((EFI_D_WARN, "Warning: The list of non-allocated buffer has reach the threshold.\n"));
|
||||||
|
}
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EFI_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is automatically invoked when the driver exits
|
||||||
|
* It frees all the non-allocated memory buffer.
|
||||||
|
* This function is not responsible to free allocated buffer (eg: case of memory leak,
|
||||||
|
* runtime allocation).
|
||||||
|
*/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
UncachedMemoryAllocationLibDestructor (
|
||||||
|
IN EFI_HANDLE ImageHandle,
|
||||||
|
IN EFI_SYSTEM_TABLE *SystemTable
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LIST_ENTRY *Link;
|
LIST_ENTRY *Link;
|
||||||
FREE_PAGE_NODE *OldNode;
|
FREE_PAGE_NODE *OldNode;
|
||||||
|
|
||||||
*Pages = 0;
|
// Test if the list is empty
|
||||||
|
Link = mPageList.ForwardLink;
|
||||||
|
if (Link == &mPageList) {
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {
|
// Free all the pages and nodes
|
||||||
|
do {
|
||||||
OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);
|
OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);
|
||||||
if (OldNode->Allocation == Allocation) {
|
// Point to the next entry
|
||||||
*Pages = OldNode->Pages;
|
Link = Link->ForwardLink;
|
||||||
|
|
||||||
|
// We only free the non-allocated buffer
|
||||||
|
if (OldNode->Allocated == FALSE) {
|
||||||
|
gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)OldNode->Base, OldNode->Pages);
|
||||||
RemoveEntryList (&OldNode->Link);
|
RemoveEntryList (&OldNode->Link);
|
||||||
FreePool (OldNode);
|
FreePool (OldNode);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} while (Link != &mPageList);
|
||||||
|
|
||||||
return;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Converts a cached or uncached address to a physical address suitable for use in SoC registers.
|
Converts a cached or uncached address to a physical address suitable for use in SoC registers.
|
||||||
|
|
||||||
|
@ -176,75 +345,20 @@ UncachedInternalAllocateAlignedPages (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
EFI_STATUS Status;
|
||||||
EFI_PHYSICAL_ADDRESS Memory;
|
VOID *Allocation;
|
||||||
EFI_PHYSICAL_ADDRESS AlignedMemory;
|
|
||||||
UINTN AlignmentMask;
|
|
||||||
UINTN UnalignedPages;
|
|
||||||
UINTN RealPages;
|
|
||||||
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Alignment must be a power of two or zero.
|
|
||||||
//
|
|
||||||
ASSERT ((Alignment & (Alignment - 1)) == 0);
|
|
||||||
|
|
||||||
if (Pages == 0) {
|
if (Pages == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (Alignment > EFI_PAGE_SIZE) {
|
|
||||||
//
|
|
||||||
// Caculate the total number of pages since alignment is larger than page size.
|
|
||||||
//
|
|
||||||
AlignmentMask = Alignment - 1;
|
|
||||||
RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
|
|
||||||
//
|
|
||||||
// Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.
|
|
||||||
//
|
|
||||||
ASSERT (RealPages > Pages);
|
|
||||||
|
|
||||||
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory);
|
Allocation = NULL;
|
||||||
|
Status = AllocatePagesFromList (MemoryType, Pages, Alignment, &Allocation);
|
||||||
if (EFI_ERROR (Status)) {
|
if (EFI_ERROR (Status)) {
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;
|
|
||||||
UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);
|
|
||||||
if (UnalignedPages > 0) {
|
|
||||||
//
|
|
||||||
// Free first unaligned page(s).
|
|
||||||
//
|
|
||||||
Status = gBS->FreePages (Memory, UnalignedPages);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
}
|
|
||||||
Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages));
|
|
||||||
UnalignedPages = RealPages - Pages - UnalignedPages;
|
|
||||||
if (UnalignedPages > 0) {
|
|
||||||
//
|
|
||||||
// Free last unaligned page(s).
|
|
||||||
//
|
|
||||||
Status = gBS->FreePages (Memory, UnalignedPages);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
//
|
return Allocation;
|
||||||
// Do not over-allocate pages in this case.
|
|
||||||
//
|
|
||||||
Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
|
|
||||||
if (EFI_ERROR (Status)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
AlignedMemory = (UINTN) Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);
|
|
||||||
if (!EFI_ERROR (Status)) {
|
|
||||||
// We are making an assumption that all of memory has the same default attributes
|
|
||||||
gAttributes = Descriptor.Attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
|
|
||||||
return (VOID *)(UINTN)Memory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -255,21 +369,10 @@ UncachedFreeAlignedPages (
|
||||||
IN UINTN Pages
|
IN UINTN Pages
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EFI_STATUS Status;
|
FreePagesFromList (Buffer);
|
||||||
EFI_PHYSICAL_ADDRESS Memory;
|
|
||||||
|
|
||||||
ASSERT (Pages != 0);
|
|
||||||
|
|
||||||
Memory = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;
|
|
||||||
Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), gAttributes);
|
|
||||||
|
|
||||||
Status = gBS->FreePages (Memory, Pages);
|
|
||||||
ASSERT_EFI_ERROR (Status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VOID *
|
VOID *
|
||||||
UncachedInternalAllocateAlignedPool (
|
UncachedInternalAllocateAlignedPool (
|
||||||
IN EFI_MEMORY_TYPE PoolType,
|
IN EFI_MEMORY_TYPE PoolType,
|
||||||
|
@ -293,8 +396,6 @@ UncachedInternalAllocateAlignedPool (
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddPagesToList ((VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize));
|
|
||||||
|
|
||||||
return (VOID *) AlignedAddress;
|
return (VOID *) AlignedAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,11 +533,7 @@ UncachedFreeAlignedPool (
|
||||||
IN VOID *Allocation
|
IN VOID *Allocation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
UINTN Pages;
|
UncachedFreePages (Allocation, 0);
|
||||||
|
|
||||||
RemovePagesFromList (Allocation, &Pages);
|
|
||||||
|
|
||||||
UncachedFreePages (Allocation, Pages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID *
|
VOID *
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
VERSION_STRING = 1.0
|
VERSION_STRING = 1.0
|
||||||
LIBRARY_CLASS = UncachedMemoryAllocationLib
|
LIBRARY_CLASS = UncachedMemoryAllocationLib
|
||||||
|
|
||||||
|
DESTRUCTOR = UncachedMemoryAllocationLibDestructor
|
||||||
|
|
||||||
[Sources.common]
|
[Sources.common]
|
||||||
UncachedMemoryAllocationLib.c
|
UncachedMemoryAllocationLib.c
|
||||||
|
|
||||||
|
@ -34,5 +36,8 @@
|
||||||
BaseLib
|
BaseLib
|
||||||
ArmLib
|
ArmLib
|
||||||
MemoryAllocationLib
|
MemoryAllocationLib
|
||||||
|
PcdLib
|
||||||
DxeServicesTableLib
|
DxeServicesTableLib
|
||||||
|
|
||||||
|
[Pcd]
|
||||||
|
gArmTokenSpaceGuid.PcdArmFreeUncachedMemorySizeThreshold
|
||||||
|
|
Loading…
Reference in New Issue