1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxInterruptThreadpoolUm.cpp 8 9 Abstract: 10 11 Threadpool functions for interrupt handling 12 13 Author: 14 15 16 17 18 Environment: 19 20 User mode only 21 22 Revision History: 23 24 --*/ 25 26 #include "fxmin.hpp" 27 #include "FxInterruptThreadpoolUm.hpp" 28 29 extern "C" { 30 #include "FxInterruptThreadpoolUm.tmh" 31 } 32 33 #define STRSAFE_LIB 34 #include <strsafe.h> 35 36 FxInterruptThreadpool::FxInterruptThreadpool( 37 PFX_DRIVER_GLOBALS FxDriverGlobals 38 ) : 39 FxGlobalsStump(FxDriverGlobals), 40 m_Pool(NULL), 41 m_MinimumThreadCount(MINIMUM_THREAD_COUNT_DEFAULT) 42 { 43 InitializeThreadpoolEnvironment(&m_CallbackEnvironment); 44 } 45 46 FxInterruptThreadpool::~FxInterruptThreadpool() 47 { 48 // 49 // close pool 50 // 51 if (m_Pool != NULL) { 52 CloseThreadpool(m_Pool); 53 m_Pool = NULL; 54 } 55 56 DestroyThreadpoolEnvironment(&m_CallbackEnvironment); 57 } 58 59 HRESULT 60 FxInterruptThreadpool::_CreateAndInit( 61 _In_ PFX_DRIVER_GLOBALS DriverGlobals, 62 _Out_ FxInterruptThreadpool** ppThreadpool 63 ) 64 { 65 HRESULT hr; 66 FxInterruptThreadpool* pool = NULL; 67 68 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NOT_NULL(ppThreadpool), 69 DriverGlobals->Public.DriverName); 70 71 *ppThreadpool = NULL; 72 73 pool = new (DriverGlobals) FxInterruptThreadpool(DriverGlobals); 74 if (pool == NULL) 75 { 76 hr = ERROR_NOT_ENOUGH_MEMORY; 77 DoTraceLevelMessage(DriverGlobals, 78 TRACE_LEVEL_ERROR, TRACINGPNP, 79 "FxInterruptThreadpool creation failed, " 80 "%!hresult!", hr); 81 return hr; 82 } 83 84 hr = pool->Initialize(); 85 86 if (SUCCEEDED(hr)) 87 { 88 *ppThreadpool = pool; 89 } 90 else { 91 delete pool; 92 } 93 94 return hr; 95 } 96 97 HRESULT 98 FxInterruptThreadpool::Initialize( 99 ) 100 { 101 HRESULT hr = S_OK; 102 DWORD error; 103 BOOL bRet; 104 105 // 106 // Create a thread pool using win32 APIs 107 // 108 m_Pool = CreateThreadpool(NULL); 109 if (m_Pool == NULL) 110 { 111 error = GetLastError(); 112 hr = HRESULT_FROM_WIN32(error); 113 DoTraceLevelMessage(GetDriverGlobals(), 114 TRACE_LEVEL_ERROR, TRACINGPNP, 115 "Threadpool creation failed, %!winerr!", error); 116 return hr; 117 } 118 119 // 120 // Set maximum thread count to equal the total number of interrupts. 121 // Set minimum thread count (persistent threads) to equal the lower of 122 // number of interrupt and number of processors. 123 // 124 // We want minimum number of persistent threads to be at least equal to the 125 // number of interrupt objects so that there is no delay in servicing the 126 // interrupt if there are processors available (the delay can come due to 127 // thread pool delay in allocating and initializing a new thread). However, 128 // there is not much benefit in having more persistent threads than 129 // processors, as threads would have to wait for processor to become 130 // available anyways. Therefore, we chose to have the number of persistent 131 // equal to the Min(number of interrupts, number of processors). 132 // 133 // In the current design, if there are more interrupts than 134 // processors, as soon as one thread finishes servicing the interrupt, both 135 // the thread and processor will be available to service the next queued 136 // interrupt. Note that thread pool will queue all the waits and will 137 // satisfy them in a FIFO manner. 138 // 139 // Since we don't know the number of interrupts in the beginning, we will 140 // start with one and update it when we know the actual number of interrupts 141 // after processing driver's OnPrepareHardware callback. 142 // 143 // Note on interrupt servicing: 144 // When fx connects the interrupt, it queues an event-based wait block 145 // to thread pool for each interrupt, so that the interrupt can get serviced 146 // using one of the thread pool threads when the auto-reset event shared 147 // with reflector is set by reflector in the DpcForIsr routine queued by Isr. 148 // While the interrupt is being serviced by one of the threads, no wait block 149 // is queued to thread pool for that interrupt, so arrival of same interrupt 150 // signals the event in DpcForIsr but doesn't cause new threads to pick up 151 // the servicing. Note that other interrupts still have their wait blocks 152 // queued so they will be serviced as they arrive. 153 // 154 // When previous servicing is over (i.e. the ISR callback has been 155 // invoked and it has returned), Fx queues another wait block to thread pool 156 // for that interrupt. If the event is already signalled, it would result in 157 // the same thread (or another) picking up servicing immediately. 158 // 159 // This means the interrupt ISR routine can never run concurrently 160 // for same interrupt. Therefore, there is no need to have more than one 161 // thread for each interrupt. 162 // 163 SetThreadpoolThreadMaximum(m_Pool, 1); 164 165 // 166 // Create one persistent thread since atleast one interrupt is the most 167 // likely scenario. We will update this number to have either the same 168 // number of threads as there are interrupts, or the number of 169 // processors on the machine. 170 // 171 bRet = SetThreadpoolThreadMinimum(m_Pool, m_MinimumThreadCount); 172 if (bRet == FALSE) { 173 error = GetLastError(); 174 hr = HRESULT_FROM_WIN32(error); 175 DoTraceLevelMessage(GetDriverGlobals(), 176 TRACE_LEVEL_ERROR, TRACINGPNP, 177 "%!FUNC!: Failed to set minimum threads (%d) in threadpool," 178 " %!winerr!", m_MinimumThreadCount, error); 179 goto cleanup; 180 } 181 182 // 183 // Associate thread pool with callback environment. 184 // 185 SetThreadpoolCallbackPool(&m_CallbackEnvironment, m_Pool); 186 187 cleanup: 188 189 if (FAILED(hr)) { 190 CloseThreadpool(m_Pool); 191 m_Pool = NULL; 192 } 193 194 return hr; 195 } 196 197 HRESULT 198 FxInterruptThreadpool::UpdateThreadPoolThreadLimits( 199 _In_ ULONG InterruptCount 200 ) 201 { 202 BOOL bRet; 203 HRESULT hr = S_OK; 204 SYSTEM_INFO sysInfo; 205 ULONG minThreadCount = 0; 206 ULONG procs; 207 DWORD error; 208 209 FX_VERIFY(INTERNAL, CHECK_NOT_NULL(m_Pool)); 210 211 // 212 // if there are more than one interrupts then we need to update minimum 213 // thread count. 214 // 215 if (m_MinimumThreadCount >= InterruptCount) { 216 // 217 // nothing to do 218 // 219 return S_OK; 220 } 221 222 // 223 // We want to have number of minimum persistent threads 224 // = Min(number of interrupts, number of processors). 225 // See comments in Initialize routine for details. 226 // 227 GetSystemInfo(&sysInfo); 228 procs = sysInfo.dwNumberOfProcessors; 229 230 minThreadCount = min(InterruptCount, procs); 231 232 if (m_MinimumThreadCount < minThreadCount) { 233 // 234 // Set threadpool min 235 // 236 bRet = SetThreadpoolThreadMinimum(m_Pool, minThreadCount); 237 if (bRet == FALSE) { 238 error = GetLastError(); 239 hr = HRESULT_FROM_WIN32(error); 240 DoTraceLevelMessage(GetDriverGlobals(), 241 TRACE_LEVEL_ERROR, TRACINGPNP, 242 "Failed to set minimum threads in threadpool," 243 " TP_POOL 0x%p to %d %!winerr!", m_Pool, minThreadCount, 244 error); 245 return hr; 246 } 247 248 m_MinimumThreadCount = minThreadCount; 249 } 250 251 // 252 // set thread pool max to max number of interrupts 253 // 254 SetThreadpoolThreadMaximum(m_Pool, InterruptCount); 255 256 DoTraceLevelMessage(GetDriverGlobals(), 257 TRACE_LEVEL_ERROR, TRACINGPNP, 258 "Threads in thread pool TP_POOL 0x%p updated" 259 " to Max %d Min %d threads", m_Pool, 260 InterruptCount, minThreadCount); 261 262 return hr; 263 } 264 265 FxInterruptWaitblock::~FxInterruptWaitblock( 266 VOID 267 ) 268 { 269 // 270 // close the thread pool wait structure 271 // 272 if (m_Wait) { 273 // 274 // Make sure no event is registered. 275 // 276 ClearThreadpoolWait(); 277 278 // 279 // Wait for all the callbacks to finish. 280 // 281 WaitForOutstandingCallbackToComplete(); 282 283 // 284 // close the wait 285 // 286 CloseThreadpoolWait(); 287 288 m_Wait = NULL; 289 } 290 291 // 292 // close event handle 293 // 294 if (m_Event) { 295 CloseHandle(m_Event); 296 m_Event = NULL; 297 } 298 } 299 300 HRESULT 301 FxInterruptWaitblock::_CreateAndInit( 302 _In_ FxInterruptThreadpool* Threadpool, 303 _In_ FxInterrupt* Interrupt, 304 _In_ PTP_WAIT_CALLBACK WaitCallback, 305 _Out_ FxInterruptWaitblock** Waitblock 306 ) 307 { 308 HRESULT hr = S_OK; 309 FxInterruptWaitblock* waitblock = NULL; 310 PFX_DRIVER_GLOBALS driverGlobals; 311 312 FX_VERIFY(INTERNAL, CHECK_NOT_NULL(Waitblock)); 313 *Waitblock = NULL; 314 driverGlobals = Interrupt->GetDriverGlobals(); 315 316 // 317 // create an instance of interrupt wait block 318 // 319 waitblock = new (driverGlobals) FxInterruptWaitblock(driverGlobals); 320 if (waitblock == NULL) { 321 hr = E_OUTOFMEMORY; 322 DoTraceLevelMessage(driverGlobals, 323 TRACE_LEVEL_ERROR, TRACINGPNP, 324 "Waitblock creation failed %!hresult!", hr); 325 goto exit; 326 } 327 328 hr = waitblock->Initialize(Threadpool, 329 Interrupt, 330 WaitCallback); 331 if (SUCCEEDED(hr)) { 332 *Waitblock = waitblock; 333 } 334 else { 335 DoTraceLevelMessage(driverGlobals, 336 TRACE_LEVEL_ERROR, TRACINGPNP, 337 "Waitblock init failed %!hresult!", hr); 338 } 339 340 exit: 341 342 if(FAILED(hr) && waitblock != NULL) { 343 delete waitblock; 344 } 345 346 return hr; 347 } 348 349 HRESULT 350 FxInterruptWaitblock::Initialize( 351 __in FxInterruptThreadpool* Threadpool, 352 __in FxInterrupt* Interrupt, 353 __in PTP_WAIT_CALLBACK WaitCallback 354 ) 355 { 356 HRESULT hr = S_OK; 357 DWORD error; 358 359 // 360 // create a per-interrupt auto-reset event, non-signalled to begin with. 361 // 362 m_Event = CreateEvent( 363 NULL, // LPSECURITY_ATTRIBUTES lpEventAttributes, 364 FALSE, // BOOL bManualReset, 365 FALSE, // BOOL bInitialState, 366 NULL // LPCTSTR lpName 367 ); 368 369 if (m_Event == NULL) { 370 error = GetLastError(); 371 hr = HRESULT_FROM_WIN32(error); 372 DoTraceLevelMessage(GetDriverGlobals(), 373 TRACE_LEVEL_ERROR, TRACINGPNP, 374 "Event creation failed for FxInterrupt object" 375 " %!winerr!", error); 376 goto exit; 377 } 378 379 // 380 // create a per-interrupt thread pool wait structure. This wait structure is 381 // needed to associate an event-based wait callback with threadpool. 382 // 383 m_Wait = Threadpool->CreateThreadpoolWait(WaitCallback, 384 Interrupt); 385 if (m_Wait == NULL) { 386 error = GetLastError(); 387 hr = HRESULT_FROM_WIN32(error); 388 DoTraceLevelMessage(GetDriverGlobals(), 389 TRACE_LEVEL_ERROR, TRACINGPNP, 390 "Event creation failed for FxInterrupt object" 391 " %!winerr!", error); 392 goto exit; 393 } 394 395 exit: 396 397 return hr; 398 } 399 400 401