00001 /*++ 00002 00003 Copyright (c) 1989-1994 Microsoft Corporation 00004 00005 Module Name: 00006 00007 apcobj.c 00008 00009 Abstract: 00010 00011 This module implements the kernel APC object. Functions are provided 00012 to initialize, flush, insert, and remove APC objects. 00013 00014 Author: 00015 00016 David N. Cutler (davec) 5-Mar-1989 00017 00018 Environment: 00019 00020 Kernel mode only. 00021 00022 Revision History: 00023 00024 --*/ 00025 00026 #include "ki.h" 00027 00028 // 00029 // The following assert macro is used to check that an input apc is 00030 // really a kapc and not something else, like deallocated pool. 00031 // 00032 00033 #define ASSERT_APC(E) { \ 00034 ASSERT((E)->Type == ApcObject); \ 00035 } 00036 00037 00038 VOID 00039 KeInitializeApc ( 00040 IN PRKAPC Apc, 00041 IN PRKTHREAD Thread, 00042 IN KAPC_ENVIRONMENT Environment, 00043 IN PKKERNEL_ROUTINE KernelRoutine, 00044 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, 00045 IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL, 00046 IN KPROCESSOR_MODE ApcMode OPTIONAL, 00047 IN PVOID NormalContext OPTIONAL 00048 ) 00049 00050 /*++ 00051 00052 Routine Description: 00053 00054 This function initializes a kernel APC object. The thread, kernel 00055 routine, and optionally a normal routine, processor mode, and normal 00056 context parameter are stored in the APC object. 00057 00058 Arguments: 00059 00060 Apc - Supplies a pointer to a control object of type APC. 00061 00062 Thread - Supplies a pointer to a dispatcher object of type thread. 00063 00064 Environment - Supplies the environment in which the APC will execute. 00065 Valid values for this parameter are: OriginalApcEnvironment, 00066 AttachedApcEnvironment, or CurrentApcEnvironment. 00067 00068 KernelRoutine - Supplies a pointer to a function that is to be 00069 executed at IRQL APC_LEVEL in kernel mode. 00070 00071 RundownRoutine - Supplies an optional pointer to a function that is to be 00072 called if the APC is in a thread's APC queue when the thread terminates. 00073 00074 NormalRoutine - Supplies an optional pointer to a function that is 00075 to be executed at IRQL 0 in the specified processor mode. If this 00076 parameter is not specified, then the ProcessorMode and NormalContext 00077 parameters are ignored. 00078 00079 ApcMode - Supplies the processor mode in which the function specified 00080 by the NormalRoutine parameter is to be executed. 00081 00082 NormalContext - Supplies a pointer to an arbitrary data structure which is 00083 to be passed to the function specified by the NormalRoutine parameter. 00084 00085 Return Value: 00086 00087 None. 00088 00089 --*/ 00090 00091 { 00092 00093 ASSERT(Environment <= CurrentApcEnvironment); 00094 00095 // 00096 // Initialize standard control object header. 00097 // 00098 00099 Apc->Type = ApcObject; 00100 Apc->Size = sizeof(KAPC); 00101 00102 // 00103 // Initialize the APC environment, thread address, kernel routine address, 00104 // rundown routine address, normal routine address, processor mode, and 00105 // normal context parameter. If the normal routine address is null, then 00106 // the processor mode is defaulted to KernelMode and the APC is a special 00107 // APC. Otherwise, the processor mode is taken from the argument list. 00108 // 00109 00110 if (Environment == CurrentApcEnvironment) { 00111 Apc->ApcStateIndex = Thread->ApcStateIndex; 00112 00113 } else { 00114 00115 ASSERT(Environment <= Thread->ApcStateIndex); 00116 00117 Apc->ApcStateIndex = (CCHAR)Environment; 00118 } 00119 00120 Apc->Thread = Thread; 00121 Apc->KernelRoutine = KernelRoutine; 00122 Apc->RundownRoutine = RundownRoutine; 00123 Apc->NormalRoutine = NormalRoutine; 00124 if (ARGUMENT_PRESENT(NormalRoutine)) { 00125 Apc->ApcMode = ApcMode; 00126 Apc->NormalContext = NormalContext; 00127 00128 } else { 00129 Apc->ApcMode = KernelMode; 00130 Apc->NormalContext = NIL; 00131 } 00132 00133 Apc->Inserted = FALSE; 00134 return; 00135 } 00136 00137 PLIST_ENTRY 00138 KeFlushQueueApc ( 00139 IN PKTHREAD Thread, 00140 IN KPROCESSOR_MODE ApcMode 00141 ) 00142 00143 /*++ 00144 00145 Routine Description: 00146 00147 This function flushes the APC queue selected by the specified processor 00148 mode for the specified thread. An APC queue is flushed by removing the 00149 listhead from the list, scanning the APC entries in the list, setting 00150 their inserted variables to FALSE, and then returning the address of the 00151 doubly linked list as the function value. 00152 00153 Arguments: 00154 00155 Thread - Supplies a pointer to a dispatcher object of type thread. 00156 00157 ApcMode - Supplies the processor mode of the APC queue that is to 00158 be flushed. 00159 00160 Return Value: 00161 00162 The address of the first entry in the list of APC objects that were flushed 00163 from the specified APC queue. 00164 00165 --*/ 00166 00167 { 00168 00169 PKAPC Apc; 00170 PLIST_ENTRY FirstEntry; 00171 PLIST_ENTRY NextEntry; 00172 KIRQL OldIrql; 00173 00174 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); 00175 00176 // 00177 // Raise IRQL to dispatcher level, lock dispatcher database, and 00178 // lock the APC queue. 00179 // 00180 00181 KiLockDispatcherDatabase(&OldIrql); 00182 KiAcquireSpinLock(&Thread->ApcQueueLock); 00183 00184 // 00185 // Get address of first APC in the list and check if the list is 00186 // empty or contains entries that should be flushed. If entries 00187 // should be flushed, then scan the list of APC objects and set their 00188 // inserted state to FALSE. 00189 // 00190 00191 FirstEntry = Thread->ApcState.ApcListHead[ApcMode].Flink; 00192 if (FirstEntry == &Thread->ApcState.ApcListHead[ApcMode]) { 00193 FirstEntry = (PLIST_ENTRY)NULL; 00194 00195 } else { 00196 RemoveEntryList(&Thread->ApcState.ApcListHead[ApcMode]); 00197 NextEntry = FirstEntry; 00198 do { 00199 Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry); 00200 Apc->Inserted = FALSE; 00201 NextEntry = NextEntry->Flink; 00202 } while (NextEntry != FirstEntry); 00203 } 00204 00205 // 00206 // Unlock the APC queue, unlock the dispatcher database, lower IRQL to 00207 // its previous value, and return address of first entry in list of APC 00208 // objects that were flushed. 00209 // 00210 00211 KiReleaseSpinLock(&Thread->ApcQueueLock); 00212 KiUnlockDispatcherDatabase(OldIrql); 00213 return FirstEntry; 00214 } 00215 00216 BOOLEAN 00217 KeInsertQueueApc ( 00218 IN PRKAPC Apc, 00219 IN PVOID SystemArgument1, 00220 IN PVOID SystemArgument2, 00221 IN KPRIORITY Increment 00222 ) 00223 00224 /*++ 00225 00226 Routine Description: 00227 00228 This function inserts an APC object into the APC queue specifed by the 00229 thread and processor mode fields of the APC object. If the APC object 00230 is already in an APC queue or APC queuing is disabled, then no operation 00231 is performed. Otherwise the APC object is inserted in the specified queue 00232 and appropriate scheduling decisions are made. 00233 00234 Arguments: 00235 00236 Apc - Supplies a pointer to a control object of type APC. 00237 00238 SystemArgument1, SystemArgument2 - Supply a set of two arguments that 00239 contain untyped data provided by the executive. 00240 00241 Increment - Supplies the priority increment that is to be applied if 00242 queuing the APC causes a thread wait to be satisfied. 00243 00244 Return Value: 00245 00246 If the APC object is already in an APC queue or APC queuing is disabled, 00247 then a value of FALSE is returned. Otherwise a value of TRUE is returned. 00248 00249 --*/ 00250 00251 { 00252 00253 BOOLEAN Inserted; 00254 KIRQL OldIrql; 00255 PRKTHREAD Thread; 00256 00257 ASSERT_APC(Apc); 00258 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); 00259 00260 // 00261 // Raise IRQL to dispatcher level and lock dispatcher database. 00262 // 00263 00264 KiLockDispatcherDatabase(&OldIrql); 00265 00266 // 00267 // If APC queuing is disabled, then set inserted to FALSE. Else save 00268 // system parameter values in APC object, and attempt to queue APC. 00269 // 00270 00271 Thread = Apc->Thread; 00272 if (Thread->ApcQueueable == FALSE) { 00273 Inserted = FALSE; 00274 00275 } else { 00276 Apc->SystemArgument1 = SystemArgument1; 00277 Apc->SystemArgument2 = SystemArgument2; 00278 Inserted = KiInsertQueueApc(Apc, Increment); 00279 } 00280 00281 // 00282 // Unlock the dispatcher database, lower IRQL to its previous value, 00283 // and return whether APC object was inserted in APC queue. 00284 // 00285 00286 KiUnlockDispatcherDatabase(OldIrql); 00287 return Inserted; 00288 } 00289 00290 BOOLEAN 00291 KeRemoveQueueApc ( 00292 IN PKAPC Apc 00293 ) 00294 00295 /*++ 00296 00297 Routine Description: 00298 00299 This function removes an APC object from an APC queue. If the APC object 00300 is not in an APC queue, then no operation is performed. Otherwise the 00301 APC object is removed from its current queue and its inserted state is 00302 set FALSE. 00303 00304 Arguments: 00305 00306 Apc - Supplies a pointer to a control object of type APC. 00307 00308 Return Value: 00309 00310 If the APC object is not in an APC queue, then a value of FALSE is returned. 00311 Otherwise a value of TRUE is returned. 00312 00313 --*/ 00314 00315 { 00316 00317 PKAPC_STATE ApcState; 00318 BOOLEAN Inserted; 00319 KIRQL OldIrql; 00320 PRKTHREAD Thread; 00321 00322 ASSERT_APC(Apc); 00323 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); 00324 00325 // 00326 // Raise IRQL to dispatcher level, lock dispatcher database, and 00327 // lock the APC queue. 00328 // 00329 00330 Thread = Apc->Thread; 00331 KiLockDispatcherDatabase(&OldIrql); 00332 KiAcquireSpinLock(&Thread->ApcQueueLock); 00333 00334 // 00335 // If the APC object is in an APC queue, then remove it from the queue 00336 // and set its inserted state to FALSE. If the queue becomes empty, set 00337 // the APC pending state to FALSE. 00338 // 00339 00340 Inserted = Apc->Inserted; 00341 if (Inserted != FALSE) { 00342 Apc->Inserted = FALSE; 00343 ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex]; 00344 RemoveEntryList(&Apc->ApcListEntry); 00345 if (IsListEmpty(&ApcState->ApcListHead[Apc->ApcMode]) != FALSE) { 00346 if (Apc->ApcMode == KernelMode) { 00347 ApcState->KernelApcPending = FALSE; 00348 00349 } else { 00350 ApcState->UserApcPending = FALSE; 00351 } 00352 } 00353 } 00354 00355 // 00356 // Unlock the APC queue, unlock the dispatcher database, lower IRQL to 00357 // its previous value, and return whether an APC object was removed from 00358 // the APC queue. 00359 // 00360 00361 KiReleaseSpinLock(&Thread->ApcQueueLock); 00362 KiUnlockDispatcherDatabase(OldIrql); 00363 return Inserted; 00364 } 00365
1.3.7