Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1291 lines
30 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. psquota.c
  5. Abstract:
  6. This module implements the quota mechanism for NT
  7. Author:
  8. Mark Lucovsky (markl) 18-Sep-1989
  9. Revision History:
  10. Neill Clift (NeillC) 4-Nov-2000
  11. Changed to be mostly lock free. Preserved the basic design in terms of how quota is managed.
  12. --*/
  13. #include "psp.h"
  14. LIST_ENTRY PspQuotaBlockList; // List of all quota blocks except the default
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text (INIT, PsInitializeQuotaSystem)
  17. #pragma alloc_text (PAGE, PspInheritQuota)
  18. #pragma alloc_text (PAGE, PspDereferenceQuota)
  19. #pragma alloc_text (PAGE, PsChargeSharedPoolQuota)
  20. #pragma alloc_text (PAGE, PsReturnSharedPoolQuota)
  21. #endif
  22. VOID
  23. PsInitializeQuotaSystem (
  24. VOID
  25. )
  26. /*++
  27. Routine Description:
  28. This function initializes the quota system.
  29. Arguments:
  30. None.
  31. Return Value:
  32. None.
  33. --*/
  34. {
  35. KeInitializeSpinLock(&PspQuotaLock);
  36. PspDefaultQuotaBlock.ReferenceCount = 1;
  37. PspDefaultQuotaBlock.ProcessCount = 1;
  38. PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit = (SIZE_T)-1;
  39. PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit = (SIZE_T)-1;
  40. PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit = (SIZE_T)-1;
  41. PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock;
  42. InitializeListHead (&PspQuotaBlockList);
  43. }
  44. VOID
  45. PspInsertQuotaBlock (
  46. IN PEPROCESS_QUOTA_BLOCK QuotaBlock
  47. )
  48. /*++
  49. Routine Description:
  50. This routines as a new quota block to the global list of system quota blocks.
  51. Arguments:
  52. QuotaBlock - Quota block to be inserted into the list.
  53. Return Value:
  54. None.
  55. --*/
  56. {
  57. KIRQL OldIrql;
  58. ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
  59. InsertTailList (&PspQuotaBlockList, &QuotaBlock->QuotaList);
  60. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  61. }
  62. VOID
  63. PspDereferenceQuotaBlock (
  64. IN PEPROCESS_QUOTA_BLOCK QuotaBlock
  65. )
  66. /*++
  67. Routine Description:
  68. This removes a single reference from a quota block and deletes the block if it was the last.
  69. Arguments:
  70. QuotaBlock - Quota block to dereference
  71. Return Value:
  72. None.
  73. --*/
  74. {
  75. KIRQL OldIrql;
  76. SIZE_T ReturnQuota;
  77. PS_QUOTA_TYPE QuotaType;
  78. if (InterlockedDecrement ((PLONG) &QuotaBlock->ReferenceCount) == 0) {
  79. ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
  80. RemoveEntryList (&QuotaBlock->QuotaList);
  81. //
  82. // Free any unreturned quota;
  83. //
  84. for (QuotaType = PsNonPagedPool;
  85. QuotaType <= PsPagedPool;
  86. QuotaType++) {
  87. ReturnQuota = QuotaBlock->QuotaEntry[QuotaType].Return + QuotaBlock->QuotaEntry[QuotaType].Limit;
  88. if (ReturnQuota > 0) {
  89. MmReturnPoolQuota (QuotaType, ReturnQuota);
  90. }
  91. }
  92. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  93. ExFreePool (QuotaBlock);
  94. }
  95. }
  96. SIZE_T
  97. FORCEINLINE
  98. PspInterlockedExchangeQuota (
  99. IN PSIZE_T pQuota,
  100. IN SIZE_T NewQuota)
  101. /*++
  102. Routine Description:
  103. This function does an interlocked exchange on a quota variable.
  104. Arguments:
  105. pQuota - Pointer to a quota entry to exchange into
  106. NewQuota - The new value to exchange into the quota location.
  107. Return Value:
  108. SIZE_T - Old value that was contained in the quota variable
  109. --*/
  110. {
  111. #if !defined(_WIN64)
  112. return InterlockedExchange ((PLONG) pQuota, NewQuota);
  113. #else
  114. return InterlockedExchange64 ((PLONGLONG) pQuota, NewQuota);
  115. #endif
  116. }
  117. SIZE_T
  118. FORCEINLINE
  119. PspInterlockedCompareExchangeQuota (
  120. IN PSIZE_T pQuota,
  121. IN SIZE_T NewQuota,
  122. IN SIZE_T OldQuota
  123. )
  124. /*++
  125. Routine Description:
  126. This function performs a compare exchange operation on a quota variable
  127. Arguments:
  128. pQuota - Pointer to the quota variable being changed
  129. NewQuota - New value to place in the quota variable
  130. OldQuota - The current contents of the quota variable
  131. Return Value:
  132. SIZE_T - The old contents of the variable
  133. --*/
  134. {
  135. #if !defined(_WIN64)
  136. return InterlockedCompareExchange ((PLONG) pQuota, NewQuota, OldQuota);
  137. #else
  138. return InterlockedCompareExchange64 ((PLONGLONG)pQuota, NewQuota, OldQuota);
  139. #endif
  140. }
  141. SIZE_T
  142. PspReleaseReturnedQuota (
  143. IN PS_QUOTA_TYPE QuotaType
  144. )
  145. /*++
  146. Routine Description:
  147. This function walks the list of system quota blocks and returns any non-returned quota.
  148. This function is called when we are about to fail a quota charge and we want to try and
  149. free some resources up.
  150. Arguments:
  151. QuotaType - Type of quota to scan for.
  152. Return Value:
  153. SIZE_T - Amount of that quota returned to the system.
  154. --*/
  155. {
  156. SIZE_T ReturnQuota, Usage, Limit;
  157. PLIST_ENTRY ListEntry;
  158. PEPROCESS_QUOTA_BLOCK QuotaBlock;
  159. ReturnQuota = 0;
  160. ListEntry = PspQuotaBlockList.Flink;
  161. while (1) {
  162. if (ListEntry == &PspQuotaBlockList) {
  163. break;
  164. }
  165. QuotaBlock = CONTAINING_RECORD (ListEntry, EPROCESS_QUOTA_BLOCK, QuotaList);
  166. //
  167. // Gather up any unreturned quota;
  168. //
  169. ReturnQuota += PspInterlockedExchangeQuota (&QuotaBlock->QuotaEntry[QuotaType].Return, 0);
  170. //
  171. // If no more processes are assocociated with this block then trim its limit back. This
  172. // block can only have quota returned at this point.
  173. //
  174. if (QuotaBlock->ProcessCount == 0) {
  175. Usage = QuotaBlock->QuotaEntry[QuotaType].Usage;
  176. Limit = QuotaBlock->QuotaEntry[QuotaType].Limit;
  177. if (Limit > Usage) {
  178. if (PspInterlockedCompareExchangeQuota (&QuotaBlock->QuotaEntry[QuotaType].Limit,
  179. Usage,
  180. Limit) == Limit) {
  181. ReturnQuota += Limit - Usage;
  182. }
  183. }
  184. }
  185. ListEntry = ListEntry->Flink;
  186. }
  187. if (ReturnQuota > 0) {
  188. MmReturnPoolQuota (QuotaType, ReturnQuota);
  189. }
  190. return ReturnQuota;
  191. }
  192. //
  193. // Interfaces return different status values for differen quotas. These are the values.
  194. //
  195. const static NTSTATUS PspQuotaStatus[PsQuotaTypes] = {STATUS_QUOTA_EXCEEDED,
  196. STATUS_QUOTA_EXCEEDED,
  197. STATUS_PAGEFILE_QUOTA_EXCEEDED};
  198. VOID
  199. FORCEINLINE
  200. PspInterlockedMaxQuota (
  201. IN PSIZE_T pQuota,
  202. IN SIZE_T NewQuota
  203. )
  204. /*++
  205. Routine Description:
  206. This function makes sure that the target contains a value that >= to the new quota value.
  207. This is used to maintain peak values.
  208. Arguments:
  209. pQuota - Pointer to a quota variable
  210. NewQuota - New value to be used in the maximum comparison.
  211. Return Value:
  212. None.
  213. --*/
  214. {
  215. SIZE_T Quota;
  216. Quota = *pQuota;
  217. while (1) {
  218. if (NewQuota <= Quota) {
  219. break;
  220. }
  221. //
  222. // This looks strange because we don't care if the exchanged suceeded. We only
  223. // care that the quota is greater than our new quota.
  224. //
  225. Quota = PspInterlockedCompareExchangeQuota (pQuota,
  226. NewQuota,
  227. Quota);
  228. }
  229. }
  230. SIZE_T
  231. FORCEINLINE
  232. PspInterlockedAddQuota (
  233. IN PSIZE_T pQuota,
  234. IN SIZE_T Amount
  235. )
  236. /*++
  237. Routine Description:
  238. This function adds the specified amount on to the target quota
  239. Arguments:
  240. pQuota - Pointer to a quota variable to be modified
  241. Amount - Amount to be added to the quota
  242. Return Value:
  243. SIZE_T - New value of quota variable after the addition was performed
  244. --*/
  245. {
  246. #if !defined(_WIN64)
  247. return InterlockedExchangeAdd ((PLONG) pQuota, Amount) + Amount;
  248. #else
  249. SIZE_T Quota, NewQuota, tQuota;
  250. Quota = *pQuota;
  251. while (1) {
  252. NewQuota = Quota + Amount;
  253. tQuota = InterlockedCompareExchange64 ((PLONGLONG) pQuota,
  254. NewQuota,
  255. Quota);
  256. if (tQuota == Quota) {
  257. return NewQuota;
  258. }
  259. Quota = tQuota;
  260. }
  261. #endif
  262. }
  263. SIZE_T
  264. FORCEINLINE
  265. PspInterlockedSubtractQuota (
  266. IN PSIZE_T pUsage,
  267. IN SIZE_T Amount
  268. )
  269. /*++
  270. Routine Description:
  271. This function subtracts the specified amount on to the target quota
  272. Arguments:
  273. pQuota - Pointer to a quota variable to be modified
  274. Amount - Amount to be subtracted from the quota
  275. Return Value:
  276. SIZE_T - New value of quota variable after the subtraction was performed
  277. --*/
  278. {
  279. #if !defined(_WIN64)
  280. return InterlockedExchangeAdd ((PLONG) pUsage, -(LONG)Amount) - Amount;
  281. #else
  282. SIZE_T Usage, NewUsage, tUsage;
  283. Usage = *pUsage;
  284. while (1) {
  285. NewUsage = Usage - Amount;
  286. tUsage = InterlockedCompareExchange64 ((PLONGLONG) pUsage,
  287. NewUsage,
  288. Usage);
  289. if (tUsage == Usage) {
  290. return NewUsage;
  291. }
  292. Usage = tUsage;
  293. }
  294. #endif
  295. }
  296. BOOLEAN
  297. PspExpandQuota (
  298. IN PS_QUOTA_TYPE QuotaType,
  299. IN PEPROCESS_QUOTA_ENTRY QE,
  300. IN SIZE_T Usage,
  301. IN SIZE_T Amount,
  302. OUT SIZE_T *pLimit
  303. )
  304. /*++
  305. Routine Description:
  306. This function charges the specified quota to a process quota block
  307. Arguments:
  308. QuotaType - The quota being charged. One of PsNonPagedPool, PsPagedPool or PsPageFile.
  309. QE - Quota entry being modified
  310. Usage - The current quota usage
  311. Amount - The amount of quota being charged.
  312. pLimit - The new limit
  313. Return Value:
  314. BOOLEAN - TRUE if quota expansion suceeded.
  315. --*/
  316. {
  317. SIZE_T Limit, NewLimit;
  318. KIRQL OldIrql;
  319. //
  320. // We need to attempt quota expansion for this request.
  321. // Acquire the global lock and see if somebody else changed the limit.
  322. // We don't want to do too many expansions. If somebody else did it
  323. // then we want to use theirs if possible.
  324. //
  325. ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
  326. //
  327. // Refetch limit information. Another thread may have done limit expansion/contraction.
  328. // By refetching limit we preserve the order we established above.
  329. //
  330. Limit = QE->Limit;
  331. //
  332. // If the request could be satisfied now then repeat.
  333. //
  334. if (Usage + Amount <= Limit) {
  335. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  336. *pLimit = Limit;
  337. return TRUE;
  338. }
  339. //
  340. // If expansion is currently enabled then attempt it.
  341. // If this fails then scavenge any returns from all the
  342. // quota blocks in the system and try again.
  343. //
  344. if (((QuotaType == PsNonPagedPool)?PspDefaultNonPagedLimit:PspDefaultPagedLimit) == 0) {
  345. if (MmRaisePoolQuota (QuotaType, Limit, &NewLimit) ||
  346. (PspReleaseReturnedQuota (QuotaType) > 0 &&
  347. MmRaisePoolQuota (QuotaType, Limit, &NewLimit))) {
  348. //
  349. // We refetch limit here but that doesn't violate the ordering
  350. //
  351. Limit = PspInterlockedAddQuota (&QE->Limit, NewLimit - Limit);
  352. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  353. *pLimit = Limit;
  354. return TRUE;
  355. }
  356. }
  357. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  358. *pLimit = Limit;
  359. return FALSE;
  360. }
  361. NTSTATUS
  362. FORCEINLINE
  363. PspChargeQuota (
  364. IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
  365. IN PEPROCESS Process,
  366. IN PS_QUOTA_TYPE QuotaType,
  367. IN SIZE_T Amount)
  368. /*++
  369. Routine Description:
  370. This function charges the specified quota to a process quota block
  371. Arguments:
  372. QuotaBlock - Quota block to make charges to.
  373. Process - Process that is being charged.
  374. QuotaType - The quota being charged. One of PsNonPagedPool, PsPagedPool or PsPageFile.
  375. Amount - The amount of quota being charged.
  376. Return Value:
  377. NTSTATUS - Status of the operation
  378. --*/
  379. {
  380. PEPROCESS_QUOTA_ENTRY QE;
  381. SIZE_T Usage, Limit, NewUsage, tUsage, Extra;
  382. QE = &QuotaBlock->QuotaEntry[QuotaType];
  383. //
  384. // This memory barrier is important. In order not to have to recheck the limit after
  385. // we charge the quota we only ever reduce the limit by the same amount we are about
  386. // reduce the usage by. Using an out of data limit will only allow us to over charge
  387. // by an amount another thread is just about to release.
  388. //
  389. Usage = *(volatile SIZE_T *)&QE->Usage;
  390. KeMemoryBarrier ();
  391. Limit = *(volatile SIZE_T *)&QE->Limit;
  392. while (1) {
  393. NewUsage = Usage + Amount;
  394. //
  395. // Wrapping cases are always rejected
  396. //
  397. if (NewUsage < Usage) {
  398. return PspQuotaStatus [QuotaType];
  399. }
  400. //
  401. // If its within the limits then try and grab the quota
  402. //
  403. if (NewUsage <= Limit) {
  404. tUsage = PspInterlockedCompareExchangeQuota (&QE->Usage,
  405. NewUsage,
  406. Usage);
  407. if (tUsage == Usage) {
  408. //
  409. // Update the Peak value
  410. //
  411. PspInterlockedMaxQuota (&QE->Peak, NewUsage);
  412. //
  413. // Update the process counts if needed
  414. //
  415. if (Process != NULL) {
  416. NewUsage = PspInterlockedAddQuota (&Process->QuotaUsage[QuotaType], Amount);
  417. //
  418. // Update the peak value
  419. //
  420. PspInterlockedMaxQuota (&Process->QuotaPeak[QuotaType], NewUsage);
  421. }
  422. return STATUS_SUCCESS;
  423. }
  424. //
  425. // The usage has changed under us. We have a new usage from the exchange
  426. // but must refetch the limit to preserve the ordering we established
  427. // above this loop. We don't need a memory barrier as we obtained
  428. // the new value via an interlocked operation and they contain barriers.
  429. //
  430. Usage = tUsage;
  431. Limit = *(volatile SIZE_T *) &QE->Limit;
  432. continue;
  433. }
  434. //
  435. // Page file quota is not increased
  436. //
  437. if (QuotaType == PsPageFile) {
  438. return PspQuotaStatus [QuotaType];
  439. } else {
  440. //
  441. // First try and grab any returns that this process has made.
  442. //
  443. Extra = PspInterlockedExchangeQuota (&QE->Return, 0);
  444. if (Extra > 0) {
  445. //
  446. // We had some returns so add this to the limit. We can retry the
  447. // acquire with the new limit. We refetch the limit here but that
  448. // doesn't violate the state we set up at the top of the loop.
  449. // The state is that we read the Usage before we read the limit.
  450. //
  451. Limit = PspInterlockedAddQuota (&QE->Limit, Extra);
  452. continue;
  453. }
  454. //
  455. // Try to expand quota if we can
  456. //
  457. if (PspExpandQuota (QuotaType, QE, Usage, Amount, &Limit)) {
  458. //
  459. // We refetched limit here but that doesn't violate the ordering
  460. //
  461. continue;
  462. }
  463. return PspQuotaStatus [QuotaType];
  464. }
  465. }
  466. }
  467. VOID
  468. PspGivebackQuota (
  469. IN PS_QUOTA_TYPE QuotaType,
  470. IN PEPROCESS_QUOTA_ENTRY QE
  471. )
  472. /*++
  473. Routine Description:
  474. This function returns excess freed quota to MM
  475. Arguments:
  476. QuotaType - The quota being returned. One of PsNonPagedPool, PsPagedPool or PsPageFile.
  477. QE - Quote entry to return to
  478. Return Value:
  479. None.
  480. --*/
  481. {
  482. SIZE_T GiveBack;
  483. KIRQL OldIrql;
  484. //
  485. // Acquire a global spinlock so we only have one thread giving back to the system
  486. //
  487. ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
  488. GiveBack = PspInterlockedExchangeQuota (&QE->Return, 0);
  489. if (GiveBack > 0) {
  490. MmReturnPoolQuota (QuotaType, GiveBack);
  491. }
  492. ExReleaseSpinLock (&PspQuotaLock, OldIrql);
  493. }
  494. VOID
  495. FORCEINLINE
  496. PspReturnQuota (
  497. IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
  498. IN PEPROCESS Process,
  499. IN PS_QUOTA_TYPE QuotaType,
  500. IN SIZE_T Amount)
  501. /*++
  502. Routine Description:
  503. This function returns previously charged quota to the quota block
  504. Arguments:
  505. QuotaBlock - Quota block to return charges to.
  506. Process - Process that was originaly charged.
  507. QuotaType - The quota being returned. One of PsNonPagedPool, PsPagedPool or PsPageFile.
  508. Amount - The amount of quota being returned.
  509. Return Value:
  510. None.
  511. --*/
  512. {
  513. PEPROCESS_QUOTA_ENTRY QE;
  514. SIZE_T Usage, NewUsage, tUsage, tAmount, rAmount, Limit, NewLimit, tLimit;
  515. SIZE_T GiveBackLimit, GiveBack;
  516. QE = &QuotaBlock->QuotaEntry[QuotaType];
  517. Usage = QE->Usage;
  518. Limit = QE->Limit;
  519. //
  520. // We need to give back quota here if we have lots to return.
  521. //
  522. #define PSMINGIVEBACK ((MMPAGED_QUOTA_INCREASE > MMNONPAGED_QUOTA_INCREASE)?MMNONPAGED_QUOTA_INCREASE:MMPAGED_QUOTA_INCREASE)
  523. if (Limit - Usage > PSMINGIVEBACK && Limit > Usage) {
  524. if (QuotaType != PsPageFile && QuotaBlock != &PspDefaultQuotaBlock && PspDoingGiveBacks) {
  525. if (QuotaType == PsPagedPool) {
  526. GiveBackLimit = MMPAGED_QUOTA_INCREASE;
  527. } else {
  528. GiveBackLimit = MMNONPAGED_QUOTA_INCREASE;
  529. }
  530. if (GiveBackLimit > Amount) {
  531. GiveBack = Amount;
  532. } else {
  533. GiveBack = GiveBackLimit;
  534. }
  535. NewLimit = Limit - GiveBack;
  536. tLimit = PspInterlockedCompareExchangeQuota (&QE->Limit,
  537. NewLimit,
  538. Limit);
  539. if (tLimit == Limit) {
  540. //
  541. // We suceeded in shrinking the limit. Add this reduction to the return field.
  542. // If returns exceed a threshhold then give the lot bacxk to MM.
  543. //
  544. GiveBack = PspInterlockedAddQuota (&QE->Return, GiveBack);
  545. if (GiveBack > GiveBackLimit) {
  546. PspGivebackQuota (QuotaType, QE);
  547. }
  548. }
  549. }
  550. }
  551. //
  552. // Now return the quota to the usage field.
  553. // The charge might have been split across the default quota block and
  554. // a new quota block. We have to handle this case here by first returning
  555. // quota to the specified quota block then skipping to the default.
  556. //
  557. rAmount = Amount;
  558. while (1) {
  559. if (rAmount > Usage) {
  560. tAmount = Usage;
  561. NewUsage = 0;
  562. } else {
  563. tAmount = rAmount;
  564. NewUsage = Usage - rAmount;
  565. }
  566. tUsage = PspInterlockedCompareExchangeQuota (&QE->Usage,
  567. NewUsage,
  568. Usage);
  569. if (tUsage == Usage) {
  570. //
  571. // Update the process counts if needed
  572. //
  573. if (Process != NULL) {
  574. ASSERT (tAmount <= Process->QuotaUsage[QuotaType]);
  575. NewUsage = PspInterlockedSubtractQuota (&Process->QuotaUsage[QuotaType], tAmount);
  576. }
  577. rAmount = rAmount - tAmount;
  578. if (rAmount == 0) {
  579. return;
  580. }
  581. ASSERT (QuotaBlock != &PspDefaultQuotaBlock);
  582. if (QuotaBlock == &PspDefaultQuotaBlock) {
  583. return;
  584. }
  585. QuotaBlock = &PspDefaultQuotaBlock;
  586. QE = &QuotaBlock->QuotaEntry[QuotaType];
  587. Usage = QE->Usage;
  588. } else {
  589. Usage = tUsage;
  590. }
  591. }
  592. }
  593. PEPROCESS_QUOTA_BLOCK
  594. PsChargeSharedPoolQuota(
  595. IN PEPROCESS Process,
  596. IN SIZE_T PagedAmount,
  597. IN SIZE_T NonPagedAmount
  598. )
  599. /*++
  600. Routine Description:
  601. This function charges shared pool quota of the specified pool type
  602. to the specified process's pooled quota block. If the quota charge
  603. would exceed the limits allowed to the process, then an exception is
  604. raised and quota is not charged.
  605. Arguments:
  606. Process - Supplies the process to charge quota to.
  607. PagedAmount - Supplies the amount of paged pool quota to charge.
  608. PagedAmount - Supplies the amount of non paged pool quota to charge.
  609. Return Value:
  610. NULL - Quota was exceeded
  611. NON-NULL - A referenced pointer to the quota block that was charged
  612. --*/
  613. {
  614. PEPROCESS_QUOTA_BLOCK QuotaBlock;
  615. NTSTATUS Status;
  616. ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
  617. if (Process == PsInitialSystemProcess) {
  618. return (PEPROCESS_QUOTA_BLOCK) 1;
  619. }
  620. QuotaBlock = Process->QuotaBlock;
  621. if (PagedAmount > 0) {
  622. Status = PspChargeQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
  623. if (!NT_SUCCESS (Status)) {
  624. return NULL;
  625. }
  626. }
  627. if (NonPagedAmount > 0) {
  628. Status = PspChargeQuota (QuotaBlock, NULL, PsNonPagedPool, NonPagedAmount);
  629. if (!NT_SUCCESS (Status)) {
  630. if (PagedAmount > 0) {
  631. PspReturnQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
  632. }
  633. return NULL;
  634. }
  635. }
  636. InterlockedIncrement ((PLONG) &QuotaBlock->ReferenceCount);
  637. return QuotaBlock;
  638. }
  639. VOID
  640. PsReturnSharedPoolQuota(
  641. IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
  642. IN SIZE_T PagedAmount,
  643. IN SIZE_T NonPagedAmount
  644. )
  645. /*++
  646. Routine Description:
  647. This function returns pool quota of the specified pool type to the
  648. specified process.
  649. Arguments:
  650. QuotaBlock - Supplies the quota block to return quota to.
  651. PagedAmount - Supplies the amount of paged pool quota to return.
  652. PagedAmount - Supplies the amount of non paged pool quota to return.
  653. Return Value:
  654. None.
  655. --*/
  656. {
  657. //
  658. // if we bypassed the quota charge, don't do anything here either
  659. //
  660. if (QuotaBlock == (PEPROCESS_QUOTA_BLOCK) 1) {
  661. return;
  662. }
  663. if (PagedAmount > 0) {
  664. PspReturnQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
  665. }
  666. if (NonPagedAmount > 0) {
  667. PspReturnQuota (QuotaBlock, NULL, PsNonPagedPool, NonPagedAmount);
  668. }
  669. PspDereferenceQuotaBlock (QuotaBlock);
  670. }
  671. VOID
  672. PsChargePoolQuota(
  673. IN PEPROCESS Process,
  674. IN POOL_TYPE PoolType,
  675. IN SIZE_T Amount
  676. )
  677. /*++
  678. Routine Description:
  679. This function charges pool quota of the specified pool type to
  680. the specified process. If the quota charge would exceed the limits
  681. allowed to the process, then an exception is raised and quota is
  682. not charged.
  683. Arguments:
  684. Process - Supplies the process to charge quota to.
  685. PoolType - Supplies the type of pool quota to charge.
  686. Amount - Supplies the amount of pool quota to charge.
  687. Return Value:
  688. Raises STATUS_QUOTA_EXCEEDED if the quota charge would exceed the
  689. limits allowed to the process.
  690. --*/
  691. {
  692. NTSTATUS Status;
  693. Status = PsChargeProcessPoolQuota (Process,
  694. PoolType,
  695. Amount);
  696. if (!NT_SUCCESS (Status)) {
  697. ExRaiseStatus (Status);
  698. }
  699. }
  700. NTSTATUS
  701. PsChargeProcessPoolQuota(
  702. IN PEPROCESS Process,
  703. IN POOL_TYPE PoolType,
  704. IN SIZE_T Amount
  705. )
  706. /*++
  707. Routine Description:
  708. This function charges pool quota of the specified pool type to
  709. the specified process. If the quota charge would exceed the limits
  710. allowed to the process, then an exception is raised and quota is
  711. not charged.
  712. Arguments:
  713. Process - Supplies the process to charge quota to.
  714. PoolType - Supplies the type of pool quota to charge.
  715. Amount - Supplies the amount of pool quota to charge.
  716. Return Value:
  717. NTSTATUS - Status of operation
  718. --*/
  719. {
  720. ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
  721. ASSERT (PoolType == PagedPool || PoolType == NonPagedPool);
  722. __assume (PoolType == PagedPool || PoolType == NonPagedPool);
  723. if (Process == PsInitialSystemProcess) {
  724. return STATUS_SUCCESS;
  725. }
  726. return PspChargeQuota (Process->QuotaBlock, Process, PoolType, Amount);
  727. }
  728. VOID
  729. PsReturnPoolQuota(
  730. IN PEPROCESS Process,
  731. IN POOL_TYPE PoolType,
  732. IN SIZE_T Amount
  733. )
  734. /*++
  735. Routine Description:
  736. This function returns pool quota of the specified pool type to the
  737. specified process.
  738. Arguments:
  739. Process - Supplies the process to return quota to.
  740. PoolType - Supplies the type of pool quota to return.
  741. Amount - Supplies the amount of pool quota to return
  742. Return Value:
  743. Raises STATUS_QUOTA_EXCEEDED if the quota charge would exceed the
  744. limits allowed to the process.
  745. --*/
  746. {
  747. ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
  748. ASSERT (PoolType == PagedPool || PoolType == NonPagedPool);
  749. __assume (PoolType == PagedPool || PoolType == NonPagedPool);
  750. if (Process == PsInitialSystemProcess) {
  751. return;
  752. }
  753. PspReturnQuota (Process->QuotaBlock, Process, PoolType, Amount);
  754. return;
  755. }
  756. VOID
  757. PspInheritQuota(
  758. IN PEPROCESS NewProcess,
  759. IN PEPROCESS ParentProcess
  760. )
  761. {
  762. PEPROCESS_QUOTA_BLOCK QuotaBlock;
  763. if (ParentProcess) {
  764. QuotaBlock = ParentProcess->QuotaBlock;
  765. } else {
  766. QuotaBlock = &PspDefaultQuotaBlock;
  767. }
  768. InterlockedIncrement ((PLONG) &QuotaBlock->ReferenceCount);
  769. InterlockedIncrement ((PLONG) &QuotaBlock->ProcessCount);
  770. NewProcess->QuotaBlock = QuotaBlock;
  771. }
  772. VOID
  773. PspDereferenceQuota (
  774. IN PEPROCESS Process
  775. )
  776. /*++
  777. Routine Description:
  778. This function is called at process object deletion to remove the quota block.
  779. Arguments:
  780. Process - Supplies the process to return quota to.
  781. Return Value:
  782. None.
  783. --*/
  784. {
  785. PEPROCESS_QUOTA_BLOCK QuotaBlock;
  786. ASSERT (Process->QuotaUsage[PsNonPagedPool] == 0);
  787. ASSERT (Process->QuotaUsage[PsPagedPool] == 0);
  788. ASSERT (Process->QuotaUsage[PsPageFile] == 0);
  789. QuotaBlock = Process->QuotaBlock;
  790. InterlockedDecrement ((PLONG) &QuotaBlock->ProcessCount);
  791. PspDereferenceQuotaBlock (QuotaBlock);
  792. }
  793. NTSTATUS
  794. PsChargeProcessQuota (
  795. IN PEPROCESS Process,
  796. IN PS_QUOTA_TYPE QuotaType,
  797. IN SIZE_T Amount
  798. )
  799. /*++
  800. Routine Description:
  801. This function is called to charge against the specified quota.
  802. Arguments:
  803. Process - Supplies the process to charge against.
  804. QuotaType - Type of quota being charged
  805. Amount - Amount of quota being charged
  806. Return Value:
  807. NTSTATUS - Status of operation
  808. --*/
  809. {
  810. ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
  811. if (Process == PsInitialSystemProcess) {
  812. return STATUS_SUCCESS;
  813. }
  814. return PspChargeQuota (Process->QuotaBlock, Process, QuotaType, Amount);
  815. }
  816. VOID
  817. PsReturnProcessQuota (
  818. IN PEPROCESS Process,
  819. IN PS_QUOTA_TYPE QuotaType,
  820. IN SIZE_T Amount
  821. )
  822. /*++
  823. Routine Description:
  824. This function is called to return previously charged quota to the specified process
  825. Arguments:
  826. Process - Supplies the process that was previously charged.
  827. QuotaType - Type of quota being returned
  828. Amount - Amount of quota being returned
  829. Return Value:
  830. NTSTATUS - Status of operation
  831. --*/
  832. {
  833. ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
  834. if (Process == PsInitialSystemProcess) {
  835. return;
  836. }
  837. PspReturnQuota (Process->QuotaBlock, Process, QuotaType, Amount);
  838. }
  839. NTSTATUS
  840. PsChargeProcessNonPagedPoolQuota(
  841. IN PEPROCESS Process,
  842. IN SIZE_T Amount
  843. )
  844. /*++
  845. Routine Description:
  846. This function is called to charge non-paged pool quota against the specified process.
  847. Arguments:
  848. Process - Supplies the process to charge against.
  849. Amount - Amount of quota being charged
  850. Return Value:
  851. NTSTATUS - Status of operation
  852. --*/
  853. {
  854. if (Process == PsInitialSystemProcess) {
  855. return STATUS_SUCCESS;
  856. }
  857. return PspChargeQuota (Process->QuotaBlock, Process, PsNonPagedPool, Amount);
  858. }
  859. VOID
  860. PsReturnProcessNonPagedPoolQuota(
  861. IN PEPROCESS Process,
  862. IN SIZE_T Amount
  863. )
  864. /*++
  865. Routine Description:
  866. This function is called to return previously charged non-paged pool quota to the specified process
  867. Arguments:
  868. Process - Supplies the process that was previously charged.
  869. Amount - Amount of quota being returned
  870. Return Value:
  871. NTSTATUS - Status of operation
  872. --*/
  873. {
  874. if (Process == PsInitialSystemProcess) {
  875. return;
  876. }
  877. PspReturnQuota (Process->QuotaBlock, Process, PsNonPagedPool, Amount);
  878. }
  879. NTSTATUS
  880. PsChargeProcessPagedPoolQuota(
  881. IN PEPROCESS Process,
  882. IN SIZE_T Amount
  883. )
  884. /*++
  885. Routine Description:
  886. This function is called to charge paged pool quota against the specified process.
  887. Arguments:
  888. Process - Supplies the process to charge against.
  889. Amount - Amount of quota being charged
  890. Return Value:
  891. NTSTATUS - Status of operation
  892. --*/
  893. {
  894. if (Process == PsInitialSystemProcess) {
  895. return STATUS_SUCCESS;
  896. }
  897. return PspChargeQuota (Process->QuotaBlock, Process, PsPagedPool, Amount);
  898. }
  899. VOID
  900. PsReturnProcessPagedPoolQuota(
  901. IN PEPROCESS Process,
  902. IN SIZE_T Amount
  903. )
  904. /*++
  905. Routine Description:
  906. This function is called to return previously charged paged pool quota to the specified process
  907. Arguments:
  908. Process - Supplies the process that was previously charged.
  909. Amount - Amount of quota being returned
  910. Return Value:
  911. NTSTATUS - Status of operation
  912. --*/
  913. {
  914. if (Process == PsInitialSystemProcess) {
  915. return;
  916. }
  917. PspReturnQuota (Process->QuotaBlock, Process, PsPagedPool, Amount);
  918. }
  919. NTSTATUS
  920. PsChargeProcessPageFileQuota(
  921. IN PEPROCESS Process,
  922. IN SIZE_T Amount
  923. )
  924. /*++
  925. Routine Description:
  926. This function is called to charge page file quota against the specified process.
  927. Arguments:
  928. Process - Supplies the process to charge against.
  929. Amount - Amount of quota being charged
  930. Return Value:
  931. NTSTATUS - Status of operation
  932. --*/
  933. {
  934. if (Process == PsInitialSystemProcess) {
  935. return STATUS_SUCCESS;
  936. }
  937. return PspChargeQuota (Process->QuotaBlock, Process, PsPageFile, Amount);
  938. }
  939. VOID
  940. PsReturnProcessPageFileQuota(
  941. IN PEPROCESS Process,
  942. IN SIZE_T Amount
  943. )
  944. /*++
  945. Routine Description:
  946. This function is called to return previously charged page file quota to the specified process
  947. Arguments:
  948. Process - Supplies the process that was previously charged.
  949. Amount - Amount of quota being returned
  950. Return Value:
  951. NTSTATUS - Status of operation
  952. --*/
  953. {
  954. if (Process == PsInitialSystemProcess) {
  955. return;
  956. }
  957. PspReturnQuota (Process->QuotaBlock, Process, PsPageFile, Amount);
  958. }