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.

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