aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs263
1 files changed, 167 insertions, 96 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
index bb4989fc..f35a3c36 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
+using System.Diagnostics;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
@@ -13,7 +14,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
private int _blockOrdersCount;
- private KMemoryRegionBlock[] _blocks;
+ private readonly KMemoryRegionBlock[] _blocks;
+
+ private readonly ushort[] _pageReferenceCounts;
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
{
@@ -80,9 +83,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
+ _pageReferenceCounts = new ushort[size / KPageTableBase.PageSize];
+
if (size != 0)
{
- FreePages(address, size / KMemoryManager.PageSize);
+ FreePages(address, size / KPageTableBase.PageSize);
}
}
@@ -90,15 +95,33 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
lock (_blocks)
{
- return AllocatePagesImpl(pagesCount, backwards, out pageList);
+ KernelResult result = AllocatePagesImpl(pagesCount, backwards, out pageList);
+
+ if (result == KernelResult.Success)
+ {
+ foreach (var node in pageList)
+ {
+ IncrementPagesReferenceCount(node.Address, node.PagesCount);
+ }
+ }
+
+ return result;
}
}
- public ulong AllocatePagesContiguous(ulong pagesCount, bool backwards)
+ public ulong AllocatePagesContiguous(KernelContext context, ulong pagesCount, bool backwards)
{
lock (_blocks)
{
- return AllocatePagesContiguousImpl(pagesCount, backwards);
+ ulong address = AllocatePagesContiguousImpl(pagesCount, backwards);
+
+ if (address != 0)
+ {
+ IncrementPagesReferenceCount(address, pagesCount);
+ context.Memory.Commit(address - DramMemoryMap.DramBase, pagesCount * KPageTableBase.PageSize);
+ }
+
+ return address;
}
}
@@ -124,7 +147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong bestFitBlockSize = 1UL << block.Order;
- ulong blockPagesCount = bestFitBlockSize / KMemoryManager.PageSize;
+ ulong blockPagesCount = bestFitBlockSize / KPageTableBase.PageSize;
// Check if this is the best fit for this page size.
// If so, try allocating as much requested pages as possible.
@@ -185,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
int blockIndex = 0;
- while ((1UL << _blocks[blockIndex].Order) / KMemoryManager.PageSize < pagesCount)
+ while ((1UL << _blocks[blockIndex].Order) / KPageTableBase.PageSize < pagesCount)
{
if (++blockIndex >= _blocks.Length)
{
@@ -197,11 +220,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong address = AllocatePagesForOrder(blockIndex, backwards, tightestFitBlockSize);
- ulong requiredSize = pagesCount * KMemoryManager.PageSize;
+ ulong requiredSize = pagesCount * KPageTableBase.PageSize;
if (address != 0 && tightestFitBlockSize > requiredSize)
{
- FreePages(address + requiredSize, (tightestFitBlockSize - requiredSize) / KMemoryManager.PageSize);
+ FreePages(address + requiredSize, (tightestFitBlockSize - requiredSize) / KPageTableBase.PageSize);
}
return address;
@@ -327,136 +350,120 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (firstFreeBlockSize > bestFitBlockSize)
{
- FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
+ FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KPageTableBase.PageSize);
}
}
return address;
}
- public void FreePage(ulong address)
- {
- lock (_blocks)
- {
- FreePages(address, 1);
- }
- }
-
- public void FreePages(KPageList pageList)
+ private void FreePages(ulong address, ulong pagesCount)
{
lock (_blocks)
{
- foreach (KPageNode pageNode in pageList)
- {
- FreePages(pageNode.Address, pageNode.PagesCount);
- }
- }
- }
-
- private void FreePages(ulong address, ulong pagesCount)
- {
- ulong endAddr = address + pagesCount * KMemoryManager.PageSize;
+ ulong endAddr = address + pagesCount * KPageTableBase.PageSize;
- int blockIndex = _blockOrdersCount - 1;
+ int blockIndex = _blockOrdersCount - 1;
- ulong addressRounded = 0;
- ulong endAddrTruncated = 0;
+ ulong addressRounded = 0;
+ ulong endAddrTruncated = 0;
- for (; blockIndex >= 0; blockIndex--)
- {
- KMemoryRegionBlock allocInfo = _blocks[blockIndex];
+ for (; blockIndex >= 0; blockIndex--)
+ {
+ KMemoryRegionBlock allocInfo = _blocks[blockIndex];
- int blockSize = 1 << allocInfo.Order;
+ int blockSize = 1 << allocInfo.Order;
- addressRounded = BitUtils.AlignUp (address, blockSize);
- endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize);
+ addressRounded = BitUtils.AlignUp (address, blockSize);
+ endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize);
- if (addressRounded < endAddrTruncated)
- {
- break;
+ if (addressRounded < endAddrTruncated)
+ {
+ break;
+ }
}
- }
- void FreeRegion(ulong currAddress)
- {
- for (int currBlockIndex = blockIndex;
- currBlockIndex < _blockOrdersCount && currAddress != 0;
- currBlockIndex++)
+ void FreeRegion(ulong currAddress)
{
- KMemoryRegionBlock block = _blocks[currBlockIndex];
+ for (int currBlockIndex = blockIndex;
+ currBlockIndex < _blockOrdersCount && currAddress != 0;
+ currBlockIndex++)
+ {
+ KMemoryRegionBlock block = _blocks[currBlockIndex];
- block.FreeCount++;
+ block.FreeCount++;
- ulong freedBlocks = (currAddress - block.StartAligned) >> block.Order;
+ ulong freedBlocks = (currAddress - block.StartAligned) >> block.Order;
- int index = (int)freedBlocks;
+ int index = (int)freedBlocks;
- for (int level = block.MaxLevel - 1; level >= 0; level--, index /= 64)
- {
- long mask = block.Masks[level][index / 64];
+ for (int level = block.MaxLevel - 1; level >= 0; level--, index /= 64)
+ {
+ long mask = block.Masks[level][index / 64];
- block.Masks[level][index / 64] = mask | (1L << (index & 63));
+ block.Masks[level][index / 64] = mask | (1L << (index & 63));
- if (mask != 0)
- {
- break;
+ if (mask != 0)
+ {
+ break;
+ }
}
- }
- int blockSizeDelta = 1 << (block.NextOrder - block.Order);
+ int blockSizeDelta = 1 << (block.NextOrder - block.Order);
- int freedBlocksTruncated = BitUtils.AlignDown((int)freedBlocks, blockSizeDelta);
+ int freedBlocksTruncated = BitUtils.AlignDown((int)freedBlocks, blockSizeDelta);
- if (!block.TryCoalesce(freedBlocksTruncated, blockSizeDelta))
- {
- break;
- }
+ if (!block.TryCoalesce(freedBlocksTruncated, blockSizeDelta))
+ {
+ break;
+ }
- currAddress = block.StartAligned + ((ulong)freedBlocksTruncated << block.Order);
+ currAddress = block.StartAligned + ((ulong)freedBlocksTruncated << block.Order);
+ }
}
- }
- // Free inside aligned region.
- ulong baseAddress = addressRounded;
+ // Free inside aligned region.
+ ulong baseAddress = addressRounded;
- while (baseAddress < endAddrTruncated)
- {
- ulong blockSize = 1UL << _blocks[blockIndex].Order;
-
- FreeRegion(baseAddress);
+ while (baseAddress < endAddrTruncated)
+ {
+ ulong blockSize = 1UL << _blocks[blockIndex].Order;
- baseAddress += blockSize;
- }
+ FreeRegion(baseAddress);
- int nextBlockIndex = blockIndex - 1;
+ baseAddress += blockSize;
+ }
- // Free region between Address and aligned region start.
- baseAddress = addressRounded;
+ int nextBlockIndex = blockIndex - 1;
- for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
- {
- ulong blockSize = 1UL << _blocks[blockIndex].Order;
+ // Free region between Address and aligned region start.
+ baseAddress = addressRounded;
- while (baseAddress - blockSize >= address)
+ for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
{
- baseAddress -= blockSize;
+ ulong blockSize = 1UL << _blocks[blockIndex].Order;
- FreeRegion(baseAddress);
- }
- }
+ while (baseAddress - blockSize >= address)
+ {
+ baseAddress -= blockSize;
- // Free region between aligned region end and End Address.
- baseAddress = endAddrTruncated;
+ FreeRegion(baseAddress);
+ }
+ }
- for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
- {
- ulong blockSize = 1UL << _blocks[blockIndex].Order;
+ // Free region between aligned region end and End Address.
+ baseAddress = endAddrTruncated;
- while (baseAddress + blockSize <= endAddr)
+ for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
{
- FreeRegion(baseAddress);
+ ulong blockSize = 1UL << _blocks[blockIndex].Order;
- baseAddress += blockSize;
+ while (baseAddress + blockSize <= endAddr)
+ {
+ FreeRegion(baseAddress);
+
+ baseAddress += blockSize;
+ }
}
}
}
@@ -477,12 +484,76 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
KMemoryRegionBlock block = _blocks[blockIndex];
- ulong blockPagesCount = (1UL << block.Order) / KMemoryManager.PageSize;
+ ulong blockPagesCount = (1UL << block.Order) / KPageTableBase.PageSize;
availablePages += blockPagesCount * block.FreeCount;
}
return availablePages;
}
+
+ public void IncrementPagesReferenceCount(ulong address, ulong pagesCount)
+ {
+ ulong index = GetPageOffset(address);
+ ulong endIndex = index + pagesCount;
+
+ while (index < endIndex)
+ {
+ ushort referenceCount = ++_pageReferenceCounts[index];
+ Debug.Assert(referenceCount >= 1);
+
+ index++;
+ }
+ }
+
+ public void DecrementPagesReferenceCount(ulong address, ulong pagesCount)
+ {
+ ulong index = GetPageOffset(address);
+ ulong endIndex = index + pagesCount;
+
+ ulong freeBaseIndex = 0;
+ ulong freePagesCount = 0;
+
+ while (index < endIndex)
+ {
+ Debug.Assert(_pageReferenceCounts[index] > 0);
+ ushort referenceCount = --_pageReferenceCounts[index];
+
+ if (referenceCount == 0)
+ {
+ if (freePagesCount != 0)
+ {
+ freePagesCount++;
+ }
+ else
+ {
+ freeBaseIndex = index;
+ freePagesCount = 1;
+ }
+ }
+ else if (freePagesCount != 0)
+ {
+ FreePages(Address + freeBaseIndex * KPageTableBase.PageSize, freePagesCount);
+ freePagesCount = 0;
+ }
+
+ index++;
+ }
+
+ if (freePagesCount != 0)
+ {
+ FreePages(Address + freeBaseIndex * KPageTableBase.PageSize, freePagesCount);
+ }
+ }
+
+ public ulong GetPageOffset(ulong address)
+ {
+ return (address - Address) / KPageTableBase.PageSize;
+ }
+
+ public ulong GetPageOffsetFromEnd(ulong address)
+ {
+ return (EndAddr - address) / KPageTableBase.PageSize;
+ }
}
} \ No newline at end of file