예제 #1
0
        /// <summary>
        /// sets the size in Bytes for a flow
        /// </summary>
        /// <param name="flowID"></param>
        /// <param name="sizeBytes"></param>
        public void CacheSetFlowSize(uint flowID, UInt64 sizeBytes)
        {
            FlowSLA sla = null;
            UInt64  oldFlowCacheSize = 0;

            // XXXET: bit of overkill, might need locks per flow
            lock (cacheLock)
            {
                if (false == FlowStats.TryGetValue(flowID, out sla))
                {
                    sla = new FlowSLA(flowID);
                    FlowStats[flowID] = sla;
                    if (noisyOutput)
                    {
                        Console.WriteLine("CacheSetFlowSize flow {0} old NA new {1}", flowID, sizeBytes);
                    }
                }
                else
                {
                    if (noisyOutput)
                    {
                        Console.WriteLine("CacheSetFlowSize flow {0} old {1} new {2}", flowID, sla.FlowCacheSize, sizeBytes);
                    }
                }
                oldFlowCacheSize  = sla.FlowCacheSize;
                sla.FlowCacheSize = sizeBytes;

                //Reset the cache counters, since hit rate will be different now
                sla.CacheAccessesHits  = 0;
                sla.CacheAccessesTotal = 0;
                // if cache is shkrinking, need to Evict
                Evict(flowID);
            }
            //Shrink the free file cache element size accordingly
            if (oldFlowCacheSize > sla.FlowCacheSize)
            {
                Debug.Assert(sla.FlowCacheSize % this.AlignedBlockSize == 0);
                Int64 blocksToFree = (Int64)((oldFlowCacheSize - sla.FlowCacheSize) / this.AlignedBlockSize);

                cleanUpFreeFileCacheElementList(blocksToFree);
            }
        }
예제 #2
0
        /// <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)
        }
예제 #3
0
        /// <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);
            }
        }
예제 #4
0
        /// <summary>
        /// Evicts an entry from the cache.  Caller must have locked cache
        /// </summary>
        /// <param name="flowid">flow id of IRP that caused eviction (we evict from same flow)</param>
        private void Evict(uint flowid)
        {
            Dictionary <string, Dictionary <uint, FileCacheElement> > f = null;
            FlowSLA          sla = null;
            FileCacheElement ce  = null;
            Dictionary <uint, FileCacheElement> b = null;
            FileCacheElement ce2 = null;

            uint blockid = 0;

            if (false == Cache.TryGetValue(flowid, out f))
            {
                return;
            }
            if (false == FlowStats.TryGetValue(flowid, out sla))
            {
                return;
            }

            int block_pos = 0;

            // check if I need to evict
            while (sla.FlowCacheSizeUsedBytes > sla.FlowCacheSize && sla.cacheEntries.Count > 0)
            {
                int inner = 0;
                foreach (FileCacheElement node in sla.cacheEntries)
                {
                    ce = node;
                    if (inner == block_pos)
                    {
                        break;
                    }
                    inner++;
                }

                {
                    lock (ce.LockObj)
                    {
                        if (ce.Data != null)
                        {
                            // real hit
                            if (noisyOutput)
                            {
                                Console.WriteLine("Evict {0}: Locked ce on Offset={1} Length={2}",
                                                  Thread.CurrentThread.ManagedThreadId, ce.FileOffset, ce.DataLength);
                            }
                            Debug.Assert(ce.DataLength == ALIGNED_BLOCK_SIZE);
                            // remove from lookup dictionary
                            blockid = (uint)(ce.FileOffset / ce.DataLength);

                            if (f.TryGetValue(ce.Flow.FileName, out b))
                            {
                                if (b.TryGetValue(blockid, out ce2))
                                {
                                    b.Remove(blockid);
                                    if (b.TryGetValue(blockid, out ce2))
                                    {
                                        Debug.Assert(0 == 1);
                                    }
                                }
                                else
                                {
                                    Debug.Assert(0 == 1);
                                }
                            }
                            else
                            {
                                Debug.Assert(0 == 1);
                            }


                            // remove from linked list
                            Debug.Assert(ce.nodeInList != null);
                            sla.cacheEntries.Remove(ce.nodeInList);

                            returnFreeFileCacheElement(ce);

                            block_pos           = 0;
                            cacheSizeUsedBytes -= ce.DataLength;
                            Debug.Assert(cacheSizeUsedBytes >= 0);
                            sla.FlowCacheSizeUsedBytes -= ce.DataLength;
                            sla.CacheTotalEvictions++;

                            //Console.WriteLine("EVICT: {0}", ce.FileOffset);

                            Debug.Assert(sla.FlowCacheSizeUsedBytes >= 0);
                        }
                        else
                        {
                            block_pos++;
                        }
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// sets the size in Bytes for a flow 
        /// </summary>
        /// <param name="flowID"></param>
        /// <param name="sizeBytes"></param>
        public void CacheSetFlowSize(uint flowID, UInt64 sizeBytes)
        {
            FlowSLA sla = null;
            UInt64 oldFlowCacheSize= 0;

            // XXXET: bit of overkill, might need locks per flow
            lock (cacheLock)
            {
                if (false == FlowStats.TryGetValue(flowID, out sla))
                {
                    sla = new FlowSLA(flowID);
                    FlowStats[flowID] = sla;
                    if (noisyOutput)
                        Console.WriteLine("CacheSetFlowSize flow {0} old NA new {1}", flowID, sizeBytes);
                }
                else
                {
                    if (noisyOutput)
                        Console.WriteLine("CacheSetFlowSize flow {0} old {1} new {2}", flowID, sla.FlowCacheSize, sizeBytes);
                }
                oldFlowCacheSize = sla.FlowCacheSize;
                sla.FlowCacheSize = sizeBytes;

                //Reset the cache counters, since hit rate will be different now
                sla.CacheAccessesHits = 0; 
                sla.CacheAccessesTotal = 0;
                // if cache is shkrinking, need to Evict
                Evict(flowID);
            }
            //Shrink the free file cache element size accordingly
            if (oldFlowCacheSize > sla.FlowCacheSize)
            {
                Debug.Assert(sla.FlowCacheSize % this.AlignedBlockSize == 0);
                Int64 blocksToFree = (Int64)((oldFlowCacheSize - sla.FlowCacheSize) / this.AlignedBlockSize); 

                cleanUpFreeFileCacheElementList(blocksToFree);
            }
        }