xref: /reactos/ntoskrnl/ps/quota.c (revision b22eefac)
1c2c66affSColin Finck /*
2*b22eefacSGeorge Bișoc  * PROJECT:         ReactOS Kernel
3*b22eefacSGeorge Bișoc  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4*b22eefacSGeorge Bișoc  * PURPOSE:         Process Pool Quotas Support
5*b22eefacSGeorge Bișoc  * COPYRIGHT:       Copyright 2005 Alex Ionescu <alex@relsoft.net>
6*b22eefacSGeorge Bișoc  *                  Copyright 2007 Mike Nordell
7*b22eefacSGeorge Bișoc  *                  Copyright 2021 George Bișoc <george.bisoc@reactos.org>
8c2c66affSColin Finck  */
9c2c66affSColin Finck 
10c2c66affSColin Finck /* INCLUDES **************************************************************/
11c2c66affSColin Finck 
12c2c66affSColin Finck #include <ntoskrnl.h>
13c2c66affSColin Finck #define NDEBUG
14c2c66affSColin Finck #include <debug.h>
15c2c66affSColin Finck 
16c2c66affSColin Finck EPROCESS_QUOTA_BLOCK PspDefaultQuotaBlock;
17c2c66affSColin Finck static LIST_ENTRY PspQuotaBlockList = {&PspQuotaBlockList, &PspQuotaBlockList};
18c2c66affSColin Finck static KSPIN_LOCK PspQuotaLock;
19c2c66affSColin Finck 
20c2c66affSColin Finck #define VALID_QUOTA_FLAGS (QUOTA_LIMITS_HARDWS_MIN_ENABLE | \
21c2c66affSColin Finck                            QUOTA_LIMITS_HARDWS_MIN_DISABLE | \
22c2c66affSColin Finck                            QUOTA_LIMITS_HARDWS_MAX_ENABLE | \
23c2c66affSColin Finck                            QUOTA_LIMITS_HARDWS_MAX_DISABLE)
24c2c66affSColin Finck 
25c2c66affSColin Finck /* PRIVATE FUNCTIONS *******************************************************/
26c2c66affSColin Finck 
27*b22eefacSGeorge Bișoc /**
28*b22eefacSGeorge Bișoc  * @brief
29*b22eefacSGeorge Bișoc  * Returns pool quotas back to the Memory Manager
30*b22eefacSGeorge Bișoc  * when the pool quota block is no longer being
31*b22eefacSGeorge Bișoc  * used by anybody.
32*b22eefacSGeorge Bișoc  *
33*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
34*b22eefacSGeorge Bișoc  * The pool quota block of which quota resources
35*b22eefacSGeorge Bișoc  * are to be sent back.
36*b22eefacSGeorge Bișoc  *
37*b22eefacSGeorge Bișoc  * @return
38*b22eefacSGeorge Bișoc  * Nothing.
39*b22eefacSGeorge Bișoc  *
40*b22eefacSGeorge Bișoc  * @remarks
41*b22eefacSGeorge Bișoc  * The function only returns quotas back to Memory
42*b22eefacSGeorge Bișoc  * Manager that is paged or non paged. It does not
43*b22eefacSGeorge Bișoc  * return page file quotas as page file quota
44*b22eefacSGeorge Bișoc  * management is done in a different way. Furthermore,
45*b22eefacSGeorge Bișoc  * quota spin lock has to be held when returning quotas.
46*b22eefacSGeorge Bișoc  */
_Requires_lock_held_(PspQuotaLock)47*b22eefacSGeorge Bișoc _Requires_lock_held_(PspQuotaLock)
48*b22eefacSGeorge Bișoc VOID
49*b22eefacSGeorge Bișoc NTAPI
50*b22eefacSGeorge Bișoc PspReturnQuotasOnDestroy(
51*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
52*b22eefacSGeorge Bișoc {
53*b22eefacSGeorge Bișoc     ULONG PsQuotaTypeIndex;
54*b22eefacSGeorge Bișoc     SIZE_T QuotaToReturn;
55*b22eefacSGeorge Bișoc 
56c2c66affSColin Finck     /*
57*b22eefacSGeorge Bișoc      * We must be in a dispatch level interrupt here
58*b22eefacSGeorge Bișoc      * as we should be under a spin lock.
59*b22eefacSGeorge Bișoc      */
60*b22eefacSGeorge Bișoc     ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
61*b22eefacSGeorge Bișoc 
62*b22eefacSGeorge Bișoc     /* Make sure that the quota block is not plain garbage */
63*b22eefacSGeorge Bișoc     ASSERT(QuotaBlock);
64*b22eefacSGeorge Bișoc 
65*b22eefacSGeorge Bișoc     /* Loop over the Process quota types */
66*b22eefacSGeorge Bișoc     for (PsQuotaTypeIndex = PsNonPagedPool; PsQuotaTypeIndex < PsPageFile; PsQuotaTypeIndex++)
67*b22eefacSGeorge Bișoc     {
68*b22eefacSGeorge Bișoc         /* The amount needed to return to Mm is the limit and return fields */
69*b22eefacSGeorge Bișoc         QuotaToReturn = QuotaBlock->QuotaEntry[PsQuotaTypeIndex].Limit + QuotaBlock->QuotaEntry[PsQuotaTypeIndex].Return;
70*b22eefacSGeorge Bișoc         MmReturnPoolQuota(PsQuotaTypeIndex, QuotaToReturn);
71*b22eefacSGeorge Bișoc     }
72*b22eefacSGeorge Bișoc }
73*b22eefacSGeorge Bișoc 
74*b22eefacSGeorge Bișoc /**
75*b22eefacSGeorge Bișoc  * @brief
76*b22eefacSGeorge Bișoc  * Releases some of excess quotas in order to attempt
77*b22eefacSGeorge Bișoc  * free up some resources. This is done primarily in
78*b22eefacSGeorge Bișoc  * in case the Memory Manager fails to raise the quota
79*b22eefacSGeorge Bișoc  * limit.
80*b22eefacSGeorge Bișoc  *
81*b22eefacSGeorge Bișoc  * @param[in] QuotaType
82*b22eefacSGeorge Bișoc  * Process pool quota type.
83*b22eefacSGeorge Bișoc  *
84*b22eefacSGeorge Bișoc  * @param[out] ReturnedQuotas
85*b22eefacSGeorge Bișoc  * A pointer to the returned amount of quotas
86*b22eefacSGeorge Bișoc  * back to Memory Manager.
87*b22eefacSGeorge Bișoc  *
88*b22eefacSGeorge Bișoc  * @return
89*b22eefacSGeorge Bișoc  * Nothing.
90*b22eefacSGeorge Bișoc  *
91*b22eefacSGeorge Bișoc  * @remarks
92*b22eefacSGeorge Bișoc  * The function releases excess paged or non
93*b22eefacSGeorge Bișoc  * paged pool quotas. Page file quota type is
94*b22eefacSGeorge Bișoc  * not permitted. Furthermore, quota spin lock
95*b22eefacSGeorge Bișoc  * has to be held when returning quotas.
96*b22eefacSGeorge Bișoc  */
_Requires_lock_held_(PspQuotaLock)97*b22eefacSGeorge Bișoc _Requires_lock_held_(PspQuotaLock)
98*b22eefacSGeorge Bișoc VOID
99*b22eefacSGeorge Bișoc NTAPI
100*b22eefacSGeorge Bișoc PspReturnExcessQuotas(
101*b22eefacSGeorge Bișoc     _In_ PS_QUOTA_TYPE QuotaType,
102*b22eefacSGeorge Bișoc     _Outptr_ PSIZE_T ReturnedQuotas)
103*b22eefacSGeorge Bișoc {
104*b22eefacSGeorge Bișoc     PLIST_ENTRY PspQuotaList;
105*b22eefacSGeorge Bișoc     PEPROCESS_QUOTA_BLOCK QuotaBlockFromList;
106*b22eefacSGeorge Bișoc     SIZE_T AmountToReturn = 0;
107*b22eefacSGeorge Bișoc 
108*b22eefacSGeorge Bișoc     /*
109*b22eefacSGeorge Bișoc      * We must be in a dispatch level interrupt here
110*b22eefacSGeorge Bișoc      * as we should be under a spin lock.
111*b22eefacSGeorge Bișoc      */
112*b22eefacSGeorge Bișoc     ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
113*b22eefacSGeorge Bișoc 
114*b22eefacSGeorge Bișoc     /*
115*b22eefacSGeorge Bișoc      * Loop over the quota block lists and reap
116*b22eefacSGeorge Bișoc      * whatever quotas we haven't returned which
117*b22eefacSGeorge Bișoc      * is needed to free up resources.
118*b22eefacSGeorge Bișoc      */
119*b22eefacSGeorge Bișoc     for (PspQuotaList = PspQuotaBlockList.Flink;
120*b22eefacSGeorge Bișoc          PspQuotaList != &PspQuotaBlockList;
121*b22eefacSGeorge Bișoc          PspQuotaList = PspQuotaList->Flink)
122*b22eefacSGeorge Bișoc     {
123*b22eefacSGeorge Bișoc         /* Gather the quota block from the list */
124*b22eefacSGeorge Bișoc         QuotaBlockFromList = CONTAINING_RECORD(PspQuotaList, EPROCESS_QUOTA_BLOCK, QuotaList);
125*b22eefacSGeorge Bișoc 
126*b22eefacSGeorge Bișoc         /*
127*b22eefacSGeorge Bișoc          * Gather any unreturned quotas and cache
128*b22eefacSGeorge Bișoc          * them to a variable.
129*b22eefacSGeorge Bișoc          */
130*b22eefacSGeorge Bișoc         AmountToReturn += InterlockedExchangeSizeT(&QuotaBlockFromList->QuotaEntry[QuotaType].Return, 0);
131*b22eefacSGeorge Bișoc 
132*b22eefacSGeorge Bișoc         /*
133*b22eefacSGeorge Bișoc          * If no other process is taking use of this
134*b22eefacSGeorge Bișoc          * block, then it means that this block has
135*b22eefacSGeorge Bișoc          * only shared pool quota and the last process
136*b22eefacSGeorge Bișoc          * no longer uses this block. If the limit is
137*b22eefacSGeorge Bișoc          * grater than the usage then trim the limit
138*b22eefacSGeorge Bișoc          * and use that as additional amount of quota
139*b22eefacSGeorge Bișoc          * to return.
140*b22eefacSGeorge Bișoc          */
141*b22eefacSGeorge Bișoc         if (QuotaBlockFromList->ProcessCount == 0)
142*b22eefacSGeorge Bișoc         {
143*b22eefacSGeorge Bișoc             if (QuotaBlockFromList->QuotaEntry[QuotaType].Usage <
144*b22eefacSGeorge Bișoc                 QuotaBlockFromList->QuotaEntry[QuotaType].Limit)
145*b22eefacSGeorge Bișoc             {
146*b22eefacSGeorge Bișoc                 InterlockedExchangeSizeT(&QuotaBlockFromList->QuotaEntry[QuotaType].Limit,
147*b22eefacSGeorge Bișoc                                          QuotaBlockFromList->QuotaEntry[QuotaType].Usage);
148*b22eefacSGeorge Bișoc                 AmountToReturn += QuotaBlockFromList->QuotaEntry[QuotaType].Limit;
149*b22eefacSGeorge Bișoc             }
150*b22eefacSGeorge Bișoc         }
151*b22eefacSGeorge Bișoc     }
152*b22eefacSGeorge Bișoc 
153*b22eefacSGeorge Bișoc     /* Invoke Mm to return quotas */
154*b22eefacSGeorge Bișoc     DPRINT("PspReturnExcessQuotas(): Amount of quota released -- %lu\n", AmountToReturn);
155*b22eefacSGeorge Bișoc     MmReturnPoolQuota(QuotaType, AmountToReturn);
156*b22eefacSGeorge Bișoc     *ReturnedQuotas = AmountToReturn;
157*b22eefacSGeorge Bișoc }
158*b22eefacSGeorge Bișoc 
159*b22eefacSGeorge Bișoc /**
160*b22eefacSGeorge Bișoc  * @brief
161*b22eefacSGeorge Bișoc  * Internal kernel function that provides the
162*b22eefacSGeorge Bișoc  * bulk logic of process quota charging,
163*b22eefacSGeorge Bișoc  * necessary for exported kernel routines
164*b22eefacSGeorge Bișoc  * needed for quota management.
165*b22eefacSGeorge Bișoc  *
166*b22eefacSGeorge Bișoc  * @param[in] Process
167*b22eefacSGeorge Bișoc  * A process, represented as a EPROCESS object.
168*b22eefacSGeorge Bișoc  * This parameter is used to charge the own
169*b22eefacSGeorge Bișoc  * process' quota usage.
170*b22eefacSGeorge Bișoc  *
171*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
172*b22eefacSGeorge Bișoc  * The quota block which quotas are to be charged.
173*b22eefacSGeorge Bișoc  * This block can either come from the process itself
174*b22eefacSGeorge Bișoc  * or from an object with specified quota charges.
175*b22eefacSGeorge Bișoc  *
176*b22eefacSGeorge Bișoc  * @param[in] QuotaType
177*b22eefacSGeorge Bișoc  * The quota type which quota in question is to
178*b22eefacSGeorge Bișoc  * be charged. The permitted types are PsPagedPool,
179*b22eefacSGeorge Bișoc  * PsNonPagedPool and PsPageFile.
180*b22eefacSGeorge Bișoc  *
181*b22eefacSGeorge Bișoc  * @param[in] Amount
182*b22eefacSGeorge Bișoc  * The amount of quota to be charged.
183*b22eefacSGeorge Bișoc  *
184*b22eefacSGeorge Bișoc  * @return
185*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if quota charging has
186*b22eefacSGeorge Bișoc  * been done successfully without problemns.
187*b22eefacSGeorge Bișoc  * STATUS_QUOTA_EXCEEDED is returned if the caller
188*b22eefacSGeorge Bișoc  * wants to charge quotas with amount way over
189*b22eefacSGeorge Bișoc  * the limits. STATUS_PAGEFILE_QUOTA_EXCEEDED
190*b22eefacSGeorge Bișoc  * is returned for the same situation but
191*b22eefacSGeorge Bișoc  * specific to page files instead.
192c2c66affSColin Finck  */
193c2c66affSColin Finck NTSTATUS
194c2c66affSColin Finck NTAPI
PspChargeProcessQuotaSpecifiedPool(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ PS_QUOTA_TYPE QuotaType,_In_ SIZE_T Amount)195*b22eefacSGeorge Bișoc PspChargeProcessQuotaSpecifiedPool(
196*b22eefacSGeorge Bișoc     _In_opt_ PEPROCESS Process,
197*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
198*b22eefacSGeorge Bișoc     _In_ PS_QUOTA_TYPE QuotaType,
199*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
200c2c66affSColin Finck {
201a340ec17SGeorge Bișoc     KIRQL OldIrql;
202*b22eefacSGeorge Bișoc     SIZE_T ReturnedQuotas;
203*b22eefacSGeorge Bișoc     SIZE_T UpdatedLimit;
204c2c66affSColin Finck 
205*b22eefacSGeorge Bișoc     /* Sanity checks */
206*b22eefacSGeorge Bișoc     ASSERT(QuotaType < PsQuotaTypes);
207*b22eefacSGeorge Bișoc     ASSERT((SSIZE_T)Amount >= 0);
208*b22eefacSGeorge Bișoc 
209*b22eefacSGeorge Bișoc     /* Guard ourselves in a spin lock */
210a340ec17SGeorge Bișoc     KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
211a340ec17SGeorge Bișoc 
212*b22eefacSGeorge Bișoc     /* Are we within the bounds of quota limit? */
213*b22eefacSGeorge Bișoc     if (QuotaBlock->QuotaEntry[QuotaType].Usage + Amount >
214*b22eefacSGeorge Bișoc         QuotaBlock->QuotaEntry[QuotaType].Limit &&
215*b22eefacSGeorge Bișoc         QuotaBlock != &PspDefaultQuotaBlock)
216c2c66affSColin Finck     {
217*b22eefacSGeorge Bișoc         /* We aren't... Is this a page file quota charging? */
218*b22eefacSGeorge Bișoc         if (QuotaType == PsPageFile)
219c2c66affSColin Finck         {
220*b22eefacSGeorge Bișoc             /* It is, return the appropriate status code */
221*b22eefacSGeorge Bișoc             DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Quota amount exceeds the limit on page file quota (limit -- %lu || amount -- %lu)\n",
222*b22eefacSGeorge Bișoc                     QuotaBlock->QuotaEntry[QuotaType].Limit, Amount);
223*b22eefacSGeorge Bișoc             return STATUS_PAGEFILE_QUOTA_EXCEEDED;
224c2c66affSColin Finck         }
225c2c66affSColin Finck 
226c2c66affSColin Finck         /*
227*b22eefacSGeorge Bișoc          * This is not a page file charge. What we can do at best
228*b22eefacSGeorge Bișoc          * in this scenario is to attempt to raise (expand) the
229*b22eefacSGeorge Bișoc          * quota limit charges of the block.
230*b22eefacSGeorge Bișoc          */
231*b22eefacSGeorge Bișoc         if (!MmRaisePoolQuota(QuotaType,
232*b22eefacSGeorge Bișoc                               QuotaBlock->QuotaEntry[QuotaType].Limit,
233*b22eefacSGeorge Bișoc                               &UpdatedLimit))
234*b22eefacSGeorge Bișoc         {
235*b22eefacSGeorge Bișoc             /*
236*b22eefacSGeorge Bișoc              * We can't? It could be that we must free
237*b22eefacSGeorge Bișoc              * up some resources in order to raise the
238*b22eefacSGeorge Bișoc              * limit, which in that case we must return
239*b22eefacSGeorge Bișoc              * the excess of quota that hasn't been
240*b22eefacSGeorge Bișoc              * returned. If we haven't returned anything
241*b22eefacSGeorge Bișoc              * then what we're doing here is futile.
242*b22eefacSGeorge Bișoc              * Bail out...
243*b22eefacSGeorge Bișoc              */
244*b22eefacSGeorge Bișoc             PspReturnExcessQuotas(QuotaType, &ReturnedQuotas);
245*b22eefacSGeorge Bișoc             if (ReturnedQuotas == 0)
246*b22eefacSGeorge Bișoc             {
247*b22eefacSGeorge Bișoc                 DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Failed to free some resources in order to raise quota limits...\n");
248*b22eefacSGeorge Bișoc                 KeReleaseSpinLock(&PspQuotaLock, OldIrql);
249*b22eefacSGeorge Bișoc                 return STATUS_QUOTA_EXCEEDED;
250*b22eefacSGeorge Bișoc             }
251*b22eefacSGeorge Bișoc 
252*b22eefacSGeorge Bișoc             /* Try to raise the quota limits again */
253*b22eefacSGeorge Bișoc             MmRaisePoolQuota(QuotaType,
254*b22eefacSGeorge Bișoc                              QuotaBlock->QuotaEntry[QuotaType].Limit,
255*b22eefacSGeorge Bișoc                              &UpdatedLimit);
256*b22eefacSGeorge Bișoc         }
257*b22eefacSGeorge Bișoc 
258*b22eefacSGeorge Bișoc         /* Enforce a new raised limit */
259*b22eefacSGeorge Bișoc         InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Limit, UpdatedLimit);
260*b22eefacSGeorge Bișoc 
261*b22eefacSGeorge Bișoc         /*
262*b22eefacSGeorge Bișoc          * Now determine if the current usage and the
263*b22eefacSGeorge Bișoc          * amounting by the caller still exceeds the
264*b22eefacSGeorge Bișoc          * quota limit of the process. If it's still
265*b22eefacSGeorge Bișoc          * over the limit then there's nothing we can
266*b22eefacSGeorge Bișoc          * do, so fail.
267*b22eefacSGeorge Bișoc          */
268*b22eefacSGeorge Bișoc         if (QuotaBlock->QuotaEntry[QuotaType].Usage + Amount >
269*b22eefacSGeorge Bișoc             QuotaBlock->QuotaEntry[QuotaType].Limit)
270*b22eefacSGeorge Bișoc         {
271*b22eefacSGeorge Bișoc             DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Quota amount exceeds the limit (limit -- %lu || amount -- %lu)\n",
272*b22eefacSGeorge Bișoc                     QuotaBlock->QuotaEntry[QuotaType].Limit, Amount);
273*b22eefacSGeorge Bișoc             return STATUS_QUOTA_EXCEEDED;
274*b22eefacSGeorge Bișoc         }
275*b22eefacSGeorge Bișoc     }
276*b22eefacSGeorge Bișoc 
277*b22eefacSGeorge Bișoc     /* Update the quota usage */
278*b22eefacSGeorge Bișoc     InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Usage, Amount);
279*b22eefacSGeorge Bișoc 
280*b22eefacSGeorge Bișoc     /* Update the entry peak if it's less than the usage */
281*b22eefacSGeorge Bișoc     if (QuotaBlock->QuotaEntry[QuotaType].Peak <
282*b22eefacSGeorge Bișoc         QuotaBlock->QuotaEntry[QuotaType].Usage)
283*b22eefacSGeorge Bișoc     {
284*b22eefacSGeorge Bișoc         InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Peak,
285*b22eefacSGeorge Bișoc                                  QuotaBlock->QuotaEntry[QuotaType].Usage);
286*b22eefacSGeorge Bișoc     }
287*b22eefacSGeorge Bișoc 
288*b22eefacSGeorge Bișoc     /* Are we being given a process as well? */
289*b22eefacSGeorge Bișoc     if (Process)
290*b22eefacSGeorge Bișoc     {
291*b22eefacSGeorge Bișoc         /* We're being given, check that's not a system one */
292*b22eefacSGeorge Bișoc         ASSERT(Process != PsInitialSystemProcess);
293*b22eefacSGeorge Bișoc 
294*b22eefacSGeorge Bișoc         InterlockedExchangeAddSizeT(&Process->QuotaUsage[QuotaType], Amount);
295*b22eefacSGeorge Bișoc 
296*b22eefacSGeorge Bișoc         /*
297*b22eefacSGeorge Bișoc          * OK, we've now updated the quota usage of the process
298*b22eefacSGeorge Bișoc          * based upon the amount that the caller wanted to charge.
299*b22eefacSGeorge Bișoc          * Although the peak of process quota can be less than it was
300*b22eefacSGeorge Bișoc          * before so update the peaks as well accordingly.
301*b22eefacSGeorge Bișoc          */
302*b22eefacSGeorge Bișoc         if (Process->QuotaPeak[QuotaType] < Process->QuotaUsage[QuotaType])
303*b22eefacSGeorge Bișoc         {
304*b22eefacSGeorge Bișoc             InterlockedExchangeSizeT(&Process->QuotaPeak[QuotaType],
305*b22eefacSGeorge Bișoc                                      Process->QuotaUsage[QuotaType]);
306*b22eefacSGeorge Bișoc         }
307*b22eefacSGeorge Bișoc     }
308*b22eefacSGeorge Bișoc 
309*b22eefacSGeorge Bișoc     /* Release the lock */
310*b22eefacSGeorge Bișoc     KeReleaseSpinLock(&PspQuotaLock, OldIrql);
311*b22eefacSGeorge Bișoc     return STATUS_SUCCESS;
312*b22eefacSGeorge Bișoc }
313*b22eefacSGeorge Bișoc 
314*b22eefacSGeorge Bișoc /**
315*b22eefacSGeorge Bișoc  * @brief
316*b22eefacSGeorge Bișoc  * Internal kernel function that provides the
317*b22eefacSGeorge Bișoc  * bulk logic of process quota returning. It
318*b22eefacSGeorge Bișoc  * returns (takes away) quotas back from a
319*b22eefacSGeorge Bișoc  * process and/or quota block, which is
320*b22eefacSGeorge Bișoc  * the opposite of charging quotas.
321*b22eefacSGeorge Bișoc  *
322*b22eefacSGeorge Bișoc  * @param[in] Process
323*b22eefacSGeorge Bișoc  * A process, represented as a EPROCESS object.
324*b22eefacSGeorge Bișoc  * This parameter is used to return the own
325*b22eefacSGeorge Bișoc  * process' quota usage.
326*b22eefacSGeorge Bișoc  *
327*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
328*b22eefacSGeorge Bișoc  * The quota block which quotas are to be returned.
329*b22eefacSGeorge Bișoc  * This block can either come from the process itself
330*b22eefacSGeorge Bișoc  * or from an object with specified quota charges.
331*b22eefacSGeorge Bișoc  *
332*b22eefacSGeorge Bișoc  * @param[in] QuotaType
333*b22eefacSGeorge Bișoc  * The quota type which quota in question is to
334*b22eefacSGeorge Bișoc  * be returned. The permitted types are PsPagedPool,
335*b22eefacSGeorge Bișoc  * PsNonPagedPool and PsPageFile.
336*b22eefacSGeorge Bișoc  *
337*b22eefacSGeorge Bișoc  * @param[in] Amount
338*b22eefacSGeorge Bișoc  * The amount of quota to be returned.
339*b22eefacSGeorge Bișoc  *
340*b22eefacSGeorge Bișoc  * @return
341*b22eefacSGeorge Bișoc  * Nothing.
342c2c66affSColin Finck  */
343c2c66affSColin Finck VOID
344c2c66affSColin Finck NTAPI
PspReturnProcessQuotaSpecifiedPool(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ PS_QUOTA_TYPE QuotaType,_In_ SIZE_T Amount)345*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(
346*b22eefacSGeorge Bișoc     _In_opt_ PEPROCESS Process,
347*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
348*b22eefacSGeorge Bișoc     _In_ PS_QUOTA_TYPE QuotaType,
349*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
350c2c66affSColin Finck {
351a340ec17SGeorge Bișoc     KIRQL OldIrql;
352*b22eefacSGeorge Bișoc     SIZE_T ReturnThreshold;
353*b22eefacSGeorge Bișoc     SIZE_T AmountToReturn = 0;
354a340ec17SGeorge Bișoc 
355*b22eefacSGeorge Bișoc     /* Sanity checks */
356*b22eefacSGeorge Bișoc     ASSERT(QuotaType < PsQuotaTypes);
357*b22eefacSGeorge Bișoc     ASSERT((SSIZE_T)Amount >= 0);
358*b22eefacSGeorge Bișoc 
359*b22eefacSGeorge Bișoc     /* Guard ourselves in a spin lock */
360a340ec17SGeorge Bișoc     KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
361a340ec17SGeorge Bișoc 
362*b22eefacSGeorge Bișoc     /* Does the caller return more quota than it was previously charged? */
363*b22eefacSGeorge Bișoc     if ((Process && Process->QuotaUsage[QuotaType] < Amount) ||
364*b22eefacSGeorge Bișoc         QuotaBlock->QuotaEntry[QuotaType].Usage < Amount)
365c2c66affSColin Finck     {
366*b22eefacSGeorge Bișoc         /* It does, crash the system! */
367*b22eefacSGeorge Bișoc         KeBugCheckEx(QUOTA_UNDERFLOW,
368*b22eefacSGeorge Bișoc                      (ULONG_PTR)Process,
369*b22eefacSGeorge Bișoc                      (ULONG_PTR)QuotaType,
370*b22eefacSGeorge Bișoc                      Process ? (ULONG_PTR)Process->QuotaUsage[QuotaType] :
371*b22eefacSGeorge Bișoc                                QuotaBlock->QuotaEntry[QuotaType].Usage,
372*b22eefacSGeorge Bișoc                      (ULONG_PTR)Amount);
373c2c66affSColin Finck     }
374a340ec17SGeorge Bișoc 
375*b22eefacSGeorge Bișoc     /* The return threshold can be non paged or paged */
376*b22eefacSGeorge Bișoc     ReturnThreshold = QuotaType ? PSP_NON_PAGED_POOL_QUOTA_THRESHOLD : PSP_PAGED_POOL_QUOTA_THRESHOLD;
377*b22eefacSGeorge Bișoc 
378*b22eefacSGeorge Bișoc     /*
379*b22eefacSGeorge Bișoc      * We need to trim the quota limits based on the
380*b22eefacSGeorge Bișoc      * amount we're going to return quotas back.
381*b22eefacSGeorge Bișoc      */
382*b22eefacSGeorge Bișoc     if ((QuotaType != PsPageFile && QuotaBlock != &PspDefaultQuotaBlock) &&
383*b22eefacSGeorge Bișoc         (QuotaBlock->QuotaEntry[QuotaType].Limit > QuotaBlock->QuotaEntry[QuotaType].Usage + ReturnThreshold))
384*b22eefacSGeorge Bișoc     {
385*b22eefacSGeorge Bișoc         /*
386*b22eefacSGeorge Bișoc          * If the amount to return exceeds the threshold,
387*b22eefacSGeorge Bișoc          * the new amount becomes the default, otherwise
388*b22eefacSGeorge Bișoc          * the amount is just the one given by the caller.
389*b22eefacSGeorge Bișoc          */
390*b22eefacSGeorge Bișoc         AmountToReturn = min(Amount, ReturnThreshold);
391*b22eefacSGeorge Bișoc 
392*b22eefacSGeorge Bișoc         /* Add up the lots to the Return field */
393*b22eefacSGeorge Bișoc         InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Return, AmountToReturn);
394*b22eefacSGeorge Bișoc 
395*b22eefacSGeorge Bișoc         /*
396*b22eefacSGeorge Bișoc          * If the amount to return exceeds the threshold then
397*b22eefacSGeorge Bișoc          * we have lots of quota to return to Mm. So do it so
398*b22eefacSGeorge Bișoc          * and zerou out the Return field.
399*b22eefacSGeorge Bișoc          */
400*b22eefacSGeorge Bișoc         if (QuotaBlock->QuotaEntry[QuotaType].Return > ReturnThreshold)
401*b22eefacSGeorge Bișoc         {
402*b22eefacSGeorge Bișoc             MmReturnPoolQuota(QuotaType, QuotaBlock->QuotaEntry[QuotaType].Return);
403*b22eefacSGeorge Bișoc             InterlockedExchangeSizeT(QuotaBlock->QuotaEntry[QuotaType].Return, 0);
404*b22eefacSGeorge Bișoc         }
405*b22eefacSGeorge Bișoc 
406*b22eefacSGeorge Bișoc         /* And try to trim the limit */
407*b22eefacSGeorge Bișoc         InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Limit,
408*b22eefacSGeorge Bișoc                                  QuotaBlock->QuotaEntry[QuotaType].Limit - AmountToReturn);
409*b22eefacSGeorge Bișoc     }
410*b22eefacSGeorge Bișoc 
411*b22eefacSGeorge Bișoc     /* Update the usage member of the block */
412*b22eefacSGeorge Bișoc     InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Usage, -(LONG_PTR)Amount);
413*b22eefacSGeorge Bișoc 
414*b22eefacSGeorge Bișoc     /* Are we being given a process? */
415*b22eefacSGeorge Bișoc     if (Process)
416*b22eefacSGeorge Bișoc     {
417*b22eefacSGeorge Bișoc         /* We're being given, check that's not a system one  */
418*b22eefacSGeorge Bișoc         ASSERT(Process != PsInitialSystemProcess);
419*b22eefacSGeorge Bișoc 
420*b22eefacSGeorge Bișoc         /* Decrease the process' quota usage */
421*b22eefacSGeorge Bișoc         InterlockedExchangeAddSizeT(&Process->QuotaUsage[QuotaType], -(LONG_PTR)Amount);
422*b22eefacSGeorge Bișoc     }
423*b22eefacSGeorge Bișoc 
424*b22eefacSGeorge Bișoc     /* We're done, release the lock */
425a340ec17SGeorge Bișoc     KeReleaseSpinLock(&PspQuotaLock, OldIrql);
426c2c66affSColin Finck }
427c2c66affSColin Finck 
428c2c66affSColin Finck /* FUNCTIONS ***************************************************************/
429c2c66affSColin Finck 
430*b22eefacSGeorge Bișoc /**
431*b22eefacSGeorge Bișoc  * @brief
432*b22eefacSGeorge Bișoc  * Initializes the quota system during boot
433*b22eefacSGeorge Bișoc  * phase of the system, which sets up the
434*b22eefacSGeorge Bișoc  * default quota block that is used across
435*b22eefacSGeorge Bișoc  * several processes.
436*b22eefacSGeorge Bișoc  *
437*b22eefacSGeorge Bișoc  * @return
438*b22eefacSGeorge Bișoc  * Nothing.
439*b22eefacSGeorge Bișoc  */
4405c7ce447SVictor Perevertkin CODE_SEG("INIT")
441c2c66affSColin Finck VOID
442c2c66affSColin Finck NTAPI
PsInitializeQuotaSystem(VOID)443c2c66affSColin Finck PsInitializeQuotaSystem(VOID)
444c2c66affSColin Finck {
445*b22eefacSGeorge Bișoc     /* Initialize the default block */
446c2c66affSColin Finck     RtlZeroMemory(&PspDefaultQuotaBlock, sizeof(PspDefaultQuotaBlock));
447*b22eefacSGeorge Bișoc 
448*b22eefacSGeorge Bișoc     /* Assign the default quota limits */
4496170b574SGeorge Bișoc     PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit = (SIZE_T)-1;
4506170b574SGeorge Bișoc     PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit = (SIZE_T)-1;
4516170b574SGeorge Bișoc     PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit = (SIZE_T)-1;
452*b22eefacSGeorge Bișoc 
453*b22eefacSGeorge Bișoc     /*
454*b22eefacSGeorge Bișoc      * Set up the count references as the
455*b22eefacSGeorge Bișoc      * default block will going to be used.
456*b22eefacSGeorge Bișoc      */
457*b22eefacSGeorge Bișoc     PspDefaultQuotaBlock.ReferenceCount = 1;
458*b22eefacSGeorge Bișoc     PspDefaultQuotaBlock.ProcessCount = 1;
459*b22eefacSGeorge Bișoc 
460*b22eefacSGeorge Bișoc     /* Assign that block to initial process */
461c2c66affSColin Finck     PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock;
462c2c66affSColin Finck }
463c2c66affSColin Finck 
464*b22eefacSGeorge Bișoc /**
465*b22eefacSGeorge Bișoc  * @brief
466*b22eefacSGeorge Bișoc  * Inherits the quota block to another newborn
467*b22eefacSGeorge Bișoc  * (child) process. If there's no parent
468*b22eefacSGeorge Bișoc  * process, the default quota block is
469*b22eefacSGeorge Bișoc  * assigned.
470*b22eefacSGeorge Bișoc  *
471*b22eefacSGeorge Bișoc  * @param[in] Process
472*b22eefacSGeorge Bișoc  * The child process which quota block
473*b22eefacSGeorge Bișoc  * is to be given.
474*b22eefacSGeorge Bișoc  *
475*b22eefacSGeorge Bișoc  * @param[in] ParentProcess
476*b22eefacSGeorge Bișoc  * The parent process.
477*b22eefacSGeorge Bișoc  *
478*b22eefacSGeorge Bișoc  * @return
479*b22eefacSGeorge Bișoc  * Nothing.
480*b22eefacSGeorge Bișoc  */
481c2c66affSColin Finck VOID
482c2c66affSColin Finck NTAPI
PspInheritQuota(_In_ PEPROCESS Process,_In_opt_ PEPROCESS ParentProcess)483*b22eefacSGeorge Bișoc PspInheritQuota(
484*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
485*b22eefacSGeorge Bișoc     _In_opt_ PEPROCESS ParentProcess)
486c2c66affSColin Finck {
487*b22eefacSGeorge Bișoc     PEPROCESS_QUOTA_BLOCK QuotaBlock;
488*b22eefacSGeorge Bișoc 
489c2c66affSColin Finck     if (ParentProcess != NULL)
490c2c66affSColin Finck     {
491*b22eefacSGeorge Bișoc         ASSERT(ParentProcess->QuotaBlock != NULL);
492*b22eefacSGeorge Bișoc         QuotaBlock = ParentProcess->QuotaBlock;
493*b22eefacSGeorge Bișoc     }
494*b22eefacSGeorge Bișoc     else
495*b22eefacSGeorge Bișoc     {
496*b22eefacSGeorge Bișoc         QuotaBlock = &PspDefaultQuotaBlock;
497*b22eefacSGeorge Bișoc     }
498c2c66affSColin Finck 
499*b22eefacSGeorge Bișoc     InterlockedIncrementSizeT(&QuotaBlock->ProcessCount);
500*b22eefacSGeorge Bișoc     InterlockedIncrementSizeT(&QuotaBlock->ReferenceCount);
501c2c66affSColin Finck 
502c2c66affSColin Finck     Process->QuotaBlock = QuotaBlock;
503c2c66affSColin Finck }
504c2c66affSColin Finck 
505*b22eefacSGeorge Bișoc /**
506*b22eefacSGeorge Bișoc  * @brief
507*b22eefacSGeorge Bișoc  * Inserts the new quota block into
508*b22eefacSGeorge Bișoc  * the quota list.
509*b22eefacSGeorge Bișoc  *
510*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
511*b22eefacSGeorge Bișoc  * The new quota block.
512*b22eefacSGeorge Bișoc  *
513*b22eefacSGeorge Bișoc  * @return
514*b22eefacSGeorge Bișoc  * Nothing.
515*b22eefacSGeorge Bișoc  */
516c2c66affSColin Finck VOID
517c2c66affSColin Finck NTAPI
PspInsertQuotaBlock(_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)518c2c66affSColin Finck PspInsertQuotaBlock(
519*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
520c2c66affSColin Finck {
521c2c66affSColin Finck     KIRQL OldIrql;
522c2c66affSColin Finck 
523c2c66affSColin Finck     KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
524c2c66affSColin Finck     InsertTailList(&PspQuotaBlockList, &QuotaBlock->QuotaList);
525c2c66affSColin Finck     KeReleaseSpinLock(&PspQuotaLock, OldIrql);
526c2c66affSColin Finck }
527c2c66affSColin Finck 
528*b22eefacSGeorge Bișoc /**
529*b22eefacSGeorge Bișoc  * @brief
530*b22eefacSGeorge Bișoc  * De-references a quota block when quotas
531*b22eefacSGeorge Bișoc  * have been returned back because of an
532*b22eefacSGeorge Bișoc  * object de-allocation or when a process
533*b22eefacSGeorge Bișoc  * gets destroyed. If the last instance
534*b22eefacSGeorge Bișoc  * that held up the block gets de-referenced
535*b22eefacSGeorge Bișoc  * the function will perform a cleanup against
536*b22eefacSGeorge Bișoc  * that block and it'll free the quota block
537*b22eefacSGeorge Bișoc  * from memory.
538*b22eefacSGeorge Bișoc  *
539*b22eefacSGeorge Bișoc  * @param[in] Process
540*b22eefacSGeorge Bișoc  * A pointer to a process that de-references the
541*b22eefacSGeorge Bișoc  * quota block.
542*b22eefacSGeorge Bișoc  *
543*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
544*b22eefacSGeorge Bișoc  * A pointer to a quota block that is to be
545*b22eefacSGeorge Bișoc  * de-referenced. This block can come from a
546*b22eefacSGeorge Bișoc  * process that references it or an object.
547*b22eefacSGeorge Bișoc  *
548*b22eefacSGeorge Bișoc  * @return
549*b22eefacSGeorge Bișoc  * Nothing.
550*b22eefacSGeorge Bișoc  */
551c2c66affSColin Finck VOID
552c2c66affSColin Finck NTAPI
PspDereferenceQuotaBlock(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)553*b22eefacSGeorge Bișoc PspDereferenceQuotaBlock(
554*b22eefacSGeorge Bișoc     _In_opt_ PEPROCESS Process,
555*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
556c2c66affSColin Finck {
557*b22eefacSGeorge Bișoc     ULONG PsQuotaTypeIndex;
558c2c66affSColin Finck     KIRQL OldIrql;
559c2c66affSColin Finck 
560*b22eefacSGeorge Bișoc     /* Make sure the quota block is not trash */
561*b22eefacSGeorge Bișoc     ASSERT(QuotaBlock);
562*b22eefacSGeorge Bișoc 
563*b22eefacSGeorge Bișoc     /* Iterate over the process quota types if we have a process */
564*b22eefacSGeorge Bișoc     if (Process)
565*b22eefacSGeorge Bișoc     {
566*b22eefacSGeorge Bișoc         for (PsQuotaTypeIndex = PsNonPagedPool; PsQuotaTypeIndex < PsQuotaTypes; PsQuotaTypeIndex++)
567*b22eefacSGeorge Bișoc         {
568*b22eefacSGeorge Bișoc             /*
569*b22eefacSGeorge Bișoc              * We need to make sure that the quota usage
570*b22eefacSGeorge Bișoc              * uniquely associated with the process is 0
571*b22eefacSGeorge Bișoc              * on that moment the process gets destroyed.
572*b22eefacSGeorge Bișoc              */
573*b22eefacSGeorge Bișoc             ASSERT(Process->QuotaUsage[PsQuotaTypeIndex] == 0);
574*b22eefacSGeorge Bișoc         }
575*b22eefacSGeorge Bișoc 
576*b22eefacSGeorge Bișoc         /* As the process is now gone, decrement the process count */
577*b22eefacSGeorge Bișoc         InterlockedDecrementUL(&QuotaBlock->ProcessCount);
578*b22eefacSGeorge Bișoc     }
579*b22eefacSGeorge Bișoc 
580*b22eefacSGeorge Bișoc     /* If no one is using this block, begin to destroy it */
581c2c66affSColin Finck     if (QuotaBlock != &PspDefaultQuotaBlock &&
582c2c66affSColin Finck         InterlockedDecrementUL(&QuotaBlock->ReferenceCount) == 0)
583c2c66affSColin Finck     {
584*b22eefacSGeorge Bișoc         /* Acquire the quota lock */
585c2c66affSColin Finck         KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
586*b22eefacSGeorge Bișoc 
587*b22eefacSGeorge Bișoc         /* Return all the quotas back to Mm and remove the quota from list */
588*b22eefacSGeorge Bișoc         PspReturnQuotasOnDestroy(QuotaBlock);
589c2c66affSColin Finck         RemoveEntryList(&QuotaBlock->QuotaList);
590*b22eefacSGeorge Bișoc 
591*b22eefacSGeorge Bișoc         /* Release the lock and free the block */
592c2c66affSColin Finck         KeReleaseSpinLock(&PspQuotaLock, OldIrql);
593*b22eefacSGeorge Bișoc         ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
594c2c66affSColin Finck     }
595c2c66affSColin Finck }
596c2c66affSColin Finck 
597*b22eefacSGeorge Bișoc /**
598*b22eefacSGeorge Bișoc  * @brief
599*b22eefacSGeorge Bișoc  * Returns the shared (paged and non paged)
600*b22eefacSGeorge Bișoc  * pool quotas. The function is used exclusively
601*b22eefacSGeorge Bișoc  * by the Object Manager to manage quota returns
602*b22eefacSGeorge Bișoc  * handling of objects.
603*b22eefacSGeorge Bișoc  *
604*b22eefacSGeorge Bișoc  * @param[in] QuotaBlock
605*b22eefacSGeorge Bișoc  * The quota block which quotas are to
606*b22eefacSGeorge Bișoc  * be returned.
607*b22eefacSGeorge Bișoc  *
608*b22eefacSGeorge Bișoc  * @param[in] AmountToReturnPaged
609*b22eefacSGeorge Bișoc  * The amount of paged quotas quotas to
610*b22eefacSGeorge Bișoc  * be returned.
611*b22eefacSGeorge Bișoc  *
612*b22eefacSGeorge Bișoc  * @param[in] AmountToReturnNonPaged
613*b22eefacSGeorge Bișoc  * The amount of non paged quotas to
614*b22eefacSGeorge Bișoc  * be returned.
615*b22eefacSGeorge Bișoc  *
616*b22eefacSGeorge Bișoc  * @return
617*b22eefacSGeorge Bișoc  * Nothing.
618*b22eefacSGeorge Bișoc  */
619*b22eefacSGeorge Bișoc VOID
620*b22eefacSGeorge Bișoc NTAPI
PsReturnSharedPoolQuota(_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ SIZE_T AmountToReturnPaged,_In_ SIZE_T AmountToReturnNonPaged)621*b22eefacSGeorge Bișoc PsReturnSharedPoolQuota(
622*b22eefacSGeorge Bișoc     _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
623*b22eefacSGeorge Bișoc     _In_ SIZE_T AmountToReturnPaged,
624*b22eefacSGeorge Bișoc     _In_ SIZE_T AmountToReturnNonPaged)
625*b22eefacSGeorge Bișoc {
626*b22eefacSGeorge Bișoc     /* Sanity check */
627*b22eefacSGeorge Bișoc     ASSERT(QuotaBlock);
628*b22eefacSGeorge Bișoc 
629*b22eefacSGeorge Bișoc     /* Return the pool quotas if there're any */
630*b22eefacSGeorge Bișoc     if (AmountToReturnPaged != 0)
631*b22eefacSGeorge Bișoc     {
632*b22eefacSGeorge Bișoc         PspReturnProcessQuotaSpecifiedPool(NULL, QuotaBlock, PsPagedPool, AmountToReturnPaged);
633*b22eefacSGeorge Bișoc     }
634*b22eefacSGeorge Bișoc 
635*b22eefacSGeorge Bișoc     if (AmountToReturnNonPaged != 0)
636*b22eefacSGeorge Bișoc     {
637*b22eefacSGeorge Bișoc         PspReturnProcessQuotaSpecifiedPool(NULL, QuotaBlock, PsNonPagedPool, AmountToReturnNonPaged);
638*b22eefacSGeorge Bișoc     }
639*b22eefacSGeorge Bișoc 
640*b22eefacSGeorge Bișoc     DPRINT("PsReturnSharedPoolQuota(): Amount returned back (paged %lu -- non paged %lu)\n", AmountToReturnPaged, AmountToReturnNonPaged);
641*b22eefacSGeorge Bișoc 
642*b22eefacSGeorge Bișoc     /* Dereference the quota block */
643*b22eefacSGeorge Bișoc     PspDereferenceQuotaBlock(NULL, QuotaBlock);
644*b22eefacSGeorge Bișoc }
645*b22eefacSGeorge Bișoc 
646*b22eefacSGeorge Bișoc /**
647*b22eefacSGeorge Bișoc  * @brief
648*b22eefacSGeorge Bișoc  * Charges the shared (paged and non paged)
649*b22eefacSGeorge Bișoc  * pool quotas. The function is used exclusively
650*b22eefacSGeorge Bișoc  * by the Object Manager to manage quota charges
651*b22eefacSGeorge Bișoc  * handling of objects.
652*b22eefacSGeorge Bișoc  *
653*b22eefacSGeorge Bișoc  * @param[in] Process
654*b22eefacSGeorge Bișoc  * The process which quotas are to
655*b22eefacSGeorge Bișoc  * be charged within its quota block.
656*b22eefacSGeorge Bișoc  *
657*b22eefacSGeorge Bișoc  * @param[in] AmountToChargePaged
658*b22eefacSGeorge Bișoc  * The amount of paged quotas quotas to
659*b22eefacSGeorge Bișoc  * be charged.
660*b22eefacSGeorge Bișoc  *
661*b22eefacSGeorge Bișoc  * @param[in] AmountToChargeNonPaged
662*b22eefacSGeorge Bișoc  * The amount of non paged quotas to
663*b22eefacSGeorge Bișoc  * be charged.
664*b22eefacSGeorge Bișoc  *
665*b22eefacSGeorge Bișoc  * @return
666*b22eefacSGeorge Bișoc  * Returns the charged quota block, which it'll
667*b22eefacSGeorge Bișoc  * be used by the Object Manager to attach
668*b22eefacSGeorge Bișoc  * the charged quotas information to the object.
669*b22eefacSGeorge Bișoc  * If the function fails to charge quotas, NULL
670*b22eefacSGeorge Bișoc  * is returned to the caller.
671*b22eefacSGeorge Bișoc  */
672*b22eefacSGeorge Bișoc PEPROCESS_QUOTA_BLOCK
673*b22eefacSGeorge Bișoc NTAPI
PsChargeSharedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T AmountToChargePaged,_In_ SIZE_T AmountToChargeNonPaged)674*b22eefacSGeorge Bișoc PsChargeSharedPoolQuota(
675*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
676*b22eefacSGeorge Bișoc     _In_ SIZE_T AmountToChargePaged,
677*b22eefacSGeorge Bișoc     _In_ SIZE_T AmountToChargeNonPaged)
678*b22eefacSGeorge Bișoc {
679*b22eefacSGeorge Bișoc     NTSTATUS Status;
680*b22eefacSGeorge Bișoc 
681*b22eefacSGeorge Bișoc     /* Sanity checks */
682*b22eefacSGeorge Bișoc     ASSERT(Process);
683*b22eefacSGeorge Bișoc     ASSERT(Process->QuotaBlock);
684*b22eefacSGeorge Bișoc 
685*b22eefacSGeorge Bișoc     /* Do we have some paged pool quota to charge? */
686*b22eefacSGeorge Bișoc     if (AmountToChargePaged != 0)
687*b22eefacSGeorge Bișoc     {
688*b22eefacSGeorge Bișoc         /* We do, charge! */
689*b22eefacSGeorge Bișoc         Status = PspChargeProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsPagedPool, AmountToChargePaged);
690*b22eefacSGeorge Bișoc         if (!NT_SUCCESS(Status))
691*b22eefacSGeorge Bișoc         {
692*b22eefacSGeorge Bișoc             DPRINT1("PsChargeSharedPoolQuota(): Failed to charge the shared pool quota (Status 0x%lx)\n", Status);
693*b22eefacSGeorge Bișoc             return NULL;
694*b22eefacSGeorge Bișoc         }
695*b22eefacSGeorge Bișoc     }
696*b22eefacSGeorge Bișoc 
697*b22eefacSGeorge Bișoc     /* Do we have some non paged pool quota to charge? */
698*b22eefacSGeorge Bișoc     if (AmountToChargeNonPaged != 0)
699*b22eefacSGeorge Bișoc     {
700*b22eefacSGeorge Bișoc         /* We do, charge! */
701*b22eefacSGeorge Bișoc         Status = PspChargeProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsNonPagedPool, AmountToChargeNonPaged);
702*b22eefacSGeorge Bișoc         if (!NT_SUCCESS(Status))
703*b22eefacSGeorge Bișoc         {
704*b22eefacSGeorge Bișoc             DPRINT1("PsChargeSharedPoolQuota(): Failed to charge the shared pool quota (Status 0x%lx). Attempting to return some paged pool back...\n", Status);
705*b22eefacSGeorge Bișoc             PspReturnProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsPagedPool, AmountToChargePaged);
706*b22eefacSGeorge Bișoc             return NULL;
707*b22eefacSGeorge Bișoc         }
708*b22eefacSGeorge Bișoc     }
709*b22eefacSGeorge Bișoc 
710*b22eefacSGeorge Bișoc     /* We have charged the quotas of an object, increment the reference */
711*b22eefacSGeorge Bișoc     InterlockedIncrementSizeT(&Process->QuotaBlock->ReferenceCount);
712*b22eefacSGeorge Bișoc 
713*b22eefacSGeorge Bișoc     DPRINT("PsChargeSharedPoolQuota(): Amount charged (paged %lu --  non paged %lu)\n", AmountToChargePaged, AmountToChargeNonPaged);
714*b22eefacSGeorge Bișoc     return Process->QuotaBlock;
715*b22eefacSGeorge Bișoc }
716*b22eefacSGeorge Bișoc 
717*b22eefacSGeorge Bișoc /**
718*b22eefacSGeorge Bișoc  * @brief
719*b22eefacSGeorge Bișoc  * Charges the process page file quota.
720*b22eefacSGeorge Bișoc  * The function is used internally by
721*b22eefacSGeorge Bișoc  * the kernel.
722*b22eefacSGeorge Bișoc  *
723*b22eefacSGeorge Bișoc  * @param[in] Process
724*b22eefacSGeorge Bișoc  * The process which page file quota is
725*b22eefacSGeorge Bișoc  * to be charged.
726*b22eefacSGeorge Bișoc  *
727*b22eefacSGeorge Bișoc  * @param[in] Amount
728*b22eefacSGeorge Bișoc  * The amount of page file quota to charge.
729*b22eefacSGeorge Bișoc  *
730*b22eefacSGeorge Bișoc  * @return
731*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if quota charging has
732*b22eefacSGeorge Bișoc  * been done with success, otherwise a NTSTATUS
733*b22eefacSGeorge Bișoc  * code of STATUS_PAGEFILE_QUOTA_EXCEEDED is
734*b22eefacSGeorge Bișoc  * returned.
735c2c66affSColin Finck  */
736c2c66affSColin Finck NTSTATUS
737c2c66affSColin Finck NTAPI
PsChargeProcessPageFileQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)738*b22eefacSGeorge Bișoc PsChargeProcessPageFileQuota(
739*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
740*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
741c2c66affSColin Finck {
742c2c66affSColin Finck     /* Don't do anything for the system process */
743c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
744c2c66affSColin Finck 
745*b22eefacSGeorge Bișoc     return PspChargeProcessQuotaSpecifiedPool(Process, Process->QuotaBlock, PsPageFile, Amount);
746c2c66affSColin Finck }
747c2c66affSColin Finck 
748*b22eefacSGeorge Bișoc /**
749*b22eefacSGeorge Bișoc  * @brief
750*b22eefacSGeorge Bișoc  * Charges the pool quota of a given process.
751*b22eefacSGeorge Bișoc  * The kind of pool quota to charge is determined
752*b22eefacSGeorge Bișoc  * by the PoolType parameter.
753*b22eefacSGeorge Bișoc  *
754*b22eefacSGeorge Bișoc  * @param[in] Process
755*b22eefacSGeorge Bișoc  * The process which quota is to be
756*b22eefacSGeorge Bișoc  * charged.
757*b22eefacSGeorge Bișoc  *
758*b22eefacSGeorge Bișoc  * @param[in] PoolType
759*b22eefacSGeorge Bișoc  * The pool type to choose to charge quotas
760*b22eefacSGeorge Bișoc  * (e.g. PagedPool or NonPagedPool).
761*b22eefacSGeorge Bișoc  *
762*b22eefacSGeorge Bișoc  * @param[in] Amount
763*b22eefacSGeorge Bișoc  * The amount of quotas to charge into a process.
764*b22eefacSGeorge Bișoc  *
765*b22eefacSGeorge Bișoc  * @return
766*b22eefacSGeorge Bișoc  * Nothing.
767*b22eefacSGeorge Bișoc  *
768*b22eefacSGeorge Bișoc  * @remarks
769*b22eefacSGeorge Bișoc  * The function raises an exception if STATUS_QUOTA_EXCEEDED
770*b22eefacSGeorge Bișoc  * status code is returned. Callers are responsible on their
771*b22eefacSGeorge Bișoc  * own to handle the raised exception.
772c2c66affSColin Finck  */
773c2c66affSColin Finck VOID
774c2c66affSColin Finck NTAPI
PsChargePoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)775*b22eefacSGeorge Bișoc PsChargePoolQuota(
776*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
777*b22eefacSGeorge Bișoc     _In_ POOL_TYPE PoolType,
778*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
779c2c66affSColin Finck {
780c2c66affSColin Finck     NTSTATUS Status;
781c2c66affSColin Finck     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
782c2c66affSColin Finck 
783c2c66affSColin Finck     /* Don't do anything for the system process */
784c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return;
785c2c66affSColin Finck 
786c2c66affSColin Finck     /* Charge the usage */
787c2c66affSColin Finck     Status = PsChargeProcessPoolQuota(Process, PoolType, Amount);
788c2c66affSColin Finck     if (!NT_SUCCESS(Status)) ExRaiseStatus(Status);
789c2c66affSColin Finck }
790c2c66affSColin Finck 
791*b22eefacSGeorge Bișoc /**
792*b22eefacSGeorge Bișoc  * @brief
793*b22eefacSGeorge Bișoc  * Charges the non paged pool quota
794*b22eefacSGeorge Bișoc  * of a given process.
795*b22eefacSGeorge Bișoc  *
796*b22eefacSGeorge Bișoc  * @param[in] Process
797*b22eefacSGeorge Bișoc  * The process which non paged quota
798*b22eefacSGeorge Bișoc  * is to be charged.
799*b22eefacSGeorge Bișoc  *
800*b22eefacSGeorge Bișoc  * @param[in] Amount
801*b22eefacSGeorge Bișoc  * The amount of quotas to charge into a process.
802*b22eefacSGeorge Bișoc  *
803*b22eefacSGeorge Bișoc  * @return
804*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if quota charing has
805*b22eefacSGeorge Bișoc  * suceeded, STATUS_QUOTA_EXCEEDED is returned
806*b22eefacSGeorge Bișoc  * otherwise to indicate the caller attempted
807*b22eefacSGeorge Bișoc  * to charge quotas over the limits.
808c2c66affSColin Finck  */
809c2c66affSColin Finck NTSTATUS
810c2c66affSColin Finck NTAPI
PsChargeProcessNonPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)811*b22eefacSGeorge Bișoc PsChargeProcessNonPagedPoolQuota(
812*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
813*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
814c2c66affSColin Finck {
815c2c66affSColin Finck     /* Call the general function */
816c2c66affSColin Finck     return PsChargeProcessPoolQuota(Process, NonPagedPool, Amount);
817c2c66affSColin Finck }
818c2c66affSColin Finck 
819*b22eefacSGeorge Bișoc /**
820*b22eefacSGeorge Bișoc  * @brief
821*b22eefacSGeorge Bișoc  * Charges the paged pool quota of a
822*b22eefacSGeorge Bișoc  * given process.
823*b22eefacSGeorge Bișoc  *
824*b22eefacSGeorge Bișoc  * @param[in] Process
825*b22eefacSGeorge Bișoc  * The process which paged quota
826*b22eefacSGeorge Bișoc  * is to be charged.
827*b22eefacSGeorge Bișoc  *
828*b22eefacSGeorge Bișoc  * @param[in] Amount
829*b22eefacSGeorge Bișoc  * The amount of quotas to charge into a process.
830*b22eefacSGeorge Bișoc  *
831*b22eefacSGeorge Bișoc  * @return
832*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if quota charing has
833*b22eefacSGeorge Bișoc  * suceeded, STATUS_QUOTA_EXCEEDED is returned
834*b22eefacSGeorge Bișoc  * otherwise to indicate the caller attempted
835*b22eefacSGeorge Bișoc  * to charge quotas over the limits.
836c2c66affSColin Finck  */
837c2c66affSColin Finck NTSTATUS
838c2c66affSColin Finck NTAPI
PsChargeProcessPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)839*b22eefacSGeorge Bișoc PsChargeProcessPagedPoolQuota(
840*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
841*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
842c2c66affSColin Finck {
843c2c66affSColin Finck     /* Call the general function */
844c2c66affSColin Finck     return PsChargeProcessPoolQuota(Process, PagedPool, Amount);
845c2c66affSColin Finck }
846c2c66affSColin Finck 
847*b22eefacSGeorge Bișoc /**
848*b22eefacSGeorge Bișoc  * @brief
849*b22eefacSGeorge Bișoc  * Charges the process' quota pool.
850*b22eefacSGeorge Bișoc  * The type of quota to be charged
851*b22eefacSGeorge Bișoc  * depends upon the PoolType parameter.
852*b22eefacSGeorge Bișoc  *
853*b22eefacSGeorge Bișoc  * @param[in] Process
854*b22eefacSGeorge Bișoc  * The process which quota is to
855*b22eefacSGeorge Bișoc  * be charged.
856*b22eefacSGeorge Bișoc  *
857*b22eefacSGeorge Bișoc  * @param[in] PoolType
858*b22eefacSGeorge Bișoc  * The type of quota pool to charge (e.g.
859*b22eefacSGeorge Bișoc  * PagedPool or NonPagedPool).
860*b22eefacSGeorge Bișoc  *
861*b22eefacSGeorge Bișoc  * @param[in] Amount
862*b22eefacSGeorge Bișoc  * The amount of quotas to charge into a process.
863*b22eefacSGeorge Bișoc  *
864*b22eefacSGeorge Bișoc  * @return
865*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if quota charing has
866*b22eefacSGeorge Bișoc  * suceeded, STATUS_QUOTA_EXCEEDED is returned
867*b22eefacSGeorge Bișoc  * otherwise to indicate the caller attempted
868*b22eefacSGeorge Bișoc  * to charge quotas over the limits.
869c2c66affSColin Finck  */
870c2c66affSColin Finck NTSTATUS
871c2c66affSColin Finck NTAPI
PsChargeProcessPoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)872*b22eefacSGeorge Bișoc PsChargeProcessPoolQuota(
873*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
874*b22eefacSGeorge Bișoc     _In_ POOL_TYPE PoolType,
875*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
876c2c66affSColin Finck {
877c2c66affSColin Finck     /* Don't do anything for the system process */
878c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
879c2c66affSColin Finck 
880c2c66affSColin Finck     return PspChargeProcessQuotaSpecifiedPool(Process,
881*b22eefacSGeorge Bișoc                                               Process->QuotaBlock,
882c2c66affSColin Finck                                               (PoolType & PAGED_POOL_MASK),
883c2c66affSColin Finck                                               Amount);
884c2c66affSColin Finck }
885c2c66affSColin Finck 
886*b22eefacSGeorge Bișoc /**
887*b22eefacSGeorge Bișoc  * @brief
888*b22eefacSGeorge Bișoc  * Returns the pool quota that the
889*b22eefacSGeorge Bișoc  * process was taking up.
890*b22eefacSGeorge Bișoc  *
891*b22eefacSGeorge Bișoc  * @param[in] Process
892*b22eefacSGeorge Bișoc  * The process which quota is to
893*b22eefacSGeorge Bișoc  * be returned.
894*b22eefacSGeorge Bișoc  *
895*b22eefacSGeorge Bișoc  * @param[in] PoolType
896*b22eefacSGeorge Bișoc  * The type of quota pool to return (e.g.
897*b22eefacSGeorge Bișoc  * PagedPool or NonPagedPool).
898*b22eefacSGeorge Bișoc  *
899*b22eefacSGeorge Bișoc  * @param[in] Amount
900*b22eefacSGeorge Bișoc  * The amount of quotas to return from a process.
901*b22eefacSGeorge Bișoc  *
902*b22eefacSGeorge Bișoc  * @return
903*b22eefacSGeorge Bișoc  * Nothing.
904c2c66affSColin Finck  */
905c2c66affSColin Finck VOID
906c2c66affSColin Finck NTAPI
PsReturnPoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)907*b22eefacSGeorge Bișoc PsReturnPoolQuota(
908*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
909*b22eefacSGeorge Bișoc     _In_ POOL_TYPE PoolType,
910*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
911c2c66affSColin Finck {
912c2c66affSColin Finck     /* Don't do anything for the system process */
913c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return;
914c2c66affSColin Finck 
915c2c66affSColin Finck     PspReturnProcessQuotaSpecifiedPool(Process,
916*b22eefacSGeorge Bișoc                                        Process->QuotaBlock,
917c2c66affSColin Finck                                        (PoolType & PAGED_POOL_MASK),
918c2c66affSColin Finck                                        Amount);
919c2c66affSColin Finck }
920c2c66affSColin Finck 
921*b22eefacSGeorge Bișoc /**
922*b22eefacSGeorge Bișoc  * @brief
923*b22eefacSGeorge Bișoc  * Returns the non paged quota pool
924*b22eefacSGeorge Bișoc  * that the process was taking up.
925*b22eefacSGeorge Bișoc  *
926*b22eefacSGeorge Bișoc  * @param[in] Process
927*b22eefacSGeorge Bișoc  * The process which non paged quota
928*b22eefacSGeorge Bișoc  * is to be returned.
929*b22eefacSGeorge Bișoc  *
930*b22eefacSGeorge Bișoc  * @param[in] Amount
931*b22eefacSGeorge Bișoc  * The amount of quotas to return from a process.
932*b22eefacSGeorge Bișoc  *
933*b22eefacSGeorge Bișoc  * @return
934*b22eefacSGeorge Bișoc  * Nothing.
935c2c66affSColin Finck  */
936c2c66affSColin Finck VOID
937c2c66affSColin Finck NTAPI
PsReturnProcessNonPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)938*b22eefacSGeorge Bișoc PsReturnProcessNonPagedPoolQuota(
939*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
940*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
941c2c66affSColin Finck {
942c2c66affSColin Finck     /* Don't do anything for the system process */
943c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return;
944c2c66affSColin Finck 
945c2c66affSColin Finck     PsReturnPoolQuota(Process, NonPagedPool, Amount);
946c2c66affSColin Finck }
947c2c66affSColin Finck 
948*b22eefacSGeorge Bișoc /**
949*b22eefacSGeorge Bișoc  * @brief
950*b22eefacSGeorge Bișoc  * Returns the paged pool quota that
951*b22eefacSGeorge Bișoc  * the process was taking up.
952*b22eefacSGeorge Bișoc  *
953*b22eefacSGeorge Bișoc  * @param[in] Process
954*b22eefacSGeorge Bișoc  * The process which paged pool
955*b22eefacSGeorge Bișoc  * quota is to be returned.
956*b22eefacSGeorge Bișoc  *
957*b22eefacSGeorge Bișoc  * @param[in] Amount
958*b22eefacSGeorge Bișoc  * The amount of quotas to return from a process.
959*b22eefacSGeorge Bișoc  *
960*b22eefacSGeorge Bișoc  * @return
961*b22eefacSGeorge Bișoc  * Nothing.
962c2c66affSColin Finck  */
963c2c66affSColin Finck VOID
964c2c66affSColin Finck NTAPI
PsReturnProcessPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)965*b22eefacSGeorge Bișoc PsReturnProcessPagedPoolQuota(
966*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
967*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
968c2c66affSColin Finck {
969c2c66affSColin Finck     /* Don't do anything for the system process */
970c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return;
971c2c66affSColin Finck 
972c2c66affSColin Finck     PsReturnPoolQuota(Process, PagedPool, Amount);
973c2c66affSColin Finck }
974c2c66affSColin Finck 
975*b22eefacSGeorge Bișoc /**
976*b22eefacSGeorge Bișoc  * @brief
977*b22eefacSGeorge Bișoc  * Returns the page file quota that the
978*b22eefacSGeorge Bișoc  * process was taking up. The function
979*b22eefacSGeorge Bișoc  * is used exclusively by the kernel.
980*b22eefacSGeorge Bișoc  *
981*b22eefacSGeorge Bișoc  * @param[in] Process
982*b22eefacSGeorge Bișoc  * The process which pagefile quota is
983*b22eefacSGeorge Bișoc  * to be returned.
984*b22eefacSGeorge Bișoc  *
985*b22eefacSGeorge Bișoc  * @param[in] Amount
986*b22eefacSGeorge Bișoc  * The amount of quotas to return from a process.
987*b22eefacSGeorge Bișoc  *
988*b22eefacSGeorge Bișoc  * @return
989*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS.
990c2c66affSColin Finck  */
991c2c66affSColin Finck NTSTATUS
992c2c66affSColin Finck NTAPI
PsReturnProcessPageFileQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)993*b22eefacSGeorge Bișoc PsReturnProcessPageFileQuota(
994*b22eefacSGeorge Bișoc     _In_ PEPROCESS Process,
995*b22eefacSGeorge Bișoc     _In_ SIZE_T Amount)
996c2c66affSColin Finck {
997c2c66affSColin Finck     /* Don't do anything for the system process */
998c2c66affSColin Finck     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
999c2c66affSColin Finck 
1000*b22eefacSGeorge Bișoc     PspReturnProcessQuotaSpecifiedPool(Process, Process->QuotaBlock, PsPageFile, Amount);
1001c2c66affSColin Finck     return STATUS_SUCCESS;
1002c2c66affSColin Finck }
1003c2c66affSColin Finck 
1004*b22eefacSGeorge Bișoc /**
1005*b22eefacSGeorge Bișoc  * @brief
1006*b22eefacSGeorge Bișoc  * This function adjusts the working set limits
1007*b22eefacSGeorge Bișoc  * of a process and sets up new quota limits
1008*b22eefacSGeorge Bișoc  * when necessary. The function is used
1009*b22eefacSGeorge Bișoc  * when the caller requests to set up
1010*b22eefacSGeorge Bișoc  * new working set sizes.
1011*b22eefacSGeorge Bișoc  *
1012*b22eefacSGeorge Bișoc  * @param[in] Process
1013*b22eefacSGeorge Bișoc  * The process which quota limits or working
1014*b22eefacSGeorge Bișoc  * set sizes are to be changed.
1015*b22eefacSGeorge Bișoc  *
1016*b22eefacSGeorge Bișoc  * @param[in] Unused
1017*b22eefacSGeorge Bișoc  * This parameter is unused.
1018*b22eefacSGeorge Bișoc  *
1019*b22eefacSGeorge Bișoc  * @param[in] QuotaLimits
1020*b22eefacSGeorge Bișoc  * An arbitrary pointer that points to a quota
1021*b22eefacSGeorge Bișoc  * limits structure, needed to determine on
1022*b22eefacSGeorge Bișoc  * setting up new working set sizes.
1023*b22eefacSGeorge Bișoc  *
1024*b22eefacSGeorge Bișoc  * @param[in] QuotaLimitsLength
1025*b22eefacSGeorge Bișoc  * The length of QuotaLimits buffer, which size
1026*b22eefacSGeorge Bișoc  * is expressed in bytes.
1027*b22eefacSGeorge Bișoc  *
1028*b22eefacSGeorge Bișoc  * @param[in] PreviousMode
1029*b22eefacSGeorge Bișoc  * The processor level access mode.
1030*b22eefacSGeorge Bișoc  *
1031*b22eefacSGeorge Bișoc  * @return
1032*b22eefacSGeorge Bișoc  * Returns STATUS_SUCCESS if the function has completed
1033*b22eefacSGeorge Bișoc  * successfully. STATUS_INVALID_PARAMETER is returned if
1034*b22eefacSGeorge Bișoc  * the caller has given a quota limits structure with
1035*b22eefacSGeorge Bișoc  * invalid data. STATUS_INFO_LENGTH_MISMATCH is returned
1036*b22eefacSGeorge Bișoc  * if the length of QuotaLimits pointed by QuotaLimitsLength
1037*b22eefacSGeorge Bișoc  * is not right. STATUS_PRIVILEGE_NOT_HELD is returned if
1038*b22eefacSGeorge Bișoc  * the calling thread of the process doesn't hold the necessary
1039*b22eefacSGeorge Bișoc  * right privilege to increase quotas. STATUS_NO_MEMORY is
1040*b22eefacSGeorge Bișoc  * returned if a memory pool allocation has failed. A failure
1041*b22eefacSGeorge Bișoc  * NTSTATUS code is returned otherwise.
1042*b22eefacSGeorge Bișoc  */
1043c2c66affSColin Finck NTSTATUS
1044c2c66affSColin Finck NTAPI
PspSetQuotaLimits(_In_ PEPROCESS Process,_In_ ULONG Unused,_In_ PVOID QuotaLimits,_In_ ULONG QuotaLimitsLength,_In_ KPROCESSOR_MODE PreviousMode)1045c2c66affSColin Finck PspSetQuotaLimits(
1046c2c66affSColin Finck     _In_ PEPROCESS Process,
1047c2c66affSColin Finck     _In_ ULONG Unused,
1048c2c66affSColin Finck     _In_ PVOID QuotaLimits,
1049c2c66affSColin Finck     _In_ ULONG QuotaLimitsLength,
1050c2c66affSColin Finck     _In_ KPROCESSOR_MODE PreviousMode)
1051c2c66affSColin Finck {
1052c2c66affSColin Finck     QUOTA_LIMITS_EX CapturedQuotaLimits;
1053c2c66affSColin Finck     PEPROCESS_QUOTA_BLOCK QuotaBlock, OldQuotaBlock;
1054c2c66affSColin Finck     BOOLEAN IncreaseOkay;
1055c2c66affSColin Finck     KAPC_STATE SavedApcState;
1056c2c66affSColin Finck     NTSTATUS Status;
1057c2c66affSColin Finck 
1058c2c66affSColin Finck     UNREFERENCED_PARAMETER(Unused);
1059c2c66affSColin Finck 
1060c2c66affSColin Finck     _SEH2_TRY
1061c2c66affSColin Finck     {
1062c2c66affSColin Finck         ProbeForRead(QuotaLimits, QuotaLimitsLength, sizeof(ULONG));
1063c2c66affSColin Finck 
1064c2c66affSColin Finck         /* Check if we have the basic or extended structure */
1065c2c66affSColin Finck         if (QuotaLimitsLength == sizeof(QUOTA_LIMITS))
1066c2c66affSColin Finck         {
1067c2c66affSColin Finck             /* Copy the basic structure, zero init the remaining fields */
1068c2c66affSColin Finck             RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS));
1069c2c66affSColin Finck             CapturedQuotaLimits.WorkingSetLimit = 0;
1070c2c66affSColin Finck             CapturedQuotaLimits.Reserved2 = 0;
1071c2c66affSColin Finck             CapturedQuotaLimits.Reserved3 = 0;
1072c2c66affSColin Finck             CapturedQuotaLimits.Reserved4 = 0;
1073c2c66affSColin Finck             CapturedQuotaLimits.CpuRateLimit.RateData = 0;
1074c2c66affSColin Finck             CapturedQuotaLimits.Flags = 0;
1075c2c66affSColin Finck         }
1076c2c66affSColin Finck         else if (QuotaLimitsLength == sizeof(QUOTA_LIMITS_EX))
1077c2c66affSColin Finck         {
1078c2c66affSColin Finck             /* Copy the full structure */
1079c2c66affSColin Finck             RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS_EX));
1080c2c66affSColin Finck 
1081c2c66affSColin Finck             /* Verify that the caller passed valid flags */
1082c2c66affSColin Finck             if ((CapturedQuotaLimits.Flags & ~VALID_QUOTA_FLAGS) ||
1083c2c66affSColin Finck                 ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_ENABLE) &&
1084c2c66affSColin Finck                  (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_DISABLE)) ||
1085c2c66affSColin Finck                 ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_ENABLE) &&
1086c2c66affSColin Finck                  (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_DISABLE)))
1087c2c66affSColin Finck             {
1088c2c66affSColin Finck                 DPRINT1("Invalid quota flags: 0x%lx\n", CapturedQuotaLimits.Flags);
1089c2c66affSColin Finck                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
1090c2c66affSColin Finck             }
1091c2c66affSColin Finck 
1092c2c66affSColin Finck             /* Verify that the caller didn't pass reserved values */
1093c2c66affSColin Finck             if ((CapturedQuotaLimits.WorkingSetLimit != 0) ||
1094c2c66affSColin Finck                 (CapturedQuotaLimits.Reserved2 != 0) ||
1095c2c66affSColin Finck                 (CapturedQuotaLimits.Reserved3 != 0) ||
1096c2c66affSColin Finck                 (CapturedQuotaLimits.Reserved4 != 0) ||
1097c2c66affSColin Finck                 (CapturedQuotaLimits.CpuRateLimit.RateData != 0))
1098c2c66affSColin Finck             {
1099c2c66affSColin Finck                 DPRINT1("Invalid value: (%lx,%lx,%lx,%lx,%lx)\n",
1100c2c66affSColin Finck                         CapturedQuotaLimits.WorkingSetLimit,
1101c2c66affSColin Finck                         CapturedQuotaLimits.Reserved2,
1102c2c66affSColin Finck                         CapturedQuotaLimits.Reserved3,
1103c2c66affSColin Finck                         CapturedQuotaLimits.Reserved4,
1104c2c66affSColin Finck                         CapturedQuotaLimits.CpuRateLimit.RateData);
1105c2c66affSColin Finck                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
1106c2c66affSColin Finck             }
1107c2c66affSColin Finck         }
1108c2c66affSColin Finck         else
1109c2c66affSColin Finck         {
1110c2c66affSColin Finck             DPRINT1("Invalid quota size: 0x%lx\n", QuotaLimitsLength);
1111c2c66affSColin Finck             _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
1112c2c66affSColin Finck         }
1113c2c66affSColin Finck     }
1114c2c66affSColin Finck     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1115c2c66affSColin Finck     {
1116c2c66affSColin Finck         DPRINT1("Exception while copying data\n");
1117c2c66affSColin Finck         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1118c2c66affSColin Finck     }
1119c2c66affSColin Finck     _SEH2_END;
1120c2c66affSColin Finck 
1121c2c66affSColin Finck     /* Check the caller changes the working set size limits */
1122c2c66affSColin Finck     if ((CapturedQuotaLimits.MinimumWorkingSetSize != 0) &&
1123c2c66affSColin Finck         (CapturedQuotaLimits.MaximumWorkingSetSize != 0))
1124c2c66affSColin Finck     {
1125c2c66affSColin Finck         /* Check for special case: trimming the WS */
1126c2c66affSColin Finck         if ((CapturedQuotaLimits.MinimumWorkingSetSize == SIZE_T_MAX) &&
1127c2c66affSColin Finck             (CapturedQuotaLimits.MaximumWorkingSetSize == SIZE_T_MAX))
1128c2c66affSColin Finck         {
1129c2c66affSColin Finck             /* No increase allowed */
1130c2c66affSColin Finck             IncreaseOkay = FALSE;
1131c2c66affSColin Finck         }
1132c2c66affSColin Finck         else
1133c2c66affSColin Finck         {
1134c2c66affSColin Finck             /* Check if the caller has the required privilege */
1135c2c66affSColin Finck             IncreaseOkay = SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege,
1136c2c66affSColin Finck                                                   PreviousMode);
1137c2c66affSColin Finck         }
1138c2c66affSColin Finck 
1139c2c66affSColin Finck         /* Attach to the target process and disable APCs */
1140c2c66affSColin Finck         KeStackAttachProcess(&Process->Pcb, &SavedApcState);
1141c2c66affSColin Finck         KeEnterGuardedRegion();
1142c2c66affSColin Finck 
1143c2c66affSColin Finck         /* Call Mm to adjust the process' working set size */
1144c2c66affSColin Finck         Status = MmAdjustWorkingSetSize(CapturedQuotaLimits.MinimumWorkingSetSize,
1145c2c66affSColin Finck                                         CapturedQuotaLimits.MaximumWorkingSetSize,
1146c2c66affSColin Finck                                         0,
1147c2c66affSColin Finck                                         IncreaseOkay);
1148c2c66affSColin Finck 
1149c2c66affSColin Finck         /* Bring back APCs and detach from the process */
1150c2c66affSColin Finck         KeLeaveGuardedRegion();
1151c2c66affSColin Finck         KeUnstackDetachProcess(&SavedApcState);
1152c2c66affSColin Finck     }
1153c2c66affSColin Finck     else if (Process->QuotaBlock == &PspDefaultQuotaBlock)
1154c2c66affSColin Finck     {
1155c2c66affSColin Finck         /* Check if the caller has the required privilege */
1156c2c66affSColin Finck         if (!SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege, PreviousMode))
1157c2c66affSColin Finck         {
1158c2c66affSColin Finck             return STATUS_PRIVILEGE_NOT_HELD;
1159c2c66affSColin Finck         }
1160c2c66affSColin Finck 
1161c2c66affSColin Finck         /* Allocate a new quota block */
1162c2c66affSColin Finck         QuotaBlock = ExAllocatePoolWithTag(NonPagedPool,
1163c2c66affSColin Finck                                            sizeof(EPROCESS_QUOTA_BLOCK),
1164c2c66affSColin Finck                                            TAG_QUOTA_BLOCK);
1165c2c66affSColin Finck         if (QuotaBlock == NULL)
1166c2c66affSColin Finck         {
1167c2c66affSColin Finck             ObDereferenceObject(Process);
1168c2c66affSColin Finck             return STATUS_NO_MEMORY;
1169c2c66affSColin Finck         }
1170c2c66affSColin Finck 
1171c2c66affSColin Finck         /* Initialize the quota block */
1172c2c66affSColin Finck         QuotaBlock->ReferenceCount = 1;
1173c2c66affSColin Finck         QuotaBlock->ProcessCount = 1;
11746170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsNonPagedPool].Peak = Process->QuotaPeak[PsNonPagedPool];
11756170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsPagedPool].Peak = Process->QuotaPeak[PsPagedPool];
11766170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsPageFile].Peak = Process->QuotaPeak[PsPageFile];
11776170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsNonPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit;
11786170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit;
11796170b574SGeorge Bișoc         QuotaBlock->QuotaEntry[PsPageFile].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit;
1180c2c66affSColin Finck 
1181c2c66affSColin Finck         /* Try to exchange the quota block, if that failed, just drop it */
1182c2c66affSColin Finck         OldQuotaBlock = InterlockedCompareExchangePointer((PVOID*)&Process->QuotaBlock,
1183c2c66affSColin Finck                                                           QuotaBlock,
1184c2c66affSColin Finck                                                           &PspDefaultQuotaBlock);
1185c2c66affSColin Finck         if (OldQuotaBlock == &PspDefaultQuotaBlock)
1186c2c66affSColin Finck         {
1187c2c66affSColin Finck             /* Success, insert the new quota block */
1188c2c66affSColin Finck             PspInsertQuotaBlock(QuotaBlock);
1189c2c66affSColin Finck         }
1190c2c66affSColin Finck         else
1191c2c66affSColin Finck         {
1192c2c66affSColin Finck             /* Failed, free the quota block and ignore it */
1193c2c66affSColin Finck             ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
1194c2c66affSColin Finck         }
1195c2c66affSColin Finck 
1196c2c66affSColin Finck         Status = STATUS_SUCCESS;
1197c2c66affSColin Finck     }
119892b8d327STimo Kreuzer     else
119992b8d327STimo Kreuzer     {
120092b8d327STimo Kreuzer         Status = STATUS_SUCCESS;
120192b8d327STimo Kreuzer     }
1202c2c66affSColin Finck 
1203c2c66affSColin Finck     return Status;
1204c2c66affSColin Finck }
1205c2c66affSColin Finck 
1206c2c66affSColin Finck /* EOF */
1207