315 lines
11 KiB
Plaintext
315 lines
11 KiB
Plaintext
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: TulipRxRingBuffer.cs
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Because the Tulip requires the receive packet length be a
|
||
|
// multiple of 4-bytes and there is no way to communicate this
|
||
|
// back to the network stack, packets are received into a temporary
|
||
|
// memory region and then copied out.
|
||
|
|
||
|
using Microsoft.Contracts;
|
||
|
using Microsoft.SingSharp;
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Io.Net;
|
||
|
using Microsoft.Singularity.V1.Services;
|
||
|
using System;
|
||
|
|
||
|
namespace Microsoft.Singularity.Drivers.Network.Tulip
|
||
|
{
|
||
|
internal class TulipRxRingBuffer
|
||
|
{
|
||
|
internal const int MaxFragmentBytes = 2047;
|
||
|
internal const int DescriptorBytes = 16;
|
||
|
internal const int DescriptorAlignment = 4;
|
||
|
internal const int PadBytes = 8; // FCS + round up to 4 byte boundary + 4 byte aligned
|
||
|
|
||
|
private ExRef<PacketFifo>! rxPackets;
|
||
|
|
||
|
private IoMemory! descRegion;
|
||
|
private IoMemory! padRegion;
|
||
|
|
||
|
private readonly int capacity;
|
||
|
int head;
|
||
|
int count;
|
||
|
|
||
|
internal TulipRxRingBuffer(int maxPackets)
|
||
|
requires maxPackets > 0 && IsPowerOf2(maxPackets);
|
||
|
{
|
||
|
this.descRegion =
|
||
|
(!)IoMemory.AllocatePhysical(
|
||
|
new UIntPtr(maxPackets * DescriptorBytes),
|
||
|
DescriptorAlignment
|
||
|
);
|
||
|
|
||
|
this.padRegion =
|
||
|
(!)IoMemory.AllocatePhysical(
|
||
|
new UIntPtr(maxPackets * PadBytes), 0
|
||
|
);
|
||
|
|
||
|
this.rxPackets = new ExRef<PacketFifo>(
|
||
|
new [ExHeap] PacketFifo(maxPackets), true
|
||
|
);
|
||
|
|
||
|
this.capacity = maxPackets;
|
||
|
this.head = 0;
|
||
|
this.count = 0;
|
||
|
}
|
||
|
|
||
|
internal void Reset()
|
||
|
requires this.Count == 0;
|
||
|
{
|
||
|
this.head = 0;
|
||
|
for (int i = 0; i < capacity; i++) {
|
||
|
int descBase = i * DescriptorBytes;
|
||
|
this.descRegion.Write32(descBase, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void PushPacket(PacketFifo*! in ExHeap rxFifo,
|
||
|
[Claims] Packet*! in ExHeap packet)
|
||
|
requires packet->FragmentCount == 1;
|
||
|
requires packet->GetLength() <= MaxFragmentBytes;
|
||
|
ensures this.Count == old(this.Count) + 1;
|
||
|
ensures this.Head == (old(this.Head) + 1) % this.Capacity;
|
||
|
|
||
|
{
|
||
|
UIntPtr fragmentVA;
|
||
|
int length;
|
||
|
|
||
|
packet->GetFragmentRange(0, out fragmentVA, out length);
|
||
|
|
||
|
// Round down buffer length to multiple of 4 bytes
|
||
|
uint rdes1 = ((uint)length & ~0x03u);
|
||
|
uint rdes2 = (uint)GetPhysicalAddress(fragmentVA);
|
||
|
uint rdes3 = ((uint)this.padRegion.PhysicalAddress.Value +
|
||
|
(uint)(this.head * PadBytes));
|
||
|
|
||
|
rdes1 |= (PadBytes << RDES1.RBS2_ROLL);
|
||
|
if (this.head == this.Capacity - 1) {
|
||
|
rdes1 |= RDES1.RER;
|
||
|
}
|
||
|
|
||
|
int offset = this.head * DescriptorBytes;
|
||
|
this.descRegion.Write32(offset + 12, rdes3);
|
||
|
this.descRegion.Write32(offset + 8, rdes2);
|
||
|
this.descRegion.Write32(offset + 4, rdes1);
|
||
|
this.descRegion.Write32(offset + 0, RDES0.OWN);
|
||
|
|
||
|
this.head = (this.head + 1) & (this.Capacity - 1);
|
||
|
this.count++;
|
||
|
|
||
|
rxFifo->Push(packet);
|
||
|
}
|
||
|
|
||
|
internal void Push(PacketFifo*! in ExHeap incoming)
|
||
|
requires this.Count + incoming->Count <= this.Capacity;
|
||
|
ensures this.Count == old(this.Count) + old(incoming->Count);
|
||
|
ensures (this.Head ==
|
||
|
(old(this.Head) + old(incoming->Count)) % this.Capacity);
|
||
|
{
|
||
|
DumpOwnership("+RX Push()");
|
||
|
PacketFifo*! in ExHeap rxFifo = this.rxPackets.Acquire();
|
||
|
try {
|
||
|
while (incoming->Count > 0) {
|
||
|
Packet*! in ExHeap packet = incoming->Pop();
|
||
|
packet->UnsetFragmentLengths();
|
||
|
PushPacket(rxFifo, packet);
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
this.rxPackets.Release(rxFifo);
|
||
|
}
|
||
|
DumpOwnership("-RX Push()");
|
||
|
}
|
||
|
|
||
|
private Packet* in ExHeap PopPacket(PacketFifo*! in ExHeap rxFifo)
|
||
|
ensures (this.Count == old(this.Count) - 1 ||
|
||
|
this.Count == old(this.Count));
|
||
|
{
|
||
|
int descBase = this.Tail * DescriptorBytes;
|
||
|
uint rdes0 = this.descRegion.Read32(descBase);
|
||
|
if ((rdes0 & RDES0.OWN) == RDES0.OWN) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
Packet*! in ExHeap packet = rxFifo->Pop();
|
||
|
|
||
|
// Length is less FCS that is passed up to anyone
|
||
|
uint length = ((rdes0 >> RDES0.FL_ROLL) & RDES0.FL_MASK) - 4;
|
||
|
int packetCapacity = packet->GetLength();
|
||
|
if ((packetCapacity % 3) != 0) {
|
||
|
// We may have data in second buffer because
|
||
|
// packet size was not a multiple of 4.
|
||
|
if (length >= (packetCapacity & ~0x3)) {
|
||
|
packet->SetFragmentLength(0, packetCapacity & ~0x3);
|
||
|
int todo = ((int)length - (packetCapacity & ~0x3)) & 0x3;
|
||
|
for (int i = 0; i < todo; i++) {
|
||
|
int srcOffset = this.Tail * PadBytes + i;
|
||
|
packet->AppendToFragment(0, this.padRegion[srcOffset]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool cropped = false;
|
||
|
uint maxLength = (uint)packet->GetLength();
|
||
|
if (length > maxLength) {
|
||
|
TulipDebug.Print("RX Packet cropped ({0} > {1})\n",
|
||
|
__arglist(length, maxLength));
|
||
|
cropped = true;
|
||
|
length = maxLength;
|
||
|
}
|
||
|
|
||
|
if ((rdes0 & RDES0.ES) != 0) {
|
||
|
TulipDebug.Print("RX Error RDES0 = {0:x8}\n",
|
||
|
__arglist(rdes0));
|
||
|
packet->FromDeviceFlags = FromDeviceFlags.ReceiveError;
|
||
|
}
|
||
|
else if (cropped) {
|
||
|
packet->FromDeviceFlags = FromDeviceFlags.ReceiveError;
|
||
|
}
|
||
|
else {
|
||
|
packet->FromDeviceFlags = FromDeviceFlags.ReceiveSuccess;
|
||
|
}
|
||
|
|
||
|
this.count--;
|
||
|
return packet;
|
||
|
}
|
||
|
|
||
|
internal void Pop(PacketFifo*! in ExHeap outgoing)
|
||
|
ensures this.Count == old(this.Count) - outgoing->Count;
|
||
|
{
|
||
|
DumpOwnership("+RX Pop");
|
||
|
PacketFifo*! in ExHeap rxFifo = this.rxPackets.Acquire();
|
||
|
try {
|
||
|
while (rxFifo->Count > 0) {
|
||
|
Packet* in ExHeap popped = PopPacket(rxFifo);
|
||
|
if (popped == null)
|
||
|
break;
|
||
|
outgoing->Push(popped);
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
this.rxPackets.Release(rxFifo);
|
||
|
}
|
||
|
DumpOwnership("-RX Pop");
|
||
|
}
|
||
|
|
||
|
private static UIntPtr GetPhysicalAddress(UIntPtr va)
|
||
|
{
|
||
|
UIntPtr pa; // Physical address
|
||
|
UIntPtr paLeft; // Bytes remaining on physical page
|
||
|
if (!DeviceService.GetDmaPhysicalAddress(va, out pa, out paLeft) ||
|
||
|
pa == UIntPtr.Zero ||
|
||
|
paLeft < Tulip.MtuBytes) {
|
||
|
throw new ApplicationException("Bad DMA pointer");
|
||
|
}
|
||
|
return pa;
|
||
|
}
|
||
|
|
||
|
internal UIntPtr BaseAddress
|
||
|
{
|
||
|
get { return this.descRegion.PhysicalAddress.Value; }
|
||
|
}
|
||
|
|
||
|
[Pure]
|
||
|
internal int Capacity { get { return this.capacity; } }
|
||
|
|
||
|
[Pure]
|
||
|
internal int Count { get { return this.count; } }
|
||
|
|
||
|
[Pure]
|
||
|
internal int Free { get { return this.capacity - this.count; } }
|
||
|
|
||
|
[Pure]
|
||
|
internal int Head { get { return this.head; } }
|
||
|
|
||
|
[Pure]
|
||
|
internal int Tail
|
||
|
{
|
||
|
get {
|
||
|
return ((this.capacity + this.head - this.count) %
|
||
|
this.capacity);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Pure]
|
||
|
private static bool IsPowerOf2(int n)
|
||
|
{
|
||
|
return (n > 0) && ((n & (n - 1)) == 0);
|
||
|
}
|
||
|
|
||
|
[Pure]
|
||
|
private static bool IsMultipleOf4(int n)
|
||
|
{
|
||
|
return (n % 4) == 0;
|
||
|
}
|
||
|
|
||
|
[ System.Diagnostics.Conditional("DEBUG_TULIP") ]
|
||
|
private static void DumpOwnership(int start, int end, bool owned)
|
||
|
{
|
||
|
TulipDebug.Print(
|
||
|
"{0:4}-{1:4} {2}\n",
|
||
|
__arglist(start, end, owned ? "OWNED" : "UNOWNED")
|
||
|
);
|
||
|
}
|
||
|
|
||
|
[ System.Diagnostics.Conditional("DEBUG_TULIP") ]
|
||
|
internal void DumpOwnership(string title)
|
||
|
{
|
||
|
TulipDebug.Print("{0}\n", __arglist(title));
|
||
|
PacketFifo*! in ExHeap fifo = this.rxPackets.Acquire();
|
||
|
try {
|
||
|
assert fifo->Count == this.Count;
|
||
|
TulipDebug.Print("Head = {0} Tail = {1} PacketCount = {2}\n",
|
||
|
__arglist(this.Head, this.Tail,
|
||
|
fifo->Count));
|
||
|
|
||
|
bool errorsFound = false;
|
||
|
|
||
|
uint lastRdes0 = this.descRegion.Read32(0) & RDES0.OWN;
|
||
|
int lastStart = 0;
|
||
|
for (int i = 1; i < this.Capacity; i++) {
|
||
|
uint rdes0 = this.descRegion.Read32(i * DescriptorBytes);
|
||
|
errorsFound = ((rdes0 & RDES0.ES) != 0);
|
||
|
|
||
|
if ((rdes0 & RDES0.OWN) != lastRdes0) {
|
||
|
DumpOwnership(lastStart, i - 1,
|
||
|
lastRdes0 == RDES0.OWN);
|
||
|
lastStart = i;
|
||
|
lastRdes0 = rdes0 & RDES0.OWN;
|
||
|
}
|
||
|
}
|
||
|
DumpOwnership(lastStart, this.Capacity - 1,
|
||
|
(lastRdes0 & RDES0.OWN) == RDES0.OWN);
|
||
|
|
||
|
if (errorsFound) {
|
||
|
for (int i = 0; i < this.Capacity; i++) {
|
||
|
uint rdes0, rdes1, rdes2, rdes3;
|
||
|
rdes0 = this.descRegion.Read32(i * DescriptorBytes);
|
||
|
rdes1 = this.descRegion.Read32(i * DescriptorBytes + 4);
|
||
|
rdes2 = this.descRegion.Read32(i * DescriptorBytes + 8);
|
||
|
rdes3 = this.descRegion.Read32(i * DescriptorBytes + 12);
|
||
|
TulipDebug.Print(
|
||
|
"{0:x4} {1}{2} FL = {3:4} RDES [0]={4:x8} [1]={5:x8} [2]={6:x8} [3]={7:x8}\n",
|
||
|
__arglist(i,
|
||
|
(rdes0 & RDES0.OWN) != 0 ? "O" : "U",
|
||
|
(rdes0 & RDES0.ES) != 0 ? "E" : "0",
|
||
|
(rdes0 >> RDES0.FL_ROLL) & RDES0.FL_MASK,
|
||
|
rdes0, rdes1, rdes2, rdes3));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
this.rxPackets.Release(fifo);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|