]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/DpcDxe/Dpc.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Network / DpcDxe / Dpc.c
1 /** @file
2
3 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5
6 Module Name:
7
8 Dpc.c
9
10 Abstract:
11
12
13 **/
14
15 #include "Dpc.h"
16
17 //
18 // Handle for the EFI_DPC_PROTOCOL instance
19 //
20 EFI_HANDLE mDpcHandle = NULL;
21
22 //
23 // The EFI_DPC_PROTOCOL instances that is installed onto mDpcHandle
24 //
25 EFI_DPC_PROTOCOL mDpc = {
26 DpcQueueDpc,
27 DpcDispatchDpc
28 };
29
30 //
31 // Global variables used to meaasure the DPC Queue Depths
32 //
33 UINTN mDpcQueueDepth = 0;
34 UINTN mMaxDpcQueueDepth = 0;
35
36 //
37 // Free list of DPC entries. As DPCs are queued, entries are removed from this
38 // free list. As DPC entries are dispatched, DPC entries are added to the free list.
39 // If the free list is empty and a DPC is queued, the free list is grown by allocating
40 // an additional set of DPC entries.
41 //
42 LIST_ENTRY mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE(mDpcEntryFreeList);
43
44 //
45 // An array of DPC queues. A DPC queue is allocated for every leval EFI_TPL value.
46 // As DPCs are queued, they are added to the end of the linked list.
47 // As DPCs are dispatched, they are removed from the beginning of the linked list.
48 //
49 LIST_ENTRY mDpcQueue[TPL_HIGH_LEVEL + 1];
50
51 /**
52 Add a Deferred Procedure Call to the end of the DPC queue.
53
54 @param This Protocol instance pointer.
55 @param DpcTpl The EFI_TPL that the DPC should be invoked.
56 @param DpcProcedure Pointer to the DPC's function.
57 @param DpcContext Pointer to the DPC's context. Passed to DpcProcedure
58 when DpcProcedure is invoked.
59
60 @retval EFI_SUCCESS The DPC was queued.
61 @retval EFI_INVALID_PARAMETER DpcTpl is not a valid EFI_TPL.
62 @retval EFI_INVALID_PARAMETER DpcProcedure is NULL.
63 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to
64 add the DPC to the queue.
65
66 **/
67 EFI_STATUS
68 EFIAPI
69 DpcQueueDpc (
70 IN EFI_DPC_PROTOCOL *This,
71 IN EFI_TPL DpcTpl,
72 IN EFI_DPC_PROCEDURE DpcProcedure,
73 IN VOID *DpcContext OPTIONAL
74 )
75 {
76 EFI_STATUS ReturnStatus;
77 EFI_TPL OriginalTpl;
78 DPC_ENTRY *DpcEntry;
79 UINTN Index;
80
81 //
82 // Make sure DpcTpl is valid
83 //
84 if (DpcTpl < TPL_APPLICATION || DpcTpl > TPL_HIGH_LEVEL) {
85 return EFI_INVALID_PARAMETER;
86 }
87
88 //
89 // Make sure DpcProcedure is valid
90 //
91 if (DpcProcedure == NULL) {
92 return EFI_INVALID_PARAMETER;
93 }
94
95 //
96 // Assume this function will succeed
97 //
98 ReturnStatus = EFI_SUCCESS;
99
100 //
101 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
102 // current TPL value so it can be restored when this function returns.
103 //
104 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
105
106 //
107 // Check to see if there are any entries in the DPC free list
108 //
109 if (IsListEmpty (&mDpcEntryFreeList)) {
110 //
111 // If the current TPL is greater than TPL_NOTIFY, then memory allocations
112 // can not be performed, so the free list can not be expanded. In this case
113 // return EFI_OUT_OF_RESOURCES.
114 //
115 if (OriginalTpl > TPL_NOTIFY) {
116 ReturnStatus = EFI_OUT_OF_RESOURCES;
117 goto Done;
118 }
119
120 //
121 // Add 64 DPC entries to the free list
122 //
123 for (Index = 0; Index < 64; Index++) {
124 //
125 // Lower the TPL level to perform a memory allocation
126 //
127 gBS->RestoreTPL (OriginalTpl);
128
129 //
130 // Allocate a new DPC entry
131 //
132 DpcEntry = AllocatePool (sizeof (DPC_ENTRY));
133
134 //
135 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
136 //
137 gBS->RaiseTPL (TPL_HIGH_LEVEL);
138
139 //
140 // If the allocation of a DPC entry fails, and the free list is empty,
141 // then return EFI_OUT_OF_RESOURCES.
142 //
143 if (DpcEntry == NULL) {
144 if (IsListEmpty (&mDpcEntryFreeList)) {
145 ReturnStatus = EFI_OUT_OF_RESOURCES;
146 goto Done;
147 }
148 }
149
150 //
151 // Add the newly allocated DPC entry to the DPC free list
152 //
153 InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
154 }
155 }
156
157 //
158 // Retrieve the first node from the free list of DPCs
159 //
160 DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));
161
162 //
163 // Remove the first node from the free list of DPCs
164 //
165 RemoveEntryList (&DpcEntry->ListEntry);
166
167 //
168 // Fill in the DPC entry with the DpcProcedure and DpcContext
169 //
170 DpcEntry->DpcProcedure = DpcProcedure;
171 DpcEntry->DpcContext = DpcContext;
172
173 //
174 // Add the DPC entry to the end of the list for the specified DplTpl.
175 //
176 InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);
177
178 //
179 // Increment the measured DPC queue depth across all TPLs
180 //
181 mDpcQueueDepth++;
182
183 //
184 // Measure the maximum DPC queue depth across all TPLs
185 //
186 if (mDpcQueueDepth > mMaxDpcQueueDepth) {
187 mMaxDpcQueueDepth = mDpcQueueDepth;
188 }
189
190 Done:
191 //
192 // Restore the original TPL level when this function was called
193 //
194 gBS->RestoreTPL (OriginalTpl);
195
196 return ReturnStatus;
197 }
198
199 /**
200 Dispatch the queue of DPCs. ALL DPCs that have been queued with a DpcTpl
201 value greater than or equal to the current TPL are invoked in the order that
202 they were queued. DPCs with higher DpcTpl values are invoked before DPCs with
203 lower DpcTpl values.
204
205 @param This Protocol instance pointer.
206
207 @retval EFI_SUCCESS One or more DPCs were invoked.
208 @retval EFI_NOT_FOUND No DPCs were invoked.
209
210 **/
211 EFI_STATUS
212 EFIAPI
213 DpcDispatchDpc (
214 IN EFI_DPC_PROTOCOL *This
215 )
216 {
217 EFI_STATUS ReturnStatus;
218 EFI_TPL OriginalTpl;
219 EFI_TPL Tpl;
220 DPC_ENTRY *DpcEntry;
221
222 //
223 // Assume that no DPCs will be invoked
224 //
225 ReturnStatus = EFI_NOT_FOUND;
226
227 //
228 // Raise the TPL level to TPL_HIGH_LEVEL for DPC list operation and save the
229 // current TPL value so it can be restored when this function returns.
230 //
231 OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
232
233 //
234 // Check to see if there are 1 or more DPCs currently queued
235 //
236 if (mDpcQueueDepth > 0) {
237 //
238 // Loop from TPL_HIGH_LEVEL down to the current TPL value
239 //
240 for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {
241 //
242 // Check to see if the DPC queue is empty
243 //
244 while (!IsListEmpty (&mDpcQueue[Tpl])) {
245 //
246 // Retrieve the first DPC entry from the DPC queue specified by Tpl
247 //
248 DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));
249
250 //
251 // Remove the first DPC entry from the DPC queue specified by Tpl
252 //
253 RemoveEntryList (&DpcEntry->ListEntry);
254
255 //
256 // Decrement the measured DPC Queue Depth across all TPLs
257 //
258 mDpcQueueDepth--;
259
260 //
261 // Lower the TPL to TPL value of the current DPC queue
262 //
263 gBS->RestoreTPL (Tpl);
264
265 //
266 // Invoke the DPC passing in its context
267 //
268 (DpcEntry->DpcProcedure) (DpcEntry->DpcContext);
269
270 //
271 // At least one DPC has been invoked, so set the return status to EFI_SUCCESS
272 //
273 ReturnStatus = EFI_SUCCESS;
274
275 //
276 // Raise the TPL level back to TPL_HIGH_LEVEL for DPC list operations
277 //
278 gBS->RaiseTPL (TPL_HIGH_LEVEL);
279
280 //
281 // Add the invoked DPC entry to the DPC free list
282 //
283 InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);
284 }
285 }
286 }
287
288 //
289 // Restore the original TPL level when this function was called
290 //
291 gBS->RestoreTPL (OriginalTpl);
292
293 return ReturnStatus;
294 }
295
296 /**
297 The entry point for DPC driver which installs the EFI_DPC_PROTOCOL onto a new handle.
298
299 @param ImageHandle The image handle of the driver.
300 @param SystemTable The system table.
301
302 @retval EFI_SUCCES The DPC queues were initialized and the EFI_DPC_PROTOCOL was
303 installed onto a new handle.
304 @retval Others Failed to install EFI_DPC_PROTOCOL.
305
306 **/
307 EFI_STATUS
308 EFIAPI
309 DpcDriverEntryPoint (
310 IN EFI_HANDLE ImageHandle,
311 IN EFI_SYSTEM_TABLE *SystemTable
312 )
313 {
314 EFI_STATUS Status;
315 UINTN Index;
316
317 //
318 // ASSERT() if the EFI_DPC_PROTOCOL is already present in the handle database
319 //
320 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiDpcProtocolGuid);
321
322 //
323 // Initialize the DPC queue for all possible TPL values
324 //
325 for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {
326 InitializeListHead (&mDpcQueue[Index]);
327 }
328
329 //
330 // Install the EFI_DPC_PROTOCOL instance onto a new handle
331 //
332 Status = gBS->InstallMultipleProtocolInterfaces (
333 &mDpcHandle,
334 &gEfiDpcProtocolGuid,
335 &mDpc,
336 NULL
337 );
338 ASSERT_EFI_ERROR (Status);
339
340 return Status;
341 }