Leaked source code of windows server 2003
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.

497 lines
13 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1997 - 1998
  3. Module Name:
  4. pmwmicnt.c
  5. Abstract:
  6. This file contains the routines to manage and maintain the disk perf
  7. counters. The counter structure is hidden from the various drivers.
  8. Author:
  9. Bruce Worthington 26-Oct-1998
  10. Environment:
  11. kernel mode only
  12. Notes:
  13. Revision History:
  14. --*/
  15. #define RTL_USE_AVL_TABLES 0
  16. #include <ntosp.h>
  17. #include <stdio.h>
  18. #include <ntddvol.h>
  19. #include <ntdddisk.h>
  20. #include <wmilib.h>
  21. #include <partmgr.h>
  22. // Round off freq to 100 microsecs prevent overflow
  23. #define CTR_TO_100NS(ctr, freq) ((freq).QuadPart > 0) ? \
  24. ((ctr).QuadPart / ((freq).QuadPart / 10000)) * 1000 : \
  25. (ctr).QuadPart;
  26. typedef struct _PMWMICOUNTER_CONTEXT
  27. {
  28. ULONG EnableCount;
  29. ULONG Processors;
  30. ULONG QueueDepth;
  31. PDISK_PERFORMANCE *DiskCounters;
  32. LARGE_INTEGER LastIdleClock;
  33. } PMWMICOUNTER_CONTEXT, *PPMWMICOUNTER_CONTEXT;
  34. NTSTATUS
  35. PmWmiCounterEnable(
  36. IN OUT PPMWMICOUNTER_CONTEXT* CounterContext
  37. );
  38. BOOLEAN
  39. PmWmiCounterDisable(
  40. IN PPMWMICOUNTER_CONTEXT* CounterContext,
  41. IN BOOLEAN ForceDisable,
  42. IN BOOLEAN DeallocateOnZero
  43. );
  44. VOID
  45. PmWmiCounterIoStart(
  46. IN PPMWMICOUNTER_CONTEXT CounterContext,
  47. OUT PLARGE_INTEGER TimeStamp
  48. );
  49. VOID
  50. PmWmiCounterIoComplete(
  51. IN PPMWMICOUNTER_CONTEXT CounterContext,
  52. IN PIRP Irp,
  53. IN PLARGE_INTEGER TimeStamp
  54. );
  55. VOID
  56. PmWmiCounterQuery(
  57. IN PPMWMICOUNTER_CONTEXT CounterContext,
  58. IN OUT PDISK_PERFORMANCE CounterBuffer,
  59. IN PWCHAR StorageManagerName,
  60. IN ULONG StorageDeviceNumber
  61. );
  62. #ifdef ALLOC_PRAGMA
  63. #pragma alloc_text(PAGE, PmWmiCounterEnable)
  64. #pragma alloc_text(PAGE, PmWmiCounterDisable)
  65. #pragma alloc_text(PAGE, PmWmiCounterQuery)
  66. #endif
  67. NTSTATUS
  68. PmWmiCounterEnable(
  69. IN OUT PPMWMICOUNTER_CONTEXT* CounterContext
  70. )
  71. /*++
  72. Routine Description:
  73. This routine will allocate and initialize a PMWMICOUNTER_CONTEXT structure
  74. for *CounterContext if it is NULL. Otherwise, an enable count is
  75. incremented.
  76. Must be called at IRQ <= SYNCH_LEVEL (APC)
  77. Arguments:
  78. CounterContext - Supplies a pointer to the PMWMICOUNTER_CONTEXT pointer
  79. Return Value:
  80. status
  81. --*/
  82. {
  83. ULONG buffersize;
  84. ULONG processors;
  85. ULONG i = 0;
  86. PCHAR buffer;
  87. PPMWMICOUNTER_CONTEXT HoldContext; // Holds context during initialization
  88. PAGED_CODE();
  89. if (CounterContext == NULL)
  90. return STATUS_INVALID_PARAMETER;
  91. if (*CounterContext != NULL) {
  92. if ((*CounterContext)->EnableCount == 0) {
  93. (*CounterContext)->QueueDepth = 0;
  94. PmWmiGetClock((*CounterContext)->LastIdleClock, NULL);
  95. }
  96. InterlockedIncrement(& (*CounterContext)->EnableCount);
  97. return STATUS_SUCCESS;
  98. }
  99. processors = KeNumberProcessors;
  100. buffersize= sizeof(PMWMICOUNTER_CONTEXT) +
  101. ((sizeof(PDISK_PERFORMANCE) + sizeof(DISK_PERFORMANCE))
  102. * processors);
  103. buffer = (PCHAR) ExAllocatePoolWithTag(NonPagedPool, buffersize,
  104. PARTMGR_TAG_PARTITION_ENTRY);
  105. if (buffer == NULL) {
  106. return STATUS_INSUFFICIENT_RESOURCES;
  107. }
  108. RtlZeroMemory(buffer, buffersize);
  109. HoldContext = (PPMWMICOUNTER_CONTEXT) buffer;
  110. buffer += sizeof(PMWMICOUNTER_CONTEXT);
  111. HoldContext->DiskCounters = (PDISK_PERFORMANCE*) buffer;
  112. buffer += sizeof(PDISK_PERFORMANCE) * processors;
  113. for (i=0; i<processors; i++) {
  114. HoldContext->DiskCounters[i] = (PDISK_PERFORMANCE) buffer;
  115. buffer += sizeof(DISK_PERFORMANCE);
  116. }
  117. HoldContext->EnableCount = 1;
  118. HoldContext->Processors = processors;
  119. PmWmiGetClock(HoldContext->LastIdleClock, NULL);
  120. *CounterContext = HoldContext;
  121. return STATUS_SUCCESS;
  122. }
  123. BOOLEAN // Return value indicates if counters are still enabled
  124. PmWmiCounterDisable(
  125. IN PPMWMICOUNTER_CONTEXT* CounterContext,
  126. IN BOOLEAN ForceDisable,
  127. IN BOOLEAN DeallocateOnZero
  128. )
  129. /*++
  130. Routine Description:
  131. This routine decrements the enable count and, if deallocation is requested,
  132. frees the *CounterContext PMWMICOUNTER_CONTEXT data structure when the
  133. enable count reaches zero. The enable count may also be forced to zero,
  134. if explicitly requested.
  135. Must be called at IRQ <= SYNCH_LEVEL (APC)
  136. Arguments:
  137. CounterContext - Supplies a pointer to the PMWMICOUNTER_CONTEXT pointer
  138. ForceDisable - If TRUE, force enable count to zero (rather than decrement)
  139. DeallocateOnZero - If TRUE, deallocate PMWMICOUNTER_CONTEXT when enable
  140. count reaches zero
  141. Return Value:
  142. Boolean indicating if the enable count is still non-zero (i.e., counters
  143. are still enabled!)
  144. --*/
  145. {
  146. LONG enablecount = 0;
  147. PAGED_CODE();
  148. if (CounterContext == NULL)
  149. return FALSE;
  150. if (*CounterContext != NULL) {
  151. if (ForceDisable) {
  152. InterlockedExchange(& (*CounterContext)->EnableCount, enablecount);
  153. enablecount = 0;
  154. } else if ((enablecount =
  155. InterlockedDecrement(&(*CounterContext)->EnableCount))!=0) {
  156. if (enablecount > 0) {
  157. return TRUE;
  158. }
  159. enablecount = InterlockedIncrement(&(*CounterContext)->EnableCount);
  160. }
  161. if (!enablecount && DeallocateOnZero) {
  162. ExFreePool(*CounterContext);
  163. *CounterContext = NULL;
  164. }
  165. }
  166. return FALSE; // counters disabled
  167. }
  168. VOID
  169. PmWmiCounterIoStart(
  170. IN PPMWMICOUNTER_CONTEXT CounterContext,
  171. OUT PLARGE_INTEGER TimeStamp
  172. )
  173. /*++
  174. Routine Description:
  175. This routine increments the queue counter in CounterContext and records
  176. the current time in TimeStamp. If the queue was empty prior to this call,
  177. the idle time counter is also accumulated.
  178. Can be called at IRQ <= DISPATCH_LEVEL
  179. Arguments:
  180. CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
  181. TimeStamp - Address at which to store the current time
  182. Return Value:
  183. void
  184. --*/
  185. {
  186. ULONG processor = (ULONG) KeGetCurrentProcessorNumber();
  187. ULONG queueLen;
  188. LARGE_INTEGER time;
  189. //
  190. // Increment queue depth counter.
  191. //
  192. queueLen = InterlockedIncrement(&CounterContext->QueueDepth);
  193. //
  194. // Time stamp current request start.
  195. //
  196. PmWmiGetClock(time, NULL);
  197. if (queueLen == 1) {
  198. CounterContext->DiskCounters[processor]->IdleTime.QuadPart +=
  199. time.QuadPart - CounterContext->LastIdleClock.QuadPart;
  200. }
  201. TimeStamp->QuadPart = time.QuadPart;
  202. }
  203. VOID
  204. PmWmiCounterIoComplete(
  205. IN PPMWMICOUNTER_CONTEXT CounterContext,
  206. IN PIRP Irp,
  207. IN PLARGE_INTEGER TimeStamp
  208. )
  209. /*++
  210. Routine Description:
  211. This routine decrements the queue counter in CounterContext and increments
  212. the split counter and read or write byte, time, and count counters with
  213. information from the Irp. If the queue is now empty, the current
  214. time is stored for future use in accumulating the idle time counter.
  215. Can be called at IRQ <= DISPATCH_LEVEL
  216. Arguments:
  217. CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
  218. Irp - relevant IRP
  219. TimeStamp - Time of the corresponding PmWmiCounterIoStart call
  220. Return Value:
  221. void
  222. --*/
  223. {
  224. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
  225. PDISK_PERFORMANCE partitionCounters;
  226. LARGE_INTEGER timeStampComplete;
  227. LONG queueLen;
  228. partitionCounters
  229. = CounterContext->DiskCounters[(ULONG)KeGetCurrentProcessorNumber()];
  230. //
  231. // Time stamp current request complete.
  232. //
  233. PmWmiGetClock(timeStampComplete, NULL);
  234. TimeStamp->QuadPart = timeStampComplete.QuadPart - TimeStamp->QuadPart;
  235. //
  236. // Decrement the queue depth counters for the volume. This is
  237. // done without the spinlock using the Interlocked functions.
  238. // This is the only
  239. // legal way to do this.
  240. //
  241. queueLen = InterlockedDecrement(&CounterContext->QueueDepth);
  242. if (queueLen < 0) {
  243. queueLen = InterlockedIncrement(&CounterContext->QueueDepth);
  244. }
  245. if (queueLen == 0) {
  246. CounterContext->LastIdleClock = timeStampComplete;
  247. }
  248. //
  249. // Update counters protection.
  250. //
  251. if (irpStack->MajorFunction == IRP_MJ_READ) {
  252. //
  253. // Add bytes in this request to bytes read counters.
  254. //
  255. partitionCounters->BytesRead.QuadPart += Irp->IoStatus.Information;
  256. //
  257. // Increment read requests processed counters.
  258. //
  259. partitionCounters->ReadCount++;
  260. //
  261. // Calculate request processing time.
  262. //
  263. partitionCounters->ReadTime.QuadPart += TimeStamp->QuadPart;
  264. }
  265. else {
  266. //
  267. // Add bytes in this request to bytes write counters.
  268. //
  269. partitionCounters->BytesWritten.QuadPart += Irp->IoStatus.Information;
  270. //
  271. // Increment write requests processed counters.
  272. //
  273. partitionCounters->WriteCount++;
  274. //
  275. // Calculate request processing time.
  276. //
  277. partitionCounters->WriteTime.QuadPart += TimeStamp->QuadPart;
  278. }
  279. if (Irp->Flags & IRP_ASSOCIATED_IRP) {
  280. partitionCounters->SplitCount++;
  281. }
  282. }
  283. VOID
  284. PmWmiCounterQuery(
  285. IN PPMWMICOUNTER_CONTEXT CounterContext,
  286. IN OUT PDISK_PERFORMANCE TotalCounters,
  287. IN PWCHAR StorageManagerName,
  288. IN ULONG StorageDeviceNumber
  289. )
  290. /*++
  291. Routine Description:
  292. This routine combines all of the per-processor counters in CounterContext
  293. into TotalCounters. The current time is also included.
  294. Must be called at IRQ <= SYNCH_LEVEL (APC)
  295. Arguments:
  296. CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
  297. TotalCounters - Pointer to a DISK_PERFORMANCE structure to fill with the
  298. current counter status
  299. StorageManagerName - Supplies an 8-character storage manager unicode string
  300. StorageDeviceNumber - Supplies a storage device number (unique within
  301. the storage manager)
  302. Return Value:
  303. void
  304. --*/
  305. {
  306. ULONG i;
  307. LARGE_INTEGER frequency;
  308. #ifdef USE_PERF_CTR
  309. LARGE_INTEGER perfctr;
  310. #endif
  311. PAGED_CODE();
  312. RtlZeroMemory(TotalCounters, sizeof(DISK_PERFORMANCE));
  313. KeQuerySystemTime(&TotalCounters->QueryTime);
  314. frequency.QuadPart = 0;
  315. #ifdef USE_PERF_CTR
  316. perfctr = KeQueryPerformanceCounter(&frequency);
  317. #endif
  318. TotalCounters->QueueDepth = CounterContext->QueueDepth;
  319. for (i = 0; i < CounterContext->Processors; i++) {
  320. PDISK_PERFORMANCE IndividualCounter = CounterContext->DiskCounters[i];
  321. TotalCounters->BytesRead.QuadPart
  322. += IndividualCounter->BytesRead.QuadPart;
  323. TotalCounters->BytesWritten.QuadPart
  324. += IndividualCounter->BytesWritten.QuadPart;
  325. TotalCounters->ReadCount += IndividualCounter->ReadCount;
  326. TotalCounters->WriteCount += IndividualCounter->WriteCount;
  327. TotalCounters->SplitCount += IndividualCounter->SplitCount;
  328. #ifdef USE_PERF_CTR
  329. TotalCounters->ReadTime.QuadPart +=
  330. CTR_TO_100NS(IndividualCounter->ReadTime, frequency);
  331. TotalCounters->WriteTime.QuadPart +=
  332. CTR_TO_100NS(IndividualCounter->WriteTime, frequency);
  333. TotalCounters->IdleTime.QuadPart +=
  334. CTR_TO_100NS(IndividualCounter->IdleTime, frequency);
  335. #else
  336. TotalCounters->ReadTime.QuadPart
  337. += IndividualCounter->ReadTime.QuadPart;
  338. TotalCounters->WriteTime.QuadPart
  339. += IndividualCounter->WriteTime.QuadPart;
  340. TotalCounters->IdleTime.QuadPart
  341. += IndividualCounter->IdleTime.QuadPart;
  342. #endif
  343. }
  344. if (TotalCounters->QueueDepth == 0) {
  345. LARGE_INTEGER difference;
  346. difference.QuadPart
  347. #ifdef USE_PERF_CTR
  348. = perfctr.QuadPart -
  349. #else
  350. = TotalCounters->QueryTime.QuadPart -
  351. #endif
  352. CounterContext->LastIdleClock.QuadPart;
  353. TotalCounters->IdleTime.QuadPart +=
  354. #ifdef USE_PERF_CTR
  355. CTR_TO_100NS(difference, frequency);
  356. #else
  357. difference.QuadPart;
  358. #endif
  359. }
  360. TotalCounters->StorageDeviceNumber = StorageDeviceNumber;
  361. RtlCopyMemory(
  362. &TotalCounters->StorageManagerName[0],
  363. &StorageManagerName[0],
  364. sizeof(WCHAR) * 8);
  365. }