643 lines
22 KiB
Plaintext
643 lines
22 KiB
Plaintext
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Microsoft Research Singularity
|
||
//
|
||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
//
|
||
// File: NicManager.sg
|
||
//
|
||
// Note:
|
||
//
|
||
// When a network device comes up, it registers with the
|
||
// NicManager, who places it in the namespace under
|
||
// /dev/nicX and advertises its existence with the netstack
|
||
// runtime core. The netstack runtime core will be responsible
|
||
// for notifying the NicManager when the device has gone away.
|
||
//
|
||
// This is a lot of jiggery-pokery just so users can see the device names
|
||
// under /dev and the names are sequential.
|
||
//
|
||
|
||
// #define DEBUG_NIC
|
||
|
||
using NetStack.Common;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Diagnostics;
|
||
using System.Threading;
|
||
using Microsoft.SingSharp;
|
||
using Microsoft.Singularity;
|
||
using Microsoft.Singularity.Channels;
|
||
using Microsoft.Singularity.Io;
|
||
using Microsoft.Singularity.Io.Net;
|
||
using Microsoft.Singularity.Directory;
|
||
|
||
using NetStack.Contracts;
|
||
using NetStack.Runtime;
|
||
|
||
using Drivers.Net;
|
||
|
||
namespace NetStack.Channels.Nic
|
||
{
|
||
internal class Nic : IAdapter
|
||
{
|
||
// Upper bounds computed for 10Gbps with maximum sized
|
||
// packets to give a small number of I/O requests per
|
||
// second and be a power of 2.
|
||
private const int MaxTxPacketsInDevice = 128 * 1024;
|
||
private const int MaxRxPacketsInDevice = 128 * 1024;
|
||
|
||
private TRef<NicDeviceContract.Imp:READY>! nicChannel;
|
||
private TRef<NicDeviceEventContract.Imp:READY> eventChannel;
|
||
|
||
string! driverName;
|
||
string! driverVersion;
|
||
byte []! macAddress;
|
||
int mtu;
|
||
int maxTxPacketsInDevice;
|
||
int maxRxPacketsInDevice;
|
||
int maxTxFragmentsPerPacket;
|
||
int maxRxFragmentsPerPacket;
|
||
|
||
private AutoResetEvent !readHandle;
|
||
private AutoResetEvent !writeHandle;
|
||
|
||
private Queue! rxFreePacketQueue;
|
||
|
||
private ExRef<PacketFifo>! rxToDeviceFifo;
|
||
private ExRef<PacketFifo>! rxFromDeviceFifo;
|
||
|
||
private ExRef<PacketFifo>! txToDeviceFifo;
|
||
private ExRef<PacketFifo>! txFromDeviceFifo;
|
||
private ExRef<PacketFifo>! txFreeFifo;
|
||
|
||
private ulong txRequests = 0;
|
||
private ulong txComplete = 0;
|
||
private ulong rxTotal = 0;
|
||
|
||
[Microsoft.Contracts.NotDelayed]
|
||
public Nic([Claims] NicDeviceContract.Imp:READY! nicImp,
|
||
NicDeviceProperties*! in ExHeap np)
|
||
{
|
||
this.readHandle = new AutoResetEvent(false);
|
||
this.writeHandle = new AutoResetEvent(false);
|
||
|
||
this.nicChannel = new TRef<NicDeviceContract.Imp:READY>(nicImp);
|
||
expose (np) {
|
||
assume np->DriverName != null;
|
||
assume np->MacAddress != null;
|
||
this.driverName = Bitter.ToString2(np->DriverName);
|
||
this.driverVersion = Bitter.ToString2(np->DriverName);
|
||
this.macAddress = Bitter.ToByteArray(np->MacAddress);
|
||
}
|
||
this.mtu = np->MtuBytes;
|
||
this.maxTxPacketsInDevice = np->MaxTxPacketsInDevice;
|
||
this.maxRxPacketsInDevice = np->MaxRxPacketsInDevice;
|
||
this.maxTxFragmentsPerPacket = np->MaxTxFragmentsPerPacket;
|
||
this.maxRxFragmentsPerPacket = np->MaxRxFragmentsPerPacket;
|
||
// The following attributes are both integers yet
|
||
// sgc is complaining it doesn't know to use
|
||
// Math.Min(sbyte, sbyte) or Math.Min(byte, byte).
|
||
int rxFifoSize = Math.Min(np->MaxRxPacketsInDevice,
|
||
(int)Nic.MaxRxPacketsInDevice);
|
||
|
||
// Create a queue for NetPackets that we pass up to
|
||
// the NetStack and it passes down to us. And
|
||
// create a Packet fifo that has a packet for each
|
||
// NetPacket.
|
||
|
||
this.rxFreePacketQueue = new Queue(rxFifoSize);
|
||
this.rxToDeviceFifo =
|
||
new ExRef<PacketFifo>(
|
||
new [ExHeap] PacketFifo(rxFifoSize),
|
||
false
|
||
);
|
||
|
||
this.rxFromDeviceFifo =
|
||
new ExRef<PacketFifo>(
|
||
new [ExHeap] PacketFifo(rxFifoSize),
|
||
false
|
||
);
|
||
|
||
// The following attributes are both integers yet
|
||
// sgc is complaining it doesn't know to use
|
||
// Math.Min(sbyte, sbyte) or Math.Min(byte, byte).
|
||
int txFifoSize = Math.Min(np->MaxTxPacketsInDevice,
|
||
(int)Nic.MaxTxPacketsInDevice);
|
||
|
||
this.txToDeviceFifo =
|
||
new ExRef<PacketFifo>(
|
||
new [ExHeap] PacketFifo(txFifoSize),
|
||
false
|
||
);
|
||
|
||
this.txFromDeviceFifo =
|
||
new ExRef<PacketFifo>(
|
||
new [ExHeap] PacketFifo(txFifoSize),
|
||
false
|
||
);
|
||
|
||
this.txFreeFifo =
|
||
new ExRef<PacketFifo>(
|
||
new [ExHeap] PacketFifo(txFifoSize),
|
||
false
|
||
);
|
||
|
||
base();
|
||
|
||
TxProvision();
|
||
RxProvision();
|
||
}
|
||
|
||
int MaxRxPackets { get { return this.maxRxPacketsInDevice; } }
|
||
|
||
internal void ThreadMain()
|
||
{
|
||
StartIO();
|
||
EventMessagePump();
|
||
}
|
||
|
||
internal void EventMessagePump()
|
||
{
|
||
Tracing.Log(Tracing.Debug, "Started event pump");
|
||
|
||
NicDeviceEventContract.Imp imp = (eventChannel).Acquire();
|
||
eventChannel = null;
|
||
try {
|
||
assert imp.InState(NicDeviceEventContract.READY.Value);
|
||
imp.RecvSuccess();
|
||
assert imp.InState(NicDeviceEventContract.RUNNING.Value);
|
||
for (;;) {
|
||
NicEventType eventType;
|
||
imp.RecvNicDeviceEvent(out eventType);
|
||
if ((eventType & NicEventType.ReceiveEvent) != 0) {
|
||
Tracing.Log(Tracing.Debug, "Receive event");
|
||
readHandle.Set();
|
||
}
|
||
if ((eventType & NicEventType.TransmitEvent) != 0) {
|
||
Tracing.Log(Tracing.Debug, "Transmit event");
|
||
writeHandle.Set();
|
||
}
|
||
if ((eventType & NicEventType.LinkEvent) != 0) {
|
||
Tracing.Log(Tracing.Debug, "Link event");
|
||
DebugStub.Break();
|
||
}
|
||
imp.SendAckNicDeviceEvent();
|
||
}
|
||
}
|
||
catch (ChannelClosedException) {
|
||
Tracing.Log(Tracing.Debug, "NIC event channel closed.");
|
||
}
|
||
finally {
|
||
delete imp;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// IAdapter interface
|
||
//
|
||
string IAdapter.DriverName
|
||
{
|
||
get { return this.driverName; }
|
||
}
|
||
|
||
string IAdapter.DriverVersion
|
||
{
|
||
get { return this.driverVersion; }
|
||
}
|
||
|
||
uint IAdapter.LinkSpeed { get { return 100000000; } }
|
||
|
||
EthernetAddress IAdapter.HardwareAddress
|
||
{
|
||
get { return new EthernetAddress(this.macAddress); }
|
||
}
|
||
|
||
WaitHandle IAdapter.GetReadHandle() { return readHandle; }
|
||
WaitHandle IAdapter.GetWriteHandle() { return writeHandle; }
|
||
|
||
[Conditional("DEBUG_NIC")]
|
||
internal static void DebugPrint(string format, __arglist)
|
||
{
|
||
DebugStub.Print(format, new ArgIterator(__arglist));
|
||
}
|
||
|
||
private void RxExchangeInternal(NicDeviceContract.Imp! imp)
|
||
{
|
||
int toCount, fromCount;
|
||
PacketFifo*! in ExHeap exFifo = this.rxToDeviceFifo.Acquire();
|
||
toCount = exFifo->Count;
|
||
try {
|
||
imp.SendGiveRxPacketsToDevice(exFifo);
|
||
imp.RecvTakeRxPacketsFromDevice(out exFifo);
|
||
fromCount = exFifo->Count;
|
||
|
||
// Transfer packets received from the device
|
||
// to the fromDevice fifo.
|
||
PacketFifo*! in ExHeap fromDevice =
|
||
this.rxFromDeviceFifo.Acquire();
|
||
|
||
try {
|
||
while (exFifo->Count > 0) {
|
||
fromDevice->Push(exFifo->Pop());
|
||
}
|
||
}
|
||
finally {
|
||
this.rxFromDeviceFifo.Release(fromDevice);
|
||
}
|
||
}
|
||
finally {
|
||
this.rxToDeviceFifo.Release(exFifo);
|
||
}
|
||
DebugPrint("RxExchange out: {0} in: {1}\n",
|
||
__arglist(toCount, fromCount));
|
||
}
|
||
|
||
private void RxExchange()
|
||
{
|
||
NicDeviceContract.Imp imp = nicChannel.Acquire();
|
||
try {
|
||
RxExchangeInternal(imp);
|
||
}
|
||
finally {
|
||
this.nicChannel.Release(imp);
|
||
}
|
||
}
|
||
|
||
// Get the received packets from the adapter
|
||
void IAdapter.GetReceivedPackets(Queue! outQueue)
|
||
{
|
||
lock (this) {
|
||
// REVIEW: Should be able to make a decision about
|
||
// whether to RxExchange or not here.
|
||
RxExchange();
|
||
|
||
PacketFifo*! in ExHeap toDevice = this.rxToDeviceFifo.Acquire();
|
||
try {
|
||
PacketFifo*! in ExHeap fromDevice = this.rxFromDeviceFifo.Acquire();
|
||
try {
|
||
while (rxFreePacketQueue.Count > 0 &&
|
||
fromDevice->Count > 0) {
|
||
// Take netstack packet from free queue
|
||
NetPacket! netPacket =
|
||
(NetPacket!)rxFreePacketQueue.Dequeue();
|
||
|
||
// Take packet from the device
|
||
Packet*! in ExHeap packet = fromDevice->Pop();
|
||
|
||
// If packet from device has an error
|
||
// recycle it right away, and try next
|
||
// packet. Careful not to lose the
|
||
// packet from the rxFreePacketQueue!
|
||
FromDeviceFlags fromFlags = packet->FromDeviceFlags;
|
||
if ((fromFlags & FromDeviceFlags.ReceiveError) != 0) {
|
||
packet->UnsetFragmentLengths();
|
||
toDevice->Push(packet);
|
||
rxFreePacketQueue.Enqueue(netPacket);
|
||
continue;
|
||
}
|
||
|
||
// Copy data from device's packet into netstack's packet
|
||
netPacket.Reset(packet->GetLength());
|
||
packet->ToByteArray((!)netPacket.GetRawData());
|
||
netPacket.AdapterContext = this;
|
||
|
||
// Enqueue netstack's packet for upward processing
|
||
outQueue.Enqueue(netPacket);
|
||
|
||
// Reset packet from device and put in the queue
|
||
// of return candidates.
|
||
packet->UnsetFragmentLengths();
|
||
toDevice->Push(packet);
|
||
}
|
||
}
|
||
finally {
|
||
this.rxFromDeviceFifo.Release(fromDevice);
|
||
}
|
||
}
|
||
finally {
|
||
this.rxToDeviceFifo.Release(toDevice);
|
||
}
|
||
}
|
||
}
|
||
|
||
// populate the adapter's receive ring with new NetPackets
|
||
void IAdapter.PopulateRxRing(NetPacket! freePacket)
|
||
{
|
||
lock (this) {
|
||
this.rxFreePacketQueue.Enqueue(freePacket);
|
||
|
||
bool sendPacketsToDevice = false;
|
||
PacketFifo*! in ExHeap toDevice = this.rxToDeviceFifo.Acquire();
|
||
try {
|
||
sendPacketsToDevice = toDevice->Count > MaxRxPacketsInDevice / 2;
|
||
}
|
||
finally {
|
||
this.rxToDeviceFifo.Release(toDevice);
|
||
}
|
||
|
||
if (sendPacketsToDevice) {
|
||
RxExchange();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void TxExchange()
|
||
{
|
||
int toCount = 0;
|
||
int fromCount = 0;
|
||
NicDeviceContract.Imp imp = nicChannel.Acquire();
|
||
try {
|
||
PacketFifo*! in ExHeap src = this.txToDeviceFifo.Acquire();
|
||
toCount = src->Count;
|
||
try {
|
||
imp->SendGiveTxPacketsToDevice(src);
|
||
imp->RecvTakeTxPacketsFromDevice(out src);
|
||
fromCount = src->Count;
|
||
PacketFifo*! in ExHeap dst =
|
||
this.txFromDeviceFifo.Acquire();
|
||
try {
|
||
dst->Push(src);
|
||
}
|
||
finally {
|
||
this.txFromDeviceFifo.Release(dst);
|
||
}
|
||
}
|
||
finally {
|
||
this.txToDeviceFifo.Release(src);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
nicChannel.Release(imp);
|
||
}
|
||
DebugPrint("TxExchange out: {0} in: {1}\n",
|
||
__arglist(toCount, fromCount));
|
||
}
|
||
|
||
uint IAdapter.GetTransmittedPackets()
|
||
{
|
||
lock (this) {
|
||
TxExchange();
|
||
|
||
PacketFifo*! in ExHeap src = this.txFromDeviceFifo.Acquire();
|
||
int srcCount = src->Count;
|
||
try {
|
||
PacketFifo*! in ExHeap dst = this.txFreeFifo.Acquire();
|
||
try {
|
||
dst->Push(src);
|
||
}
|
||
finally {
|
||
this.txFreeFifo.Release(dst);
|
||
}
|
||
}
|
||
finally {
|
||
this.txFromDeviceFifo.Release(src);
|
||
}
|
||
|
||
return (uint) srcCount;
|
||
}
|
||
}
|
||
|
||
void IAdapter.PopulateTxRing(NetPacket[]! fromUser, uint count)
|
||
{
|
||
lock (this) {
|
||
PacketFifo*! in ExHeap txFree = this.txFreeFifo.Acquire();
|
||
try {
|
||
PacketFifo*! in ExHeap txToDevice = this.txToDeviceFifo.Acquire();
|
||
try {
|
||
for (int i = 0; i < count; i++) {
|
||
Packet*! in ExHeap packet = txFree->Pop();
|
||
byte []! data = (!)((!)fromUser[i]).GetRawData();
|
||
packet->SetFragment(0, data, 0, data.Length);
|
||
txToDevice->Push(packet);
|
||
}
|
||
}
|
||
finally {
|
||
this.txToDeviceFifo.Release(txToDevice);
|
||
}
|
||
}
|
||
finally {
|
||
this.txFreeFifo.Release(txFree);
|
||
}
|
||
|
||
TxExchange();
|
||
|
||
// TODO: Recycle fromUser packets...
|
||
// Making work for the GC da...da...
|
||
}
|
||
}
|
||
|
||
int IAdapter.TxSlotsFree
|
||
{
|
||
get {
|
||
lock (this) {
|
||
PacketFifo*! in ExHeap txFree = this.txFreeFifo.Acquire();
|
||
try {
|
||
return txFree->Count;
|
||
}
|
||
finally {
|
||
this.txFreeFifo.Release(txFree);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool
|
||
ConfigureEventChannel(NicDeviceContract.Imp:IO_CONFIGURE_BEGIN! nicImp)
|
||
{
|
||
NicDeviceEventContract.Imp! imp;
|
||
NicDeviceEventContract.Exp! exp;
|
||
NicDeviceEventContract.NewChannel(out imp, out exp);
|
||
|
||
nicImp.SendRegisterForEvents(exp);
|
||
switch receive {
|
||
case nicImp.Success():
|
||
eventChannel =
|
||
new TRef<NicDeviceEventContract.Imp:READY>(imp);
|
||
return true;
|
||
|
||
case nicImp.ChannelClosed():
|
||
Tracing.Log(Tracing.Error, "NIC channel closed");
|
||
delete imp;
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private bool
|
||
ConfigureChecksumProperties(NicDeviceContract.Imp! nicImp)
|
||
{
|
||
nicImp.SendSetChecksumProperties(0);
|
||
switch receive {
|
||
case nicImp.Success():
|
||
return true;
|
||
|
||
case nicImp.UnsupportedChecksumProperties():
|
||
Tracing.Log(Tracing.Error, "NIC refused checksum configuration it advertised");
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private bool Configure()
|
||
{
|
||
NicDeviceContract.Imp! nicImp = nicChannel.Acquire();
|
||
try {
|
||
nicImp.SendConfigureIO();
|
||
nicImp.RecvAckConfigureIO();
|
||
if (ConfigureEventChannel(nicImp) == true &&
|
||
ConfigureChecksumProperties(nicImp) == true) {
|
||
return true;
|
||
}
|
||
}
|
||
catch (ChannelClosedException) {
|
||
Tracing.Log(Tracing.Error, "NIC channel closed.");
|
||
}
|
||
catch (SystemException e) {
|
||
Console.WriteLine(e);
|
||
DebugStub.Break();
|
||
}
|
||
finally {
|
||
nicChannel.Release(nicImp);
|
||
}
|
||
DebugStub.Break();
|
||
return false;
|
||
}
|
||
|
||
private void RxProvision()
|
||
{
|
||
PacketFifo*! in ExHeap toDevice = this.rxToDeviceFifo.Acquire();
|
||
for (int i = 0; i < toDevice->Capacity; i++) {
|
||
this.rxFreePacketQueue.Enqueue( new NetPacket(this.mtu) );
|
||
toDevice->Push(
|
||
new [ExHeap] Packet(
|
||
new [ExHeap] byte [this.mtu]
|
||
)
|
||
);
|
||
}
|
||
this.rxToDeviceFifo.Release(toDevice);
|
||
}
|
||
|
||
private void TxProvision()
|
||
{
|
||
PacketFifo*! in ExHeap txFree = this.txFreeFifo.Acquire();
|
||
for (int i = 0; i < txFree->Capacity; i++) {
|
||
txFree->Push(
|
||
new [ExHeap] Packet(
|
||
new [ExHeap] byte [this.mtu]
|
||
)
|
||
);
|
||
}
|
||
this.txFreeFifo.Release(txFree);
|
||
}
|
||
|
||
private bool StartIO()
|
||
{
|
||
NicDeviceContract.Imp imp = nicChannel.Acquire();
|
||
try {
|
||
if (imp.InState(NicDeviceContract.IO_CONFIGURED.Value) == true) {
|
||
imp.SendStartIO();
|
||
imp.RecvAckStartIO();
|
||
RxExchangeInternal(imp);
|
||
return true;
|
||
}
|
||
}
|
||
catch (ChannelClosedException) {
|
||
}
|
||
finally {
|
||
nicChannel.Release(imp);
|
||
}
|
||
DebugStub.Break();
|
||
return false;
|
||
}
|
||
|
||
private bool StopIO()
|
||
{
|
||
NicDeviceContract.Imp imp = nicChannel.Acquire();
|
||
try
|
||
{
|
||
if (imp.InState(NicDeviceContract.IO_RUNNING.Value) == true)
|
||
{
|
||
imp.SendStopIO();
|
||
imp.RecvAckStopIO();
|
||
return true;
|
||
}
|
||
}
|
||
catch (ChannelClosedException)
|
||
{
|
||
}
|
||
finally
|
||
{
|
||
nicChannel.Release(imp);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//
|
||
// Factory methods
|
||
//
|
||
internal static NicDeviceProperties* in ExHeap
|
||
GetNicProperties(NicDeviceContract.Imp:READY! imp)
|
||
{
|
||
try {
|
||
NicDeviceProperties*! in ExHeap np =
|
||
new [ExHeap] NicDeviceProperties();
|
||
imp.SendGetDeviceProperties(np);
|
||
imp.RecvDeviceProperties(out np);
|
||
return np;
|
||
}
|
||
catch (OutOfMemoryException) {
|
||
Tracing.Log(Tracing.Debug,
|
||
"Out-of-memory getting NIC properties");
|
||
return null;
|
||
}
|
||
catch (ChannelClosedException) {
|
||
Tracing.Log(Tracing.Debug,
|
||
"Channel closed while getting NIC properties");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
internal static bool
|
||
CreateAndRegister([Claims] NicDeviceContract.Imp:Start! imp,
|
||
string! nicName)
|
||
{
|
||
Nic nic = null;
|
||
try {
|
||
imp.RecvSuccess();
|
||
Tracing.Log(Tracing.Debug, "Nic channel transition");
|
||
Tracing.Log(Tracing.Debug, imp.CurrentState());
|
||
|
||
NicDeviceProperties* in ExHeap np = GetNicProperties(imp);
|
||
if (np == null) {
|
||
delete imp;
|
||
return false;
|
||
}
|
||
|
||
nic = new Nic(imp, np);
|
||
delete np;
|
||
if (nic.Configure() == false) {
|
||
return false;
|
||
}
|
||
|
||
NetStack.Runtime.Core! ns = (!)NetStack.Runtime.Core.Instance();
|
||
ns.RegisterAdapter(nic, nicName, nic.MaxRxPackets);
|
||
|
||
Thread thread = new Thread(
|
||
new ThreadStart(nic.ThreadMain)
|
||
);
|
||
thread.Start();
|
||
|
||
return true;
|
||
}
|
||
catch {
|
||
delete imp;
|
||
DebugStub.Break();
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
}
|