public void Enqueue(IRP irp) { lock (IrpList) { IrpList.Enqueue(irp); } }
public IRP Dequeue() { IRP irp = null; lock (IrpList) { irp = IrpList.Dequeue(); } return(irp); }
public void FreeIrp(IRP irp) { if (irp.State != IrpState.Free) { throw new ApplicationException("FreeIrp() : Irp!=Free."); } irp.State = IrpState.Pool; lock (IrpPoolLock) { Debug.Assert(irp.Next == null, "FreeIrp expected single irp, got multi-irp chain"); irp.Next = FreeIrpPool; FreeIrpPool = irp; irp.IoFlow = null; countFreeIrpList++; } }
// // Obtain one Irp object from free Irp list. // internal IRP AllocateIrp() { IRP irp = null; lock (IrpPoolLock) { if (FreeIrpPool != null) { irp = FreeIrpPool; FreeIrpPool = FreeIrpPool.Next; countFreeIrpList--; } } if (irp == null) // Expected to fail, with some diags. { throw new ApplicationException("Free Irp pool exhausted"); } irp.Next = irp.Prev = null; irp.State = IrpState.Free; return(irp); }
private PreWriteReturnCode PreWrite(IRP irp) { if (noisyOutput) Console.WriteLine("PreWrite {0}: Got Offset={1} Length={2} Align={3}", Thread.CurrentThread.ManagedThreadId, irp.FileOffset, irp.DataLength, cache.AlignedBlockSize); if (irp.DataLength % cache.AlignedBlockSize != 0 || irp.FileOffset % cache.AlignedBlockSize != 0) { Console.WriteLine("PreWrite {0}: Got UNALIGNED Offset={1} Length={2} Align={3}", Thread.CurrentThread.ManagedThreadId, irp.FileOffset, irp.DataLength, cache.AlignedBlockSize); } //Console.WriteLine("OFFSETS,LENGTHS,{0},{1}", irp.FileOffset, irp.DataLength); //return PreWriteReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; return cache.CacheWriteIRP(irp); //return PreWriteReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; }
private PreCreateReturnCode PreCreate(IRP irp) { return cache.CachePreCreate(irp); //return PreCreateReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; }
private PreCleanupReturnCode PreCleanup(IRP irp) { //return PreCleanupReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; return cache.CachePreCleanup(irp); }
/// <summary> /// Caches a given IRP on the way down /// </summary> /// <param name="irp"></param> /// <returns></returns> public PreReadReturnCode CacheIRP(IRP irp) { // lookup if IRP is already cached Dictionary<string, Dictionary<uint, FileCacheElement>> f = null; Dictionary<uint, FileCacheElement> b = null; FileCacheElement ce = null; bool hit = false; bool anyMisses = false; FlowSLA sla = null; ulong savedFileOffset = irp.FileOffset; uint savedDataLength = irp.DataLength; uint dataOffset = irp.DataOffset; ulong fileOffset = savedFileOffset; bool canSatisfyRequest = false; Debug.Assert(irp.IoFlowHeader.MajorFunction == MajorFunction.IRP_MJ_READ); if (noisyOutput) Console.WriteLine("CacheIRP {0}: Attempting to lock cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); Monitor.Enter(cacheLock); sla = FlowStats[irp.FlowId]; Debug.Assert(sla != null); //Only deal with explicitly declared flows //Do we have enough cache space available to satisfy this request? if (sla.FlowCacheSize > irp.DataLength) { canSatisfyRequest = true; } if (noisyOutput) Console.WriteLine("CacheIRP {0}: Locked cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (canSatisfyRequest) { // iterate over all blocks // it's a hit if all blocks are a hit, otherwise its a miss for the entire IRP do { uint blockid = (uint)(fileOffset / ALIGNED_BLOCK_SIZE); hit = false; //block isn't a hit yet { if (Cache.TryGetValue(irp.FlowId, out f)) { if (f.TryGetValue(irp.IoFlow.FileName, out b)) { if (b.TryGetValue(blockid, out ce)) { // cache entry exists if (noisyOutput) Console.WriteLine("CacheIRP {0}: Attempting to lock ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); lock (ce.LockObj) { if (noisyOutput) Console.WriteLine("CacheIRP {0}: Locked ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (ce.Data != null) { // real hit ; cache entry has data being read sla = FlowStats[irp.FlowId]; Debug.Assert(sla != null); //We should always have a valid sla entry if we have a hit hit = true; byte[] irpData = irp.GetDataReadWrite(); Debug.Assert(ce.DataLength == ALIGNED_BLOCK_SIZE); Buffer.BlockCopy(ce.Data, 0, irpData, (int)dataOffset, (int)ALIGNED_BLOCK_SIZE); Debug.Assert(ce.nodeInList != null); Debug.Assert(ce.nodeInList != null); sla.cacheEntries.Remove(ce.nodeInList); //Assumes no duplicate ce's in the list, which should be true... //ce.UpdateNodeList(sla.cacheEntries.AddLast(ce)); sla.cacheEntries.AddLast(ce.nodeInList); if (sla.FlowSLAHasGhostCache()) { sla.GhostCache.CacheReadReference(ce.fileName + Convert.ToString(blockid)); //Forward the reference to the ghost cache } } else { // cache entry exists, BUT data is still in-flight from storage medium hit = false; } } if (noisyOutput) Console.WriteLine("CacheIRP {0}: UnLocked ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); } } } if (!hit) { // evict first Evict(irp.FlowId); // then insert if (f == null) { Cache[irp.FlowId] = new Dictionary<string, Dictionary<uint, FileCacheElement>>(); f = Cache[irp.FlowId]; } if (b == null) { f[irp.IoFlow.FileName] = new Dictionary<uint, FileCacheElement>(); b = f[irp.IoFlow.FileName]; } if (ce == null) { //b[blockid] = new FileCacheElement(irp.IoFlow, irp.IoFlowRuntime.getDriveLetterFileName(irp.IoFlow.FileName), null, // fileOffset, dataOffset, ALIGNED_BLOCK_SIZE /* copying data only */); //string tempFileNameChunking = irp.IoFlowRuntime.getDriveLetterFileName(irp.IoFlow.FileName); //save file name here once, so we don't do multiple calls to this b[blockid] = getFileCacheElement(irp.IoFlow, irp.IoFlow.FileName, null, fileOffset, dataOffset, ALIGNED_BLOCK_SIZE /* copying data only */); ce = b[blockid]; // insert element into list if (false == FlowStats.TryGetValue(irp.FlowId, out sla)) { //sla = new FlowSLA(); //FlowStats[irp.FlowId] = sla; Debug.Assert(0 == 1); //XXXIS let's only deal with explicitly declared flows right now } ce.UpdateNodeList(sla.cacheEntries.AddLast(ce)); cacheSizeUsedBytes += ALIGNED_BLOCK_SIZE; sla.FlowCacheSizeUsedBytes += ALIGNED_BLOCK_SIZE; if (sla.FlowSLAHasGhostCache()) { sla.GhostCache.CacheReadReference(ce.fileName + Convert.ToString(blockid)); //Forward the reference to the ghost cache } } } } if (noisyOutput) Console.WriteLine("CacheIRP {0}: UnLock cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); fileOffset += ALIGNED_BLOCK_SIZE; dataOffset += ALIGNED_BLOCK_SIZE; if (hit == false) { anyMisses = true; } } while (fileOffset < savedFileOffset + savedDataLength); if (false == FlowStats.TryGetValue(irp.FlowId, out sla)) { Debug.Assert(0 == 1); //XXXIS let's only deal with explicitly declared flows right now } if (!anyMisses) { sla.CacheAccessesHits++; //Increment the number of hits in the cache } } sla.CacheAccessesTotal++; //increment all the accesses to this cache sla.FlowBytesAccessed += irp.DataLength; Monitor.Exit(cacheLock); // deal with MISSES // Let IRP go through and intercept POST operation (with payload) // if (anyMisses == true || !canSatisfyRequest) { //Console.WriteLine("MISS: {0}", irp.FileOffset); if (noisyOutput) Console.WriteLine("CacheIRP {0}: PreRead MISS Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, irp.FileOffset, irp.DataLength); return PreReadReturnCode.FLT_PREOP_SUCCESS_WITH_CALLBACK; } else { //Console.WriteLine("HIT: {0}", irp.FileOffset); if (noisyOutput) Console.WriteLine("CacheIRP {0}: PreRead HIT Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, irp.FileOffset, irp.DataLength); return PreReadReturnCode.FLT_PREOP_COMPLETE; } }
private PostReadReturnCode InvalidPostRead(IRP irp) { throw new NotImplementedException(String.Format("FlowId {0} PostRead callback not registered.", FlowId)); }
private PreLockControlReturnCode InvalidPreLockControl(IRP irp) { throw new NotImplementedException(String.Format("FlowId {0} PreLockControl callback not registered.", FlowId)); }
public PreCreateReturnCode CachePreCreate(IRP irp) { return PreCreateReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; }
/// <summary> /// Iocp completion thread using GetQueuedCompletionStatus(). /// </summary> /// <param name="o"></param> private void IocpThreadProc(object o) { // // Start the READ requests to minifilter driver. // for (int i = 0; i < Parameters.COUNT_IO_READ_MPL; i++) { IRP irp = AllocateIrp(); irp.StartMiniFilterGetMessage(); } while (!ShuttingDown) { uint lpNumberOfBytes; uint lpCompletionKey; IntPtr lpOverlapped; FLT_PREOP_CALLBACK_STATUS PreOpResult = FLT_PREOP_CALLBACK_STATUS.FLT_PREOP_SUCCESS_WITH_CALLBACK; FLT_POSTOP_CALLBACK_STATUS PostOpResult = FLT_POSTOP_CALLBACK_STATUS.FLT_POSTOP_FINISHED_PROCESSING; // // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364986(v=vs.85).aspx // if (!GetQueuedCompletionStatus(hCompletionPort, // HANDLE CompletionPort out lpNumberOfBytes, // LPDWORD lpNumberOfBytes out lpCompletionKey, // PULONG_PTR lpCompletionKey out lpOverlapped, // LPOVERLAPPED lpOverlapped WIN32_INFINITE)) // DWORD dwMilliseconds { int HResult = Marshal.GetHRForLastWin32Error(); // // Expect to get here when Close() has been called. // const uint THE_HANDLE_IS_INVALID = 0x80070006; // Not an error. const uint ERROR_ABANDONED_WAIT_0 = 0x800702DF; // Not an error. if ((uint)HResult == THE_HANDLE_IS_INVALID || (uint)HResult == ERROR_ABANDONED_WAIT_0) { break; } // // Unexpected error. // int LastWin32Error = Marshal.GetLastWin32Error(); Console.WriteLine("LastWin32Error {0} HResult {1:X8}", LastWin32Error, HResult); Marshal.ThrowExceptionForHR((int)HResult); } // // Get from native addr of completing OVERLAPPED struct to the associated managed code IRP. // IRP irp = FindIrp[lpOverlapped]; irp.CompleteMiniFilterGetMessage(); // // Find the IoFlow for this IRP and call the user's callback function. // LockIoFlow.EnterReadLock(); irp.IoFlow = DictIoFlow[irp.IoFlowHeader.FlowId]; LockIoFlow.ExitReadLock(); // // Indicate any late errors for earlier IO indicated on this flow. // These can happen if an error happens in the driver *after* the // filterMgr messaging API has indicated that the reply message has // been successfully delivered to the driver. In priciple the owning // app should also know due to IO failure. // if ((irp.IoFlowHeader.Flags & (uint)HeaderFlags.HeaderFlagLateIoError) != 0) { string msg = "One or more errors reported for earlier IO on flowId "; msg = string.Format("{0} {1} file {2}", msg, irp.IoFlow.FlowId, irp.IoFlow.FileName); throw new ExceptionIoFlow(msg); } // // Throw iff the kmode IRP's buffer is too large for our k2u buffers. // In this case we see incomplete data so any ops on the data are invalid. // if (irp.DataLength > irp.MaxDataLength) { string msg = string.Format("Err irp.DataLength({0}) > k2u buffer size({1})", irp.DataLength, irp.MaxDataLength); throw new ExceptionIoFlow(msg); } // // Upcall to user-supplied callback routine. // Note the minifilter only sends IRPs for flows that have registered an appropriate callback routine. // bool IsPreOp = irp.IsPreOp, IsPostOp = !IsPreOp; switch (irp.MajorFunction) { case MajorFunction.IRP_MJ_CREATE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCreate(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCreate(irp); } break; } case MajorFunction.IRP_MJ_READ: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreRead(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostRead(irp); } break; } case MajorFunction.IRP_MJ_WRITE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreWrite(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostWrite(irp); } break; } case MajorFunction.IRP_MJ_CLEANUP: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCleanup(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCleanup(irp); } break; } case MajorFunction.IRP_MJ_LOCK_CONTROL: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreLockControl(irp); } else { throw new NotImplementedException(String.Format("irp.IoFlow.FlowId {0} PostLockControl callback not supported.", irp.IoFlow.FlowId)); } break; } default: throw new ApplicationException("should never get here."); } irp.SetStateCallbackReturned(); irp.IoFlowHeader.FilterReplyHeader.Status = 0; irp.IoFlowHeader.Resultcode = (irp.IsPreOp ? (uint)PreOpResult : (uint)PostOpResult); #if UseReplyAndGetNext // // App is done with this IRP. // Send reply to minifilter driver and restart next upcall on same IRP. // irp.MiniFilterReplyAndGetNext(hDataAsyncPort, hControlPort); #else // UseReplyAndGetNext // // Send reply to our minifilter driver. // irp.MiniFilterReplyMessage(hDataAsyncPort, hControlPort); // // The IRP is no longer in use by app - we can restart next upcall request on same IRP. // irp.StartMiniFilterGetMessage(); #endif // UseReplyAndGetNext } if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) { LastThreadTerminated.Set(); } }
private PreCleanupReturnCode PreCleanup(IRP irp) { return cache.CachePreCleanup(irp); }
private PreCreateReturnCode PreCreate(IRP irp) { return this.cache.CachePreCreate(irp); }
private PostWriteReturnCode PostWrite(IRP irp) { return cache.CacheWriteIRPCompleted(irp); }
private PostReadReturnCode PostRead(IRP irp) { return cache.CacheIRPCompleted(irp); }
public PreCleanupReturnCode CachePreCleanup(IRP irp) { return PreCleanupReturnCode.FLT_PREOP_SUCCESS_NO_CALLBACK; }
private void IoFlowRuntimeInternal(uint maxIocpThreads, bool useIocpEx) { Console.WriteLine("IoFlowRuntime created with {0} iocp threads", maxIocpThreads); this.countIocpThreads = maxIocpThreads; LockIoFlow = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); if (this.countIocpThreads < System.Environment.ProcessorCount) { Console.WriteLine("IoFlowRuntime: Warning: MPL {0} < ProcessorCount {1}", this.countIocpThreads, System.Environment.ProcessorCount); } uint HResult; TenantId = (uint)Process.GetCurrentProcess().Id; // // Open FltMgr comms port for asynchronous data exchange with IoFlowUser minifilter driver. // string strDataPortName = Parameters.DATA_PORT_NAME; HResult = FilterConnectCommunicationPort(strDataPortName, // LPCWSTR lpPortName, 0, // DWORD dwOptions, IntPtr.Zero, // LPCVOID lpContext, 0, // WORD dwSizeOfContext IntPtr.Zero, // LP_SECURITY_ATTRIBUTES lpSecurityAttributes out hDataAsyncPort); // HANDLE *hPort if (HResult != S_OK) { Console.WriteLine("IoFlowRuntime failed to contact IoFlow minifilter driver async: check the driver is loaded."); Marshal.ThrowExceptionForHR((int)HResult); } // // Open FltMgr comms port for control messages to IoFlowUser minifilter driver. // We open the control port for synchronous I/O because we required calls to // FilterReplyMessage to complete synchronously when pending IRPs to kernel-mode // cancelsafe queue *before* the app calls CompletePendedPreOp() or // CompletePendedPostOp() in the days when we supported pending ops. // string strControlPortName = Parameters.CONTROL_PORT_NAME; HResult = FilterConnectCommunicationPort(strControlPortName, // LPCWSTR lpPortName, 0, // DWORD dwOptions, IntPtr.Zero, // LPCVOID lpContext, 0, // WORD dwSizeOfContext IntPtr.Zero, // LP_SECURITY_ATTRIBUTES lpSecurityAttributes out hControlPort); // HANDLE *hPort if (HResult != S_OK) { Marshal.ThrowExceptionForHR((int)HResult); } // // Purge any old state from the minifilter driver. // DriverReset(); // // Dictionary implements lookup func f(FlowId)=>IoFlow // DictIoFlow = new Dictionary <uint, IoFlow>(); // // Open I/O completion port for completing async I/O to the minifilter driver. // hCompletionPort = CreateIoCompletionPort(hDataAsyncPort, hCompletionPort, // HANDLE ExistingCompletionPort IntPtr.Zero, // ULONG_PTR CompletionKey (uint)maxIocpThreads); // DWORD NumberOfConcurrentThreads if (hCompletionPort == IntPtr.Zero) { int err = Marshal.GetHRForLastWin32Error(); Console.WriteLine("CreateIoCompletionPort err={0:X8}", err); Marshal.ThrowExceptionForHR(err); } // // Init dict for translating friendly drive names to unfriendly device names. // For example on my dev machine "D:" => "\Device\HarddiskVolume4" // No .NET support for discovering this, so we scan stdout from "fltmc volume". // DriveNameToVolumeName = new Dictionary <string, string>(); Process process = new Process(); process.StartInfo = new ProcessStartInfo("fltmc.exe", "volumes"); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); string s1 = null; string[] separators = new string[] { " ", "\t" }; while ((s1 = process.StandardOutput.ReadLine()) != null) { if (s1.Length > 1 && s1.Contains(@":")) { string[] toks = s1.Split(separators, StringSplitOptions.RemoveEmptyEntries); if (toks.Length > 1 && toks[0].Length == 2 && toks[0].Substring(1, 1).Equals(@":") && toks[1].Length > DeviceHarddiskVolume.Length && toks[1].ToLower().StartsWith(DeviceHarddiskVolume)) { DriveNameToVolumeName.Add(toks[0].ToLower(), toks[1].ToLower()); } } } process.WaitForExit(); VolumeNameToDriveName = DriveNameToVolumeName.ToDictionary(x => x.Value, x => x.Key); //gets the reverse dictionary (this assumes no duplicate values) // // Allocate pool of statically allocated Irp objects for I/O to/from minifilter driver. // int countIrps = Parameters.IRP_POOL_SIZE * (int)maxIocpThreads; FindIrp = new Dictionary <IntPtr, IRP>(countIrps); irpArray = new IRP[countIrps]; for (int i = 0; i < irpArray.Length; i++) { IRP irp = new IRP(this, hDataAsyncPort); FindIrp.Add(irp.addrOverlapped, irp); irpArray[i] = irp; irp.State = IrpState.Free; FreeIrp(irp); } // // Prep the requested number of IO completion worker threads. // arrayIocpThreads = new Thread[countIocpThreads]; for (int i = 0; i < countIocpThreads; i++) { if (useIocpEx) { arrayIocpThreads[i] = new Thread(IocpThreadProcEx); } else { arrayIocpThreads[i] = new Thread(IocpThreadProc); } } // // Iff a local oktofsagent exists try for a connection for fast sid lookup. // Assumes oktofsagent is listening on its default TCP port. // bridgeOktofsAgent = new BridgeOktofsAgent(); if (bridgeOktofsAgent.Connect() && bridgeOktofsAgent.SendMessageRegister()) { Console.WriteLine("Registered with local oktofsAgent for fast (account,SID) lookup."); } else { bridgeOktofsAgent = null; } }
/// <summary> /// Intercepts callback after a write succeeds on write-through /// </summary> /// <param name="irp"></param> /// <returns></returns> public PostWriteReturnCode CacheWriteIRPCompleted(IRP irp) { return PostWriteReturnCode.FLT_POSTOP_FINISHED_PROCESSING; }
private PreReadReturnCode PreRead(IRP irp) { return this.cache.CacheIRP(irp); }
/// <summary> /// Intercepts a write request, caches it, then sends it down if doing write-through, or returns success and writes it out later if doing write-back /// XXXET: shares a lot of functionality with cacheIRP. Probably should be 1 function /// </summary> /// <param name="irp"></param> /// <returns></returns> public PreWriteReturnCode CacheWriteIRP(IRP irp) { Dictionary<string, Dictionary<uint, FileCacheElement>> f = null; Dictionary<uint, FileCacheElement> b = null; FileCacheElement ce = null; FlowSLA sla = null; bool blockUpdated = false; bool anyBlockUpdated = false; ulong savedFileOffset = irp.FileOffset; uint savedDataLength = irp.DataLength; uint dataOffset = irp.DataOffset; ulong fileOffset = savedFileOffset; bool writeHit = false; bool canSatisfyRequest = false; Debug.Assert(irp.IoFlowHeader.MajorFunction == MajorFunction.IRP_MJ_WRITE); if ((int)irp.IoFlowHeader.ProcessID != CacheProcessID) //don't intercept traffic we generated { if (noisyOutput) Console.WriteLine("CacheWriteIRP {0}: Attempting to lock cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); Monitor.Enter(cacheLock); if (noisyOutput) Console.WriteLine("CacheWriteIRP {0}: Locked cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); //string tempFileNameChunking = irp.IoFlowRuntime.getDriveLetterFileName(irp.IoFlow.FileName); //save file name here once, so we don't do multiple calls to this // iterate over all blocks // it's a hit if all blocks are a hit, otherwise its a miss for the entire IRP do { uint blockid = (uint)(fileOffset / ALIGNED_BLOCK_SIZE); //Get the flow stats list if (!FlowStats.TryGetValue(irp.FlowId, out sla)) { //sla = new FlowSLA(); //FlowStats[irp.FlowId] = sla; Debug.Assert(0 == 1); //XXXIS let's only deal with explicitly declared flows right now } if (!Cache.TryGetValue(irp.FlowId, out f)) { Cache[irp.FlowId] = new Dictionary<string, Dictionary<uint, FileCacheElement>>(); f = Cache[irp.FlowId]; } if (!f.TryGetValue(irp.IoFlow.FileName, out b)) { f[irp.IoFlow.FileName] = new Dictionary<uint, FileCacheElement>(); b = f[irp.IoFlow.FileName]; } if (!b.TryGetValue(blockid, out ce)) // block is not currently cached { if (this.cacheWrites == CacheWriteBuffer.Cache) // only cache the block if write caching is turned on { //b[blockid] = new FileCacheElement(irp.IoFlow, irp.IoFlowRuntime.getDriveLetterFileName(irp.IoFlow.FileName), null, // fileOffset, dataOffset, ALIGNED_BLOCK_SIZE /* copying data only */); b[blockid] = getFileCacheElement(irp.IoFlow, irp.IoFlow.FileName, null, fileOffset, dataOffset, ALIGNED_BLOCK_SIZE /* copying data only */); ce = b[blockid]; //Might need to evict if we don't have enough space for the new entry Evict(irp.FlowId); ce.UpdateNodeList(sla.cacheEntries.AddLast(ce)); //Just create the cache entry cacheSizeUsedBytes += ALIGNED_BLOCK_SIZE; sla.FlowCacheSizeUsedBytes += ALIGNED_BLOCK_SIZE; } } // block is in the cache; only update if the block has data in flight (eg: a read), or if write caching is turned on if ((this.cacheWrites == CacheWriteBuffer.noCache && ce != null) || this.cacheWrites == CacheWriteBuffer.Cache) { { lock (ce.LockObj) { if (noisyOutput) Console.WriteLine("CacheWriteIRP {0}: Locked ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (noisyOutput) Console.WriteLine("CacheWriteIRP {0}: Caching write on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); ce.UpdateData(irp.GetDataReadOnly(), dataOffset, ALIGNED_BLOCK_SIZE /* copying data */); blockUpdated = true; //Move to the front of the LRU list Debug.Assert(ce.nodeInList != null); sla.cacheEntries.Remove(ce.nodeInList); sla.cacheEntries.AddLast(ce.nodeInList); //XXXIS: send all writes to ghost cache if (sla.FlowSLAHasGhostCache()) { sla.GhostCache.CacheReadReference(ce.fileName + Convert.ToString(blockid)); //Forward the reference to the ghost cache } } } } fileOffset += ALIGNED_BLOCK_SIZE; dataOffset += ALIGNED_BLOCK_SIZE; if (blockUpdated == true) { anyBlockUpdated = true; } } while (fileOffset < savedFileOffset + savedDataLength); sla.CacheAccessesTotal++; //update total number of ops that passed through this cache sla.FlowBytesAccessed += irp.DataLength; if (writeHit) { sla.CacheAccessesHits++; } Monitor.Exit(cacheLock); if (noisyOutput) Console.WriteLine("CacheWriteIRP {0}: UnLocked cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (this.writePolicy == CacheWritePolicy.WriteThrough || anyBlockUpdated == true) //if write-through, or waiting for disk to reply on a read for some block we wrote { return PreWriteReturnCode.FLT_PREOP_SUCCESS_WITH_CALLBACK; //send it down } else { Debug.Assert(0 == 1); //we shouldn't be in this case } } return PreWriteReturnCode.FLT_PREOP_COMPLETE; //return complete (ignore traffic we generated) }
/// <summary> /// Iocp completion thread using GetQueuedCompletionStatusEx(). /// You would expect to get benefit from this over GetQueuedCompletionStatus() but in tests /// we've not seen that, at least not yet, but you can select it at ctor time hoping we /// can find a way to unlock its perf in future. /// </summary> /// <param name="o"></param> private void IocpThreadProcEx(object o) { try { Console.WriteLine("IoFlowRuntime: using experimental GetQueuedCompletionStatusEx() thread."); // // IOCP thread init: start multiple concurrent READ IRPS to driver. // Note all I/O must originate on this thread for Win32 CancelIo to work. // for (int i = 0; i < Parameters.COUNT_IO_READ_MPL; i++) { IRP irp = AllocateIrp(); irp.StartMiniFilterGetMessage(); } ThreadPriority oldThreadPriority = Thread.CurrentThread.Priority; Thread.CurrentThread.Priority = ThreadPriority.Highest; // // Prep for GetQueuedCompletionStatusEx(). // IntPtr lpOverlapped; uint lpNumberOfBytes; uint lpCompletionKey; OVERLAPPED_ENTRY[] ArrayOverlappedEntry = new OVERLAPPED_ENTRY[IocpSizeofOverlappedArray]; GCHandle gcArrayOverlappedEntry = GCHandle.Alloc(ArrayOverlappedEntry, GCHandleType.Pinned); IntPtr addrArrayOverlappedEntry = gcArrayOverlappedEntry.AddrOfPinnedObject(); uint NumEntriesRemoved = 1; FLT_PREOP_CALLBACK_STATUS PreOpResult = FLT_PREOP_CALLBACK_STATUS.FLT_PREOP_SUCCESS_WITH_CALLBACK; FLT_POSTOP_CALLBACK_STATUS PostOpResult = FLT_POSTOP_CALLBACK_STATUS.FLT_POSTOP_FINISHED_PROCESSING; // // IOCP main loop: think -- {completePrior; flow.Callback(IRP); startNext}. // while (ShuttingDown == false) // IOCP main loop. { // // Block until IoCompletionPort indicates a) I/O completion or b) some signal. // if (GetQueuedCompletionStatusEx(hCompletionPort, addrArrayOverlappedEntry, IocpSizeofOverlappedArray, out NumEntriesRemoved, Parameters.IocpTimeoutInfinite, // Parameters.IocpTimeoutMSecs, //WIN32_INFINITE, false) == false) { int HResult = Marshal.GetHRForLastWin32Error(); int LastWin32Error = Marshal.GetLastWin32Error(); // // Expect to get here when Close() has been called. // const uint THE_HANDLE_IS_INVALID = 0x80070006; // Not an error. const uint ERROR_ABANDONED_WAIT_0 = 0x800702DF; // Not an error. if ((uint)HResult == THE_HANDLE_IS_INVALID || (uint)HResult == ERROR_ABANDONED_WAIT_0) { break; } // // In absence of I/O load, iocp timeout opt triggers polled queued I/O support. // if (LastWin32Error == WIN32_WAIT_TIMEOUT) { continue; } // // Handle I/O failures. Non-fatal iff timeout or if media disconnected. // if (LastWin32Error == WIN32_ERROR_GEN_FAILURE) { Console.WriteLine("warning: ignored iocp tx status ERROR_GEN_FAILURE"); continue; } // // Unexpected error. // Console.WriteLine("LastWin32Error {0} HResult {1:X8}", LastWin32Error, HResult); Marshal.ThrowExceptionForHR((int)HResult); } // // One iteration per entry in array returned from I/O completion port. // for (int i = 0; i < (int)NumEntriesRemoved; i++) { lpOverlapped = ArrayOverlappedEntry[i].addrOverlapped; lpCompletionKey = (uint)ArrayOverlappedEntry[i].lpCompletionKey.ToInt32(); // // Get from native addr of completing OVERLAPPED struct to the associated managed code IRP. // IRP irp = FindIrp[lpOverlapped]; irp.CompleteMiniFilterGetMessage(); // // Find the IoFlow for this IRP and call the user's callback function. // LockIoFlow.EnterReadLock(); irp.IoFlow = DictIoFlow[irp.IoFlowHeader.FlowId]; LockIoFlow.ExitReadLock(); // // Indicate any late errors for earlier IO indicated on this flow. // These can happen if an error happens in the driver *after* the // filterMgr messaging API has indicated that the reply message has // been successfully delivered to the driver. In priciple the owning // app should also know due to IO failure. // if ((irp.IoFlowHeader.Flags & (uint)HeaderFlags.HeaderFlagLateIoError) != 0) { string msg = "One or more errors reported for earlier IO on flowId "; msg = string.Format("{0} {1} file {2}", msg, irp.IoFlow.FlowId, irp.IoFlow.FileName); throw new ExceptionIoFlow(msg); } // // Throw iff the kmode IRP's buffer is too large for our k2u buffers. // In this case we see incomplete data so any ops on the data are invalid. // if (irp.DataLength > irp.MaxDataLength) { string msg = string.Format("Err irp.DataLength({0}) > k2u buffer size({1})", irp.DataLength, irp.MaxDataLength); throw new ExceptionIoFlow(msg); } // // Upcall to user-supplied callback routine. // Note the minifilter only sends IRPs for flows that have registered an appropriate callback routine. // bool IsPreOp = irp.IsPreOp, IsPostOp = !IsPreOp; switch (irp.MajorFunction) { case MajorFunction.IRP_MJ_CREATE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCreate(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCreate(irp); } break; } case MajorFunction.IRP_MJ_READ: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreRead(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostRead(irp); } break; } case MajorFunction.IRP_MJ_WRITE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreWrite(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostWrite(irp); } break; } case MajorFunction.IRP_MJ_CLEANUP: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCleanup(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCleanup(irp); } break; } case MajorFunction.IRP_MJ_LOCK_CONTROL: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreLockControl(irp); } else { throw new NotImplementedException(String.Format("irp.IoFlow.FlowId {0} PostLockControl callback not supported.", irp.IoFlow.FlowId)); } break; } default: throw new ApplicationException("should never get here."); } irp.SetStateCallbackReturned(); irp.IoFlowHeader.FilterReplyHeader.Status = 0; irp.IoFlowHeader.Resultcode = (irp.IsPreOp ? (uint)PreOpResult : (uint)PostOpResult); #if UseReplyAndGetNext // // App is done with this IRP. // Send reply to minifilter driver and restart next upcall on same IRP. // irp.MiniFilterReplyAndGetNext(hDataAsyncPort, hControlPort); #else // UseReplyAndGetNext // // Send reply to our minifilter driver. // irp.MiniFilterReplyMessage(hDataAsyncPort, hControlPort); // // The IRP is no longer in use by app - we can restart next upcall request on same IRP. // irp.StartMiniFilterGetMessage(); #endif // UseReplyAndGetNext } //for (int i = 0; i < (int)NumEntriesRemoved; i++) } // while (ShuttingDown == false) // IOCP main loop. // // Shut down. // CancelIo(hDataAsyncPort); } catch (ThreadInterruptedException CaughtException) { if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) { LastThreadTerminated.Set(); } if (ShuttingDown) { return; } throw new ThreadInterruptedException(null, CaughtException); } if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) { LastThreadTerminated.Set(); } }
private PreWriteReturnCode PreWrite(IRP irp) { return cache.CacheWriteIRP(irp); }
private void IoFlowRuntimeInternal(uint maxIocpThreads, bool useIocpEx) { Console.WriteLine("IoFlowRuntime created with {0} iocp threads", maxIocpThreads); this.countIocpThreads = maxIocpThreads; LockIoFlow = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); if (this.countIocpThreads < System.Environment.ProcessorCount) Console.WriteLine("IoFlowRuntime: Warning: MPL {0} < ProcessorCount {1}", this.countIocpThreads, System.Environment.ProcessorCount); uint HResult; TenantId = (uint)Process.GetCurrentProcess().Id; // // Open FltMgr comms port for asynchronous data exchange with IoFlowUser minifilter driver. // string strDataPortName = Parameters.DATA_PORT_NAME; HResult = FilterConnectCommunicationPort(strDataPortName, // LPCWSTR lpPortName, 0, // DWORD dwOptions, IntPtr.Zero, // LPCVOID lpContext, 0, // WORD dwSizeOfContext IntPtr.Zero, // LP_SECURITY_ATTRIBUTES lpSecurityAttributes out hDataAsyncPort); // HANDLE *hPort if (HResult != S_OK) { Console.WriteLine("IoFlowRuntime failed to contact IoFlow minifilter driver async: check the driver is loaded."); Marshal.ThrowExceptionForHR((int)HResult); } // // Open FltMgr comms port for control messages to IoFlowUser minifilter driver. // We open the control port for synchronous I/O because we required calls to // FilterReplyMessage to complete synchronously when pending IRPs to kernel-mode // cancelsafe queue *before* the app calls CompletePendedPreOp() or // CompletePendedPostOp() in the days when we supported pending ops. // string strControlPortName = Parameters.CONTROL_PORT_NAME; HResult = FilterConnectCommunicationPort(strControlPortName, // LPCWSTR lpPortName, 0, // DWORD dwOptions, IntPtr.Zero, // LPCVOID lpContext, 0, // WORD dwSizeOfContext IntPtr.Zero, // LP_SECURITY_ATTRIBUTES lpSecurityAttributes out hControlPort); // HANDLE *hPort if (HResult != S_OK) { Marshal.ThrowExceptionForHR((int)HResult); } // // Purge any old state from the minifilter driver. // DriverReset(); // // Dictionary implements lookup func f(FlowId)=>IoFlow // DictIoFlow = new Dictionary<uint, IoFlow>(); // // Open I/O completion port for completing async I/O to the minifilter driver. // hCompletionPort = CreateIoCompletionPort(hDataAsyncPort, hCompletionPort, // HANDLE ExistingCompletionPort IntPtr.Zero, // ULONG_PTR CompletionKey (uint)maxIocpThreads); // DWORD NumberOfConcurrentThreads if (hCompletionPort == IntPtr.Zero) { int err = Marshal.GetHRForLastWin32Error(); Console.WriteLine("CreateIoCompletionPort err={0:X8}", err); Marshal.ThrowExceptionForHR(err); } // // Init dict for translating friendly drive names to unfriendly device names. // For example on my dev machine "D:" => "\Device\HarddiskVolume4" // No .NET support for discovering this, so we scan stdout from "fltmc volume". // DriveNameToVolumeName = new Dictionary<string, string>(); Process process = new Process(); process.StartInfo = new ProcessStartInfo("fltmc.exe", "volumes"); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); string s1 = null; string[] separators = new string[] { " ", "\t" }; while ((s1 = process.StandardOutput.ReadLine()) != null) { if (s1.Length > 1 && s1.Contains(@":")) { string[] toks = s1.Split(separators, StringSplitOptions.RemoveEmptyEntries); if (toks.Length > 1 && toks[0].Length == 2 && toks[0].Substring(1, 1).Equals(@":") && toks[1].Length > DeviceHarddiskVolume.Length && toks[1].ToLower().StartsWith(DeviceHarddiskVolume)) { DriveNameToVolumeName.Add(toks[0].ToLower(), toks[1].ToLower()); } } } process.WaitForExit(); VolumeNameToDriveName = DriveNameToVolumeName.ToDictionary(x => x.Value, x => x.Key); //gets the reverse dictionary (this assumes no duplicate values) // // Allocate pool of statically allocated Irp objects for I/O to/from minifilter driver. // int countIrps = Parameters.IRP_POOL_SIZE * (int)maxIocpThreads; FindIrp = new Dictionary<IntPtr, IRP>(countIrps); irpArray = new IRP[countIrps]; for (int i = 0; i < irpArray.Length; i++) { IRP irp = new IRP(this, hDataAsyncPort); FindIrp.Add(irp.addrOverlapped, irp); irpArray[i] = irp; irp.State = IrpState.Free; FreeIrp(irp); } // // Prep the requested number of IO completion worker threads. // arrayIocpThreads = new Thread[countIocpThreads]; for (int i = 0; i < countIocpThreads; i++) { if (useIocpEx) arrayIocpThreads[i] = new Thread(IocpThreadProcEx); else arrayIocpThreads[i] = new Thread(IocpThreadProc); } // // Iff a local oktofsagent exists try for a connection for fast sid lookup. // Assumes oktofsagent is listening on its default TCP port. // bridgeOktofsAgent = new BridgeOktofsAgent(); if (bridgeOktofsAgent.Connect() && bridgeOktofsAgent.SendMessageRegister()) Console.WriteLine("Registered with local oktofsAgent for fast (account,SID) lookup."); else bridgeOktofsAgent = null; }
public void FreeIrp(IRP irp) { if (irp.State != IrpState.Free) throw new ApplicationException("FreeIrp() : Irp!=Free."); irp.State = IrpState.Pool; lock (IrpPoolLock) { Debug.Assert(irp.Next == null, "FreeIrp expected single irp, got multi-irp chain"); irp.Next = FreeIrpPool; FreeIrpPool = irp; irp.IoFlow = null; countFreeIrpList++; } }
private PostCleanupReturnCode InvalidPostCleanup(IRP irp) { throw new NotImplementedException(String.Format("FlowId {0} PostCleanup callback not registered.", FlowId)); }
// // Obtain one Irp object from free Irp list. // internal IRP AllocateIrp() { IRP irp = null; lock (IrpPoolLock) { if (FreeIrpPool != null) { irp = FreeIrpPool; FreeIrpPool = FreeIrpPool.Next; countFreeIrpList--; } } if (irp == null) // Expected to fail, with some diags. throw new ApplicationException("Free Irp pool exhausted"); irp.Next = irp.Prev = null; irp.State = IrpState.Free; return irp; }
private PostWriteReturnCode PostWrite(IRP irp) { //return PostWriteReturnCode.FLT_POSTOP_FINISHED_PROCESSING; return cache.CacheWriteIRPCompleted(irp); //return PostWriteReturnCode.FLT_POSTOP_FINISHED_PROCESSING; }
private PreWriteReturnCode InvalidPreWrite(IRP irp) { throw new NotImplementedException(String.Format("FlowId {0} PreWrite callback not registered.", FlowId)); }
/// <summary> /// Receives a completed IRP, e.g., a completed read /// </summary> /// <param name="irp"></param> /// <returns></returns> public PostReadReturnCode CacheIRPCompleted(IRP irp) { // lookup if IRP is already cached. Assert if so Dictionary<string, Dictionary<uint, FileCacheElement>> f = null; ulong savedFileOffset = irp.FileOffset; uint savedDataLength = irp.DataLength; uint dataOffset = irp.DataOffset; ulong fileOffset = savedFileOffset; Dictionary<uint, FileCacheElement> b = null; FileCacheElement ce = null; Debug.Assert(irp.IoFlowHeader.MajorFunction == MajorFunction.IRP_MJ_READ); if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Attempting to lock cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); lock (cacheLock) { do { uint blockid = (uint)(fileOffset / ALIGNED_BLOCK_SIZE); if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Locked cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (Cache.TryGetValue(irp.FlowId, out f)) { if (f.TryGetValue(irp.IoFlow.FileName, out b)) { if (b.TryGetValue(blockid, out ce)) { // real hit if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Attempting to lock ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); lock (ce.LockObj) { if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Locked ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (ce.Data != null) { if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Thought we had a miss. Now hit? {1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); //Debug.Assert(0 == 1); } //else //{ ce.UpdateData(irp.GetDataReadOnly(), dataOffset, ALIGNED_BLOCK_SIZE /* copying data */); ce.Dirty = false; if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Waking up all on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); // Monitor.PulseAll(ce.LockObj); // wake up anyone waiting on this object //} } if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: UnLocked ce on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); } } } if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: Cached Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); if (noisyOutput) Console.WriteLine("CacheIRPCompleted {0}: UnLocked cache on Offset={1} Length={2}", Thread.CurrentThread.ManagedThreadId, fileOffset, ALIGNED_BLOCK_SIZE); fileOffset += ALIGNED_BLOCK_SIZE; dataOffset += ALIGNED_BLOCK_SIZE; } while (fileOffset < savedFileOffset + savedDataLength); } return PostReadReturnCode.FLT_POSTOP_FINISHED_PROCESSING; }