/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Disk.sg // using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Io; using System; using System.Threading; namespace Microsoft.Singularity.Services.Fat.Fs { /// /// Class responsible for performing low-level disk I/O. /// internal class DiskEndpoint { internal TRef epTRef; internal DiskEndpoint([Claims] DiskDeviceContract.Imp:Ready! diskImp) { epTRef = new TRef(diskImp); } internal byte[] in ExHeap Read(ulong sectorId, [Claims] byte[]! in ExHeap data) { DiskDeviceContract.Imp:Ready! imp = epTRef.Acquire(); ulong dataLength = (ulong)data.Length; imp.SendRead(data, 0, dataLength, sectorId); switch receive { case imp.RecvAckRead(outBuffer): epTRef.Release(imp); return outBuffer; break; case imp.RecvNakRead(): epTRef.Release(imp); return null; break; case imp.ChannelClosed(): delete imp; throw new ChannelClosedException(); break; } } internal byte[] in ExHeap Write(ulong sectorId, [Claims] byte[]! in ExHeap data) { return Write(sectorId, data, 0, (uint)data.Length); } internal byte[] in ExHeap Write(ulong sectorId, [Claims] byte[]! in ExHeap data, uint dataStart, uint dataBytesToWrite) { DiskDeviceContract.Imp:Ready! imp = epTRef.Acquire(); imp.SendWrite(data, dataStart, (ulong)dataBytesToWrite, sectorId); switch receive { case imp.RecvAckWrite(outBuffer): epTRef.Release(imp); return outBuffer; break; case imp.RecvNakWrite(): epTRef.Release(imp); Tracing.Log(Tracing.Warning, "FatFs received disk NakWrite for sector {0}.", sectorId); return null; break; case imp.ChannelClosed(): delete imp; throw new ChannelClosedException(); break; } } } // end class Endpoint /// /// Class responsible for maintaining pool of disk endpoints. /// internal class DiskEndpointPool { /////////////////////////////////////////////////////////////////////// // Fields DiskEndpoint[] endpoints; int available; /////////////////////////////////////////////////////////////////////// // Invariants // invariant available >= 0 && available <= endpoints.Length; /////////////////////////////////////////////////////////////////////// // Methods internal DiskEndpointPool(int capacity) { endpoints = new DiskEndpoint[capacity]; available = 0; } internal int Capacity { get { return endpoints.Length; } } /// /// Takes DiskEndpoint from the Pool for use. /// This method takes an endpoint from the pool. /// If no endpoints are available, this method blocks. /// internal DiskEndpoint! Get() { Monitor.Enter(this); try { for (;;) { if (available > 0) { // Programmer error missed // the return statement here. The compiler generated // no warning and obviously locked the application. return InternalGet(); } Monitor.Wait(this); } } finally { Monitor.Exit(this); } } /// /// Places DiskEndpoint in the pool for potential use by others. /// internal void Add(DiskEndpoint! endpoint) { Monitor.Enter(this); try { InternalAdd(endpoint); Monitor.Pulse(this); } finally { Monitor.Exit(this); } } private DiskEndpoint! InternalGet() // requires monitor held requires available > 0; ensures available == old(available) - 1; { return (!) endpoints[--available]; } private void InternalAdd(DiskEndpoint! endpoint) // requires monitor held requires available < endpoints.Length; ensures available == old(available) + 1; { endpoints[available++] = endpoint; } } internal class Disk { /////////////////////////////////////////////////////////////////////// // Constants internal const int BytesPerSector = 512; internal const ushort NoSystemId = 0xffff; /////////////////////////////////////////////////////////////////////// // Members string! name; DiskEndpointPool! endpoints; ulong startSector; ulong totalSectors; ushort systemId; /////////////////////////////////////////////////////////////////////// // Methods private Disk(string! diskName, DiskEndpointPool! diskEndpoints, ulong diskStartSector, ulong diskSectorCount, ushort diskSystemId) { name = diskName; endpoints = diskEndpoints; startSector = diskStartSector; totalSectors = diskSectorCount; systemId = diskSystemId; base(); } internal string! Name { get { return name;} } internal ulong StartSector { get { return startSector; } } internal ulong TotalSectors { get { return totalSectors; } } internal int MaxConnections { get { return endpoints.Capacity; } } internal DiskAttributes DiskAttributes { get { return 0; } } internal bool GetSystemId(out byte sysId) { if (systemId != NoSystemId) { sysId = (byte)(systemId & 0xff); return true; } sysId = 0; return false; } internal byte[] in ExHeap Read(ulong sectorId, ulong length) requires sectorId < TotalSectors; { byte[]! in ExHeap data = new [ExHeap] byte[length]; return Read(sectorId, data); } internal byte[] in ExHeap Read(ulong sectorId, [Claims] byte[]! in ExHeap data) requires sectorId < TotalSectors; requires (data.Length % BytesPerSector) == 0; { DiskEndpoint ep = endpoints.Get(); try { return ep.Read(sectorId, data); } finally { endpoints.Add(ep); } } internal byte[] in ExHeap Write(ulong sectorId, [Claims] byte[]! in ExHeap data) requires sectorId < TotalSectors; requires (data.Length % BytesPerSector) == 0; { DiskEndpoint ep = endpoints.Get(); try { return ep.Write(sectorId, data); } finally { endpoints.Add(ep); } } internal byte[] in ExHeap Write(ulong sectorId, [Claims] byte[]! in ExHeap data, uint dataStart, uint dataBytesToWrite) requires sectorId < TotalSectors; requires (data.Length % BytesPerSector) == 0; requires dataStart <= data.Length; requires dataStart + dataBytesToWrite <= data.Length; { DiskEndpoint ep = endpoints.Get(); try { return ep.Write(sectorId, data, dataStart, dataBytesToWrite); } finally { endpoints.Add(ep); } } /////////////////////////////////////////////////////////////////////// // Static construction methods static private bool GetDiskStartSector(DiskDeviceContract.Imp:Ready! diskImp, out ulong startSector) { diskImp.SendGetStartSector(); switch receive { case diskImp.RecvAckGetStartSector(sectors): startSector = sectors; return true; break; case diskImp.ChannelClosed(): startSector = 0; return false; break; } } static private bool GetDiskSectorCount(DiskDeviceContract.Imp:Ready! diskImp, out ulong sectorCount) { diskImp.SendGetSectorCount(); switch receive { case diskImp.RecvAckGetSectorCount(sectors): sectorCount = sectors; return true; break; case diskImp.ChannelClosed(): sectorCount = 0; return false; break; } } static private bool GetDiskSystemId(DiskDeviceContract.Imp:Ready! diskImp, out ushort systemId) { diskImp.SendGetSystemId(); switch receive { case diskImp.RecvSystemId(theSystemId): systemId = theSystemId; return true; break; case diskImp.RecvNoSystemId(): systemId = NoSystemId; return true; break; case diskImp.ChannelClosed(): systemId = 0; return false; break; } } static private bool GetDiskAttributes(DiskDeviceContract.Imp:Ready! diskImp, out DiskAttributes da) { diskImp.SendGetDiskAttributes(); switch receive { case diskImp.RecvAckGetDiskAttributes(theAttributes): da = theAttributes; return true; case diskImp.ChannelClosed(): da = 0; return false; } } static private DiskDeviceContract.Imp:Ready ValidatedDiskEndpoint([Claims] DiskDeviceContract.Imp:Start! diskImp) { switch receive { case diskImp.RecvSuccess(): return diskImp; break; case diskImp.RecvContractNotSupported(): delete diskImp; return null; break; case diskImp.ChannelClosed(): // Log "Disk {0} closed channel unexpectedly." delete diskImp; return null; break; } } static private DiskDeviceContract.Imp:Ready OpenDevice(DirectoryServiceContract.Imp:Ready! nsImp, string! deviceName) { DiskDeviceContract.Exp! diskExp; DiskDeviceContract.Imp! diskImp; DiskDeviceContract.NewChannel(out diskImp, out diskExp); nsImp.SendBind(Bitter.FromString2(deviceName), diskExp); switch receive { case nsImp.RecvAckBind(): return ValidatedDiskEndpoint(diskImp); break; case nsImp.RecvNakBind(badExp, errorCode): delete diskImp; delete badExp; return null; break; case nsImp.ChannelClosed(): delete diskImp; return null; break; } } static internal Disk Create(DirectoryServiceContract.Imp:Ready! nsImp, string! deviceName, int channelCount) requires channelCount >= 1; { // // Create first connection to disk // DiskDeviceContract.Imp diskImp = OpenDevice(nsImp, deviceName); if (diskImp == null) { Console.WriteLine("Could not establish a connection to {0}\n", deviceName); return null; } // // Retrieve cacheable disk information // ulong startSector; if (GetDiskStartSector(diskImp, out startSector) == false) { Console.WriteLine("Lost connection to {0}.\n", deviceName); delete diskImp; return null; } ulong totalSectors; if (GetDiskSectorCount(diskImp, out totalSectors) == false) { Console.WriteLine("Lost connection to {0}.\n", deviceName); delete diskImp; return null; } ushort systemId; if (GetDiskSystemId(diskImp, out systemId) == false) { Console.WriteLine("Lost connection to {0}.\n", deviceName); delete diskImp; return null; } DiskAttributes da; if (GetDiskAttributes(diskImp, out da) == false) { Console.WriteLine("Lost connection to {0}.\n", deviceName); delete diskImp; return null; } // // Initialize endpoints for handling concurrent I/O requests // DiskEndpointPool pool = new DiskEndpointPool(channelCount); pool.Add(new DiskEndpoint(diskImp)); for (int i = 1; i < channelCount; i++) { diskImp = OpenDevice(nsImp, deviceName); if (diskImp == null) { Console.WriteLine("Failed establishing connection {0} to {1}.\n", i, deviceName); return null; } pool.Add(new DiskEndpoint(diskImp)); } // // Construct and return Disk object // return new Disk(deviceName, pool, startSector, totalSectors, systemId); } } }