master
c 750 lines 25.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows_plugin.h"
4 #include "windows-internals.h"
5
6 #define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME
7 #define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage"
8 #include "../common-contexts/common-contexts.h"
9 #include "libnetdata/os/windows-wmi/windows-wmi.h"
10
11 struct logical_disk {
12 usec_t last_collected;
13 bool collected_metadata;
14
15 UINT DriveType;
16 DWORD SerialNumber;
17 ULONG divisor;
18 bool readonly;
19
20 STRING *filesystem;
21
22 RRDSET *st_disk_space;
23 RRDDIM *rd_disk_space_used;
24 RRDDIM *rd_disk_space_free;
25
26 COUNTER_DATA percentDiskFree;
27 // COUNTER_DATA freeMegabytes;
28 };
29
30 struct physical_disk {
31 usec_t last_collected;
32 bool collected_metadata;
33
34 STRING *device;
35 STRING *mount_point;
36 STRING *manufacturer;
37 STRING *model;
38 STRING *media_type;
39 STRING *name;
40 STRING *device_id;
41
42 ND_DISK_IO disk_io;
43 // COUNTER_DATA diskBytesPerSec;
44 COUNTER_DATA diskReadBytesPerSec;
45 COUNTER_DATA diskWriteBytesPerSec;
46
47 ND_DISK_OPS disk_ops;
48 // COUNTER_DATA diskTransfersPerSec;
49 COUNTER_DATA diskReadsPerSec;
50 COUNTER_DATA diskWritesPerSec;
51
52 ND_DISK_UTIL disk_util;
53 COUNTER_DATA percentIdleTime;
54
55 ND_DISK_BUSY disk_busy;
56 COUNTER_DATA percentDiskTime;
57
58 ND_DISK_IOTIME disk_iotime;
59 COUNTER_DATA percentDiskReadTime;
60 COUNTER_DATA percentDiskWriteTime;
61
62 ND_DISK_QOPS disk_qops;
63 COUNTER_DATA currentDiskQueueLength;
64 // COUNTER_DATA averageDiskQueueLength;
65 // COUNTER_DATA averageDiskReadQueueLength;
66 // COUNTER_DATA averageDiskWriteQueueLength;
67
68 ND_DISK_AWAIT disk_await;
69 COUNTER_DATA averageDiskSecondsPerRead;
70 COUNTER_DATA averageDiskSecondsPerWrite;
71
72 ND_DISK_SVCTM disk_svctm;
73 COUNTER_DATA averageDiskSecondsPerTransfer;
74
75 ND_DISK_AVGSZ disk_avgsz;
76 //COUNTER_DATA averageDiskBytesPerTransfer;
77 COUNTER_DATA averageDiskBytesPerRead;
78 COUNTER_DATA averageDiskBytesPerWrite;
79
80 COUNTER_DATA splitIoPerSec;
81 RRDSET *st_split;
82 RRDDIM *rd_split;
83 };
84
85 struct physical_disk system_physical_total = {
86 .collected_metadata = true,
87 };
88
89 static void
90 dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
91 {
92 struct logical_disk *d = value;
93
94 d->percentDiskFree.key = "% Free Space";
95 // d->freeMegabytes.key = "Free Megabytes";
96 }
97
98 static void logical_disk_cleanup(struct logical_disk *d)
99 {
100 string_freez(d->filesystem);
101 d->filesystem = NULL;
102
103 d->collected_metadata = false;
104
105 rrdset_is_obsolete___safe_from_collector_thread(d->st_disk_space);
106 d->st_disk_space = NULL;
107 }
108
109 static void physical_disk_initialize(struct physical_disk *d)
110 {
111 d->percentIdleTime.key = "% Idle Time";
112 d->percentDiskTime.key = "% Disk Time";
113 d->percentDiskReadTime.key = "% Disk Read Time";
114 d->percentDiskWriteTime.key = "% Disk Write Time";
115 d->currentDiskQueueLength.key = "Current Disk Queue Length";
116 // d->averageDiskQueueLength.key = "Avg. Disk Queue Length";
117 // d->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
118 // d->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
119 d->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
120 d->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
121 d->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
122 // d->diskTransfersPerSec.key = "Disk Transfers/sec";
123 d->diskReadsPerSec.key = "Disk Reads/sec";
124 d->diskWritesPerSec.key = "Disk Writes/sec";
125 // d->diskBytesPerSec.key = "Disk Bytes/sec";
126 d->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
127 d->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
128 // d->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
129 d->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
130 d->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
131 d->splitIoPerSec.key = "Split IO/Sec";
132 }
133
134 static void physical_disk_cleanup(struct physical_disk *d)
135 {
136 string_freez(d->device);
137 d->device = NULL;
138 string_freez(d->mount_point);
139 d->mount_point = NULL;
140 string_freez(d->manufacturer);
141 d->manufacturer = NULL;
142 string_freez(d->model);
143 d->model = NULL;
144 string_freez(d->media_type);
145 d->media_type = NULL;
146 string_freez(d->name);
147 d->name = NULL;
148 string_freez(d->device_id);
149 d->device_id = NULL;
150
151 d->collected_metadata = false;
152
153 rrdset_is_obsolete___safe_from_collector_thread(d->disk_io.st_io);
154 rrdset_is_obsolete___safe_from_collector_thread(d->disk_ops.st_ops);
155 rrdset_is_obsolete___safe_from_collector_thread(d->disk_util.st_util);
156 rrdset_is_obsolete___safe_from_collector_thread(d->disk_busy.st_busy);
157 rrdset_is_obsolete___safe_from_collector_thread(d->disk_iotime.st_iotime);
158 rrdset_is_obsolete___safe_from_collector_thread(d->disk_qops.st_qops);
159 rrdset_is_obsolete___safe_from_collector_thread(d->disk_await.st_await);
160 rrdset_is_obsolete___safe_from_collector_thread(d->disk_svctm.st_svctm);
161 rrdset_is_obsolete___safe_from_collector_thread(d->disk_avgsz.st_avgsz);
162 rrdset_is_obsolete___safe_from_collector_thread(d->st_split);
163 }
164
165 void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
166 {
167 struct physical_disk *pd = value;
168 physical_disk_initialize(pd);
169 }
170
171 static void dict_logical_disk_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
172 {
173 struct logical_disk *d = value;
174 logical_disk_cleanup(d);
175 }
176
177 static void dict_physical_disk_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused)
178 {
179 struct physical_disk *d = value;
180 physical_disk_cleanup(d);
181 }
182
183 static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL;
184 static void initialize(void)
185 {
186 physical_disk_initialize(&system_physical_total);
187
188 logicalDisks = dictionary_create_advanced(
189 DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk));
190
191 dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL);
192 dictionary_register_delete_callback(logicalDisks, dict_logical_disk_delete_cb, NULL);
193
194 physicalDisks = dictionary_create_advanced(
195 DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk));
196
197 dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL);
198 dictionary_register_delete_callback(physicalDisks, dict_physical_disk_delete_cb, NULL);
199 }
200
201 static STRING *getFileSystemType(struct logical_disk *d, const char *diskName)
202 {
203 if (!diskName || !*diskName)
204 return NULL;
205
206 char fileSystemNameBuffer[128] = {0}; // Buffer for file system name
207 char pathBuffer[260] = {0}; // Path buffer to accommodate different formats
208 char volumeName[260 + 1] = {0};
209 DWORD serialNumber = 0;
210 DWORD maxComponentLength = 0;
211 DWORD fileSystemFlags = 0;
212 BOOL success;
213
214 // Check if the input is likely a drive letter (e.g., "C:")
215 if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0')
216 snprintfz(pathBuffer, sizeof(pathBuffer) - 1, "%s\\", diskName); // Format as "C:\"
217 else
218 // Assume it's a Volume GUID path or a device path
219 snprintfz(pathBuffer, sizeof(pathBuffer) - 1, "\\\\.\\%s\\", diskName); // Format as "\\.\HarddiskVolume1\"
220
221 d->DriveType = GetDriveTypeA(pathBuffer);
222
223 // Attempt to get the volume information
224 success = GetVolumeInformationA(
225 pathBuffer, // Path to the disk
226 volumeName, // Volume name buffer
227 260, // Size of volume name buffer
228 &serialNumber, // Volume serial number
229 &maxComponentLength, // Maximum component length
230 &fileSystemFlags, // File system flags
231 fileSystemNameBuffer, // File system name buffer
232 sizeof(fileSystemNameBuffer) // Size of file system name buffer
233 );
234
235 if (success) {
236 d->readonly = fileSystemFlags & FILE_READ_ONLY_VOLUME;
237 d->SerialNumber = serialNumber;
238
239 if (fileSystemNameBuffer[0]) {
240 char *s = fileSystemNameBuffer;
241 while (*s) {
242 *s = tolower((uint8_t)*s);
243 s++;
244 }
245 return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
246 }
247 }
248 return NULL;
249 }
250
251 static const char *drive_type_to_str(UINT type)
252 {
253 switch (type) {
254 default:
255 case 0:
256 return "unknown";
257 case 1:
258 return "norootdir";
259 case 2:
260 return "removable";
261 case 3:
262 return "fixed";
263 case 4:
264 return "remote";
265 case 5:
266 return "cdrom";
267 case 6:
268 return "ramdisk";
269 }
270 }
271
272 static inline void netdata_set_hd_usage(PERF_DATA_BLOCK *pDataBlock,
273 PERF_OBJECT_TYPE *pObjectType,
274 PERF_INSTANCE_DEFINITION *pi,
275 struct logical_disk *d)
276 {
277 ULARGE_INTEGER totalNumberOfBytes;
278 ULARGE_INTEGER totalNumberOfFreeBytes;
279 ULARGE_INTEGER totalAvailableToCaller;
280
281 // https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
282 #define MAX_DRIVE_LENGTH 255
283 char path[MAX_DRIVE_LENGTH + 1];
284 snprintfz(path, MAX_DRIVE_LENGTH, "%s\\", windows_shared_buffer);
285
286 // Description of incompatibilities present in both methods we are using
287 // https://devblogs.microsoft.com/oldnewthing/20071101-00/?p=24613
288 // We are using the variable that should not be affected by qyota ()
289 if ((GetDriveTypeA(path) == DRIVE_UNKNOWN) || !GetDiskFreeSpaceExA(path,
290 &totalAvailableToCaller,
291 &totalNumberOfBytes,
292 &totalNumberOfFreeBytes)) {
293 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree);
294 d->divisor = 1024;
295 return;
296 }
297
298 d->divisor = GIGA_FACTOR;
299 d->percentDiskFree.current.Data = totalNumberOfFreeBytes.QuadPart;
300 d->percentDiskFree.current.Time = totalNumberOfBytes.QuadPart;
301 }
302
303 static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut)
304 {
305 DICTIONARY *dict = logicalDisks;
306
307 PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk");
308 if (!pObjectType)
309 return false;
310
311 PERF_INSTANCE_DEFINITION *pi = NULL;
312 for (LONG i = 0; i < pObjectType->NumInstances; i++) {
313 pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
314 if (!pi)
315 break;
316
317 if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
318 strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
319
320 if (strcasecmp(windows_shared_buffer, "_Total") == 0)
321 continue;
322
323 struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
324 d->last_collected = now_ut;
325
326 if (!d->collected_metadata) {
327 d->filesystem = getFileSystemType(d, windows_shared_buffer);
328 d->collected_metadata = true;
329 }
330
331 netdata_set_hd_usage(pDataBlock, pObjectType, pi, d);
332 // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes);
333
334 if (!d->st_disk_space) {
335 d->st_disk_space = rrdset_create_localhost(
336 "disk_space",
337 windows_shared_buffer,
338 NULL,
339 windows_shared_buffer,
340 "disk.space",
341 "Disk Space Usage",
342 "GiB",
343 PLUGIN_WINDOWS_NAME,
344 "PerflibStorage",
345 NETDATA_CHART_PRIO_DISKSPACE_SPACE,
346 update_every,
347 RRDSET_TYPE_STACKED);
348
349 rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO);
350 rrdlabels_add(
351 d->st_disk_space->rrdlabels, "drive_type", drive_type_to_str(d->DriveType), RRDLABEL_SRC_AUTO);
352 rrdlabels_add(
353 d->st_disk_space->rrdlabels,
354 "filesystem",
355 d->filesystem ? string2str(d->filesystem) : "unknown",
356 RRDLABEL_SRC_AUTO);
357 rrdlabels_add(d->st_disk_space->rrdlabels, "rw_mode", d->readonly ? "ro" : "rw", RRDLABEL_SRC_AUTO);
358
359 {
360 char buf[UINT64_HEX_MAX_LENGTH];
361 print_uint64_hex(buf, d->SerialNumber);
362 rrdlabels_add(d->st_disk_space->rrdlabels, "serial_number", buf, RRDLABEL_SRC_AUTO);
363 }
364
365 d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, d->divisor, RRD_ALGORITHM_ABSOLUTE);
366 d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, d->divisor, RRD_ALGORITHM_ABSOLUTE);
367 }
368
369 // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB.
370 rrddim_set_by_pointer(
371 d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data);
372 rrddim_set_by_pointer(
373 d->st_disk_space,
374 d->rd_disk_space_used,
375 (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data));
376 rrdset_done(d->st_disk_space);
377 }
378
379 // cleanup - delete callback will handle resource cleanup
380 {
381 struct logical_disk *d;
382 dfe_start_write(dict, d)
383 {
384 if (d->last_collected < now_ut)
385 dictionary_del(dict, d_dfe.name);
386 }
387 dfe_done(d);
388 dictionary_garbage_collect(dict);
389 }
390
391 return true;
392 }
393
394 static void physical_disk_labels(RRDSET *st, void *data)
395 {
396 struct physical_disk *d = data;
397
398 if (d->device)
399 rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO);
400
401 if (d->mount_point)
402 rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO);
403
404 // if (d->manufacturer)
405 // rrdlabels_add(st->rrdlabels, "manufacturer", string2str(d->manufacturer), RRDLABEL_SRC_AUTO);
406
407 if (d->model)
408 rrdlabels_add(st->rrdlabels, "model", string2str(d->model), RRDLABEL_SRC_AUTO);
409
410 // if (d->media_type)
411 // rrdlabels_add(st->rrdlabels, "media_type", string2str(d->media_type), RRDLABEL_SRC_AUTO);
412
413 // if (d->name)
414 // rrdlabels_add(st->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
415
416 if (d->device_id)
417 rrdlabels_add(st->rrdlabels, "device_id", string2str(d->device_id), RRDLABEL_SRC_AUTO);
418 }
419
420 static bool str_is_numeric(const char *s)
421 {
422 while (*s)
423 if (!isdigit((uint8_t)*s++))
424 return false;
425 return true;
426 }
427
428 static inline double perflib_average_timer_ms(COUNTER_DATA *d)
429 {
430 if (!d->updated)
431 return 0.0;
432
433 ULONGLONG data1 = d->current.Data;
434 ULONGLONG data0 = d->previous.Data;
435 LONGLONG time1 = d->current.Time;
436 LONGLONG time0 = d->previous.Time;
437 LONGLONG freq1 = d->current.Frequency;
438
439 if (data1 >= data0 && time1 > time0 && time0 && freq1)
440 return ((double)(data1 - data0) * (double)MSEC_PER_SEC) / ((double)freq1 * (double)(time1 - time0));
441
442 return 0;
443 }
444
445 static inline uint64_t perflib_average_bulk(COUNTER_DATA *d)
446 {
447 if (!d->updated)
448 return 0;
449
450 ULONGLONG data1 = d->current.Data;
451 ULONGLONG data0 = d->previous.Data;
452 LONGLONG time1 = d->current.Time;
453 LONGLONG time0 = d->previous.Time;
454
455 if (data1 >= data0 && time1 > time0 && time0)
456 return (data1 - data0) / (time1 - time0);
457
458 return 0;
459 }
460
461 static inline uint64_t perflib_idle_time_percent(COUNTER_DATA *d)
462 {
463 if (!d->updated)
464 return 0.0;
465
466 ULONGLONG data1 = d->current.Data;
467 ULONGLONG data0 = d->previous.Data;
468 LONGLONG time1 = d->current.Time;
469 LONGLONG time0 = d->previous.Time;
470
471 if (data1 >= data0 && time1 > time0 && time0) {
472 uint64_t pcent = 100 * (data1 - data0) / (time1 - time0);
473 return pcent > 100 ? 100 : pcent;
474 }
475
476 return 0;
477 }
478
479 #define MAX_WMI_DRIVES 100
480 static DiskDriveInfoWMI infos[MAX_WMI_DRIVES];
481
482 static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut)
483 {
484 DICTIONARY *dict = physicalDisks;
485 size_t infoCount = 0;
486 bool infos_loaded = false;
487
488 PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
489 if (!pObjectType)
490 return false;
491
492 PERF_INSTANCE_DEFINITION *pi = NULL;
493 for (LONG i = 0; i < pObjectType->NumInstances; i++) {
494 pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
495 if (!pi)
496 break;
497
498 if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
499 strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
500
501 int device_index = -1;
502 char *device = windows_shared_buffer;
503 char mount_point[128];
504 mount_point[0] = '\0';
505
506 struct physical_disk *d;
507 bool is_system;
508 if (strcasecmp(windows_shared_buffer, "_Total") == 0) {
509 d = &system_physical_total;
510 is_system = true;
511 } else {
512 char *space;
513 if ((space = strchr(windows_shared_buffer, ' '))) {
514 *space++ = '\0';
515 strncpyz(mount_point, space, sizeof(mount_point) - 1);
516 }
517
518 if (str_is_numeric(windows_shared_buffer)) {
519 device_index = str2ull(device, NULL);
520 snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %d", device_index);
521 device = windows_shared_buffer;
522 }
523
524 d = dictionary_set(dict, device, NULL, sizeof(*d));
525 is_system = false;
526 }
527 d->last_collected = now_ut;
528
529 if (!d->collected_metadata) {
530 if (!is_system && device_index != -1) {
531 if (!infos_loaded) {
532 infoCount = GetDiskDriveInfo(infos, _countof(infos));
533 infos_loaded = true;
534 }
535
536 for (size_t k = 0; k < infoCount; k++) {
537 if (infos[k].Index != device_index)
538 continue;
539
540 d->manufacturer = string_strdupz(infos[k].Manufacturer);
541 d->model = string_strdupz(infos[k].Model);
542 d->media_type = string_strdupz(infos[k].MediaType);
543 d->name = string_strdupz(infos[k].Name);
544 d->device_id = string_strdupz(infos[k].DeviceID);
545
546 break;
547 }
548 }
549
550 d->device = string_strdupz(device);
551 d->mount_point = string_strdupz(mount_point);
552 d->collected_metadata = true;
553 }
554
555 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) &&
556 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) {
557 if (is_system)
558 common_system_io(
559 d->diskReadBytesPerSec.current.Data, d->diskWriteBytesPerSec.current.Data, update_every);
560 else
561 common_disk_io(
562 &d->disk_io,
563 device,
564 NULL,
565 d->diskReadBytesPerSec.current.Data,
566 d->diskWriteBytesPerSec.current.Data,
567 update_every,
568 physical_disk_labels,
569 d);
570 }
571
572 if (is_system)
573 continue;
574
575 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec) &&
576 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec)) {
577 common_disk_ops(
578 &d->disk_ops,
579 device,
580 NULL,
581 d->diskReadsPerSec.current.Data,
582 d->diskWritesPerSec.current.Data,
583 update_every,
584 physical_disk_labels,
585 d);
586 }
587
588 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime)) {
589 common_disk_util(
590 &d->disk_util,
591 device,
592 NULL,
593 100 - perflib_idle_time_percent(&d->percentIdleTime),
594 update_every,
595 physical_disk_labels,
596 d);
597 }
598
599 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime)) {
600 common_disk_busy(
601 &d->disk_busy,
602 device,
603 NULL,
604 d->percentDiskTime.current.Data / NS100_PER_MS,
605 update_every,
606 physical_disk_labels,
607 d);
608 }
609
610 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime) &&
611 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime)) {
612 common_disk_iotime(
613 &d->disk_iotime,
614 device,
615 NULL,
616 d->percentDiskReadTime.current.Data / NS100_PER_MS,
617 d->percentDiskWriteTime.current.Data / NS100_PER_MS,
618 update_every,
619 physical_disk_labels,
620 d);
621 }
622
623 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength)) {
624 common_disk_qops(
625 &d->disk_qops,
626 device,
627 NULL,
628 d->currentDiskQueueLength.current.Data,
629 update_every,
630 physical_disk_labels,
631 d);
632 }
633
634 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead) &&
635 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite)) {
636 common_disk_await(
637 &d->disk_await,
638 device,
639 NULL,
640 perflib_average_timer_ms(&d->averageDiskSecondsPerRead),
641 perflib_average_timer_ms(&d->averageDiskSecondsPerWrite),
642 update_every,
643 physical_disk_labels,
644 d);
645 }
646
647 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer)) {
648 common_disk_svctm(
649 &d->disk_svctm,
650 device,
651 NULL,
652 perflib_average_timer_ms(&d->averageDiskSecondsPerTransfer),
653 update_every,
654 physical_disk_labels,
655 d);
656 }
657
658 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead) &&
659 perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite)) {
660 common_disk_avgsz(
661 &d->disk_avgsz,
662 device,
663 NULL,
664 perflib_average_bulk(&d->averageDiskBytesPerRead),
665 perflib_average_bulk(&d->averageDiskBytesPerWrite),
666 update_every,
667 physical_disk_labels,
668 d);
669 }
670
671 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec)) {
672 if (!d->st_split) {
673 d->st_split = rrdset_create_localhost(
674 "disk_split",
675 device,
676 NULL,
677 "iops",
678 "disk.split",
679 "Split I/O Operations",
680 "operations/s",
681 _COMMON_PLUGIN_NAME,
682 _COMMON_PLUGIN_MODULE_NAME,
683 NETDATA_CHART_PRIO_DISK_SPLIT,
684 update_every,
685 RRDSET_TYPE_LINE);
686
687 d->rd_split = rrddim_add(d->st_split, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
688
689 physical_disk_labels(d->st_split, d);
690 }
691
692 rrddim_set_by_pointer(d->st_split, d->rd_split, d->splitIoPerSec.current.Data);
693 rrdset_done(d->st_split);
694 }
695 }
696
697 // cleanup - delete callback will handle resource cleanup
698 {
699 struct physical_disk *d;
700 dfe_start_write(dict, d)
701 {
702 if (d->last_collected < now_ut)
703 dictionary_del(dict, d_dfe.name);
704 }
705 dfe_done(d);
706 dictionary_garbage_collect(dict);
707 }
708
709 return true;
710 }
711
712 int do_PerflibStorage(int update_every, usec_t dt __maybe_unused)
713 {
714 static bool initialized = false;
715 DWORD logical_id, physical_id;
716
717 if (unlikely(!initialized)) {
718 initialize();
719 initialized = true;
720 }
721
722 logical_id = RegistryFindIDByName("LogicalDisk");
723 physical_id = RegistryFindIDByName("PhysicalDisk");
724
725 if (logical_id == PERFLIB_REGISTRY_NAME_NOT_FOUND && physical_id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
726 return -1;
727
728 // Each perflibGetPerformanceData() call reuses the same internal buffer, so we must
729 // query and consume each block before issuing the next call to avoid pointer aliasing.
730 usec_t now_ut = now_monotonic_usec();
731 bool processed_any = false;
732
733 if (logical_id != PERFLIB_REGISTRY_NAME_NOT_FOUND) {
734 PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(logical_id);
735 if (pDataBlock) {
736 do_logical_disk(pDataBlock, update_every, now_ut);
737 processed_any = true;
738 }
739 }
740
741 if (physical_id != PERFLIB_REGISTRY_NAME_NOT_FOUND) {
742 PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(physical_id);
743 if (pDataBlock) {
744 do_physical_disk(pDataBlock, update_every, now_ut);
745 processed_any = true;
746 }
747 }
748
749 return processed_any ? 0 : -1;
750 }