Revision: 16205
          http://sourceforge.net/p/edk2/code/16205
Author:   oliviermartin
Date:     2014-10-10 11:24:11 +0000 (Fri, 10 Oct 2014)
Log Message:
-----------
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 <[email protected]>

Modified Paths:
--------------
    trunk/edk2/ArmPkg/ArmPkg.dec
    
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
    
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf

Modified: trunk/edk2/ArmPkg/ArmPkg.dec
===================================================================
--- trunk/edk2/ArmPkg/ArmPkg.dec        2014-10-10 11:22:50 UTC (rev 16204)
+++ trunk/edk2/ArmPkg/ArmPkg.dec        2014-10-10 11:24:11 UTC (rev 16205)
@@ -77,6 +77,9 @@
   gArmTokenSpaceGuid.PcdVFPEnabled|0|UINT32|0x00000024
 
   
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.PcdCpuVectorBaseAddress|0xffff0000|UINT32|0x00000004
   gArmTokenSpaceGuid.PcdCpuResetAddress|0x00000000|UINT32|0x00000005

Modified: 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
===================================================================
--- 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
 2014-10-10 11:22:50 UTC (rev 16204)
+++ 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
 2014-10-10 11:24:11 UTC (rev 16205)
@@ -3,6 +3,7 @@
   a buffer.
 
   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
   are licensed and made available under the terms and conditions of the BSD 
License
@@ -46,60 +47,228 @@
 UINT64  gAttributes;
 
 typedef struct {
-  VOID        *Allocation;
-  UINTN       Pages;
-  LIST_ENTRY  Link;
+  EFI_PHYSICAL_ADDRESS  Base;
+  VOID                  *Allocation;
+  UINTN                 Pages;
+  EFI_MEMORY_TYPE       MemoryType;
+  BOOLEAN               Allocated;
+  LIST_ENTRY            Link;
 } 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 (
-  IN VOID   *Allocation,
-  UINTN     Pages
+/**
+ * This function firstly checks if the requested allocation can fit into one
+ * of the previously allocated buffer.
+ * 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;
+  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) {
+            // It is the first node that could contain our new allocation
+            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;
+  }
+
+  //
+  // 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);
-    return;
+    gBS->FreePages (Memory, Pages);
+    return EFI_OUT_OF_RESOURCES;
   }
 
-  NewNode->Allocation = Allocation;
+  NewNode->Base       = Memory;
+  NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & 
~AlignmentMask);
   NewNode->Pages      = Pages;
+  NewNode->Allocated  = TRUE;
+  NewNode->MemoryType = MemoryType;
 
   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
-RemovePagesFromList (
-  OUT VOID  *Allocation,
-  OUT UINTN *Pages
+  if (Allocation == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  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;
   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);
-    if (OldNode->Allocation == Allocation) {
-      *Pages = OldNode->Pages;
+    // Point to the next entry
+    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);
       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.
 
@@ -175,76 +344,21 @@
   IN UINTN            Alignment
   )
 {
-  EFI_STATUS                        Status;
-  EFI_PHYSICAL_ADDRESS              Memory;
-  EFI_PHYSICAL_ADDRESS              AlignedMemory;
-  UINTN                             AlignmentMask;
-  UINTN                             UnalignedPages;
-  UINTN                             RealPages;
-  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;
+  EFI_STATUS Status;
+  VOID   *Allocation;
 
-  //
-  // Alignment must be a power of two or zero.
-  //
-  ASSERT ((Alignment & (Alignment - 1)) == 0);
-
   if (Pages == 0) {
     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);
-    if (EFI_ERROR (Status)) {
-      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);
-    }
+  Allocation = NULL;
+  Status = AllocatePagesFromList (MemoryType, Pages, Alignment, &Allocation);
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    return NULL;
   } else {
-    //
-    // Do not over-allocate pages in this case.
-    //
-    Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);
-    if (EFI_ERROR (Status)) {
-      return NULL;
-    }
-    AlignedMemory  = (UINTN) Memory;
+    return Allocation;
   }
-
-  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 @@
   IN UINTN  Pages
   )
 {
-  EFI_STATUS            Status;
-  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);
+  FreePagesFromList (Buffer);
 }
 
 
-
-
 VOID *
 UncachedInternalAllocateAlignedPool (
   IN EFI_MEMORY_TYPE  PoolType,
@@ -293,8 +396,6 @@
     return NULL;
   }
 
-  AddPagesToList ((VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES 
(AllocationSize));
-
   return (VOID *) AlignedAddress;
 }
 
@@ -432,11 +533,7 @@
   IN VOID   *Allocation
   )
 {
-  UINTN   Pages;
-
-  RemovePagesFromList (Allocation, &Pages);
-
-  UncachedFreePages (Allocation, Pages);
+  UncachedFreePages (Allocation, 0);
 }
 
 VOID *

Modified: 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf
===================================================================
--- 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf
       2014-10-10 11:22:50 UTC (rev 16204)
+++ 
trunk/edk2/ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf
       2014-10-10 11:24:11 UTC (rev 16205)
@@ -23,6 +23,8 @@
   VERSION_STRING                 = 1.0
   LIBRARY_CLASS                  = UncachedMemoryAllocationLib
 
+  DESTRUCTOR                     = UncachedMemoryAllocationLibDestructor
+
 [Sources.common]
   UncachedMemoryAllocationLib.c
 
@@ -34,5 +36,8 @@
   BaseLib
   ArmLib
   MemoryAllocationLib
+  PcdLib
   DxeServicesTableLib
 
+[Pcd]
+  gArmTokenSpaceGuid.PcdArmFreeUncachedMemorySizeThreshold


------------------------------------------------------------------------------
Meet PCI DSS 3.0 Compliance Requirements with EventLog Analyzer
Achieve PCI DSS 3.0 Compliant Status with Out-of-the-box PCI DSS Reports
Are you Audit-Ready for PCI DSS 3.0 Compliance? Download White paper
Comply to PCI DSS 3.0 Requirement 10 and 11.5 with EventLog Analyzer
http://pubads.g.doubleclick.net/gampad/clk?id=154622311&iu=/4140/ostg.clktrk
_______________________________________________
edk2-commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-commits

Reply via email to