Beispiel #1
0
        /// <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();
            }
        }
        /// <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();
        }