[HLE] Resolve backing aliases for readers that hold an access window
SharedBackingViews publishes its view snapshot as an alias identity, and a reader can enter an alias access window once and resolve guest pages to their backing alias without the views lock until the snapshot changes.
This commit is contained in:
parent
e47798a854
commit
8324faed84
|
|
@ -1174,6 +1174,18 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||
}
|
||||
}
|
||||
|
||||
public object? BackingAliasSnapshot => _backedSpace?.AliasSnapshot;
|
||||
|
||||
public bool TryEnterBackingAliasAccess() => !_disposed && _backedSpace is { } backed && backed.TryEnterAliasAccess();
|
||||
|
||||
public void ExitBackingAliasAccess() => _backedSpace?.ExitAliasAccess();
|
||||
|
||||
public bool TryResolveBackingAlias(ulong address, ulong size, out ulong alias)
|
||||
{
|
||||
alias = 0;
|
||||
return !_disposed && _backedSpace is { } backed && backed.TryResolveAlias(address, size, out alias);
|
||||
}
|
||||
|
||||
private bool HasBackingOwner() => !_disposed && _backedSpace != null;
|
||||
|
||||
// A span may cross adjacent view records; the owner validates full coverage.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,14 @@ public sealed class GuestSpaceOwner : IDisposable
|
|||
// Reads through a view that covers the whole range without any lock; false otherwise.
|
||||
public bool TryReadSingleView(ulong address, Span<byte> data) => _views.TryReadSingleView(address, data);
|
||||
|
||||
public object AliasSnapshot => _views.AliasSnapshot;
|
||||
|
||||
public bool TryEnterAliasAccess() => _views.TryEnterAliasAccess();
|
||||
|
||||
public void ExitAliasAccess() => _views.ExitAliasAccess();
|
||||
|
||||
public bool TryResolveAlias(ulong address, ulong size, out ulong alias) => _views.TryResolveAlias(address, size, out alias);
|
||||
|
||||
public bool TryCopyBacking(ulong destination, ulong source, ulong size) => _views.TryCopyBacking(destination, source, size);
|
||||
|
||||
public bool IsBacked(ulong address, ulong size) => _views.Contains(address, size);
|
||||
|
|
|
|||
|
|
@ -623,7 +623,40 @@ public sealed unsafe class SharedBackingViews : IDisposable
|
|||
}
|
||||
|
||||
Interlocked.Increment(ref _activeAccesses);
|
||||
var snapshot = Volatile.Read(ref _snapshot);
|
||||
if (TryFindAlias(Volatile.Read(ref _snapshot), address, size, out target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref _activeAccesses);
|
||||
return false;
|
||||
}
|
||||
|
||||
public object AliasSnapshot => Volatile.Read(ref _snapshot);
|
||||
|
||||
public bool TryEnterAliasAccess()
|
||||
{
|
||||
Interlocked.Increment(ref _activeAccesses);
|
||||
if (!Volatile.Read(ref _disposed) && _backing != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref _activeAccesses);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ExitAliasAccess() => Interlocked.Decrement(ref _activeAccesses);
|
||||
|
||||
public bool TryResolveAlias(ulong address, ulong size, out ulong target)
|
||||
{
|
||||
target = 0;
|
||||
return size != 0 && ulong.MaxValue - address >= size && TryFindAlias(Volatile.Read(ref _snapshot), address, size, out target);
|
||||
}
|
||||
|
||||
private bool TryFindAlias(ViewRecord[] snapshot, ulong address, ulong size, out ulong target)
|
||||
{
|
||||
target = 0;
|
||||
var low = 0;
|
||||
var high = snapshot.Length - 1;
|
||||
var found = -1;
|
||||
|
|
@ -655,7 +688,6 @@ public sealed unsafe class SharedBackingViews : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref _activeAccesses);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,4 +24,18 @@ public interface IGuestBackedSpace
|
|||
// Copies through the backing alias only: no protection change, no store notification.
|
||||
bool TryWriteBacking(ulong address, ReadOnlySpan<byte> data);
|
||||
bool TryReadBacking(ulong address, Span<byte> data);
|
||||
|
||||
object? BackingAliasSnapshot => null;
|
||||
|
||||
bool TryEnterBackingAliasAccess() => false;
|
||||
|
||||
void ExitBackingAliasAccess()
|
||||
{
|
||||
}
|
||||
|
||||
bool TryResolveBackingAlias(ulong address, ulong size, out ulong alias)
|
||||
{
|
||||
alias = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,31 @@ public sealed unsafe class SharedBackingTransferTests
|
|||
Assert.Equal(Marker, *(ulong*)(mapping.Address + Segment));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AResolvedAliasReadsTheViewBytesUntilTheMappingChanges()
|
||||
{
|
||||
if (!Supported) return;
|
||||
using var mapping = new TransferMappings();
|
||||
Assert.True(mapping.Store.TryWriteBacking(mapping.Address + Segment + 8, BitConverter.GetBytes(Marker)));
|
||||
var snapshot = mapping.Store.AliasSnapshot;
|
||||
Assert.True(mapping.Store.TryEnterAliasAccess());
|
||||
try
|
||||
{
|
||||
Assert.True(mapping.Store.TryResolveAlias(mapping.Address + Segment, 16, out var alias));
|
||||
Assert.Equal(Marker, *(ulong*)(alias + 8));
|
||||
Assert.Same(snapshot, mapping.Store.AliasSnapshot);
|
||||
Assert.False(mapping.Store.TryResolveAlias(mapping.Address + Segment - 8, 16, out _));
|
||||
}
|
||||
finally
|
||||
{
|
||||
mapping.Store.ExitAliasAccess();
|
||||
}
|
||||
|
||||
Assert.True(mapping.Store.Unmap(mapping.Address + Segment, Segment, out _));
|
||||
Assert.NotSame(snapshot, mapping.Store.AliasSnapshot);
|
||||
Assert.False(mapping.Store.TryResolveAlias(mapping.Address + Segment, 16, out _));
|
||||
}
|
||||
|
||||
private sealed class TransferMappings : IDisposable
|
||||
{
|
||||
public IHostViewMemory Host { get; } = HostViewMemory.Create();
|
||||
|
|
|
|||
Loading…
Reference in New Issue