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
FxInterruptThreadpool(PFX_DRIVER_GLOBALS FxDriverGlobals)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
~FxInterruptThreadpool()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
_CreateAndInit(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_Out_ FxInterruptThreadpool ** ppThreadpool)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
Initialize()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
UpdateThreadPoolThreadLimits(_In_ ULONG InterruptCount)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
~FxInterruptWaitblock(VOID)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
_CreateAndInit(_In_ FxInterruptThreadpool * Threadpool,_In_ FxInterrupt * Interrupt,_In_ PTP_WAIT_CALLBACK WaitCallback,_Out_ FxInterruptWaitblock ** Waitblock)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
Initialize(__in FxInterruptThreadpool * Threadpool,__in FxInterrupt * Interrupt,__in PTP_WAIT_CALLBACK WaitCallback)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