/// <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> /// 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; }