]> arthur.barton.de Git - netdata.git/blob - src/freebsd_sysctl.c
Add disk stats and do minor style cleanups
[netdata.git] / src / freebsd_sysctl.c
1 #include "common.h"
2
3 // NEEDED BY: struct vmstat
4 #include <sys/vmmeter.h>
5 // NEEDED BY: struct devstat
6 #include <sys/devicestat.h>
7 // NEEDED BY: do_disk_io
8 #define RRD_TYPE_DISK "disk"
9
10 // FreeBSD calculates load averages once every 5 seconds
11 #define MIN_LOADAVG_UPDATE_EVERY 5
12
13 int do_freebsd_sysctl(int update_every, unsigned long long dt) {
14     (void)dt;
15
16     static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1,
17         do_loadavg = -1, do_all_processes = -1, do_disk_io = -1;
18
19     if (unlikely(do_cpu == -1)) {
20         do_cpu                  = config_get_boolean("plugin:freebsd:sysctl", "cpu utilization", 1);
21         do_cpu_cores            = config_get_boolean("plugin:freebsd:sysctl", "per cpu core utilization", 1);
22         do_interrupts           = config_get_boolean("plugin:freebsd:sysctl", "cpu interrupts", 1);
23         do_context              = config_get_boolean("plugin:freebsd:sysctl", "context switches", 1);
24         do_forks                = config_get_boolean("plugin:freebsd:sysctl", "processes started", 1);
25         do_processes            = config_get_boolean("plugin:freebsd:sysctl", "processes running", 1);
26         do_loadavg              = config_get_boolean("plugin:freebsd:sysctl", "enable load average", 1);
27         do_all_processes        = config_get_boolean("plugin:freebsd:sysctl", "enable total processes", 1);
28
29         do_disk_io              = config_get_boolean("plugin:freebsd:sysctl", "stats for all disks", 1);
30     }
31
32     RRDSET *st;
33
34     int i;
35
36 // NEEDED BY: do_loadavg
37     static unsigned long long last_loadavg_usec = 0;
38     struct loadavg sysload;
39
40 // NEEDED BY: do_cpu, do_cpu_cores
41     long cp_time[CPUSTATES];
42
43 // NEEDED BY: do_cpu_cores
44     int ncpus;
45     static long *pcpu_cp_time = NULL;
46     char cpuid[8]; // no more than 4 digits expected
47
48 // NEEDED BY: do_all_processes, do_processes
49     struct vmtotal vmtotal_data;
50
51 // NEEDED BY: do_context, do_forks
52     u_int u_int_data;
53
54 // NEEDED BY: do_interrupts
55     size_t intrcnt_size;
56     unsigned long nintr = 0;
57     static unsigned long *intrcnt = NULL;
58     unsigned long long totalintr = 0;
59
60 // NEEDED BY: do_disk_io
61     #define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64
62     int numdevs;
63     static void *devstat_data = NULL;
64     struct devstat *dstat;
65     struct cur_dstat {
66         collected_number duration_read_ms;
67         collected_number duration_write_ms;
68         collected_number busy_time_ms;
69     } cur_dstat;
70     struct prev_dstat {
71         collected_number bytes_read;
72         collected_number bytes_write;
73         collected_number operations_read;
74         collected_number operations_write;
75         collected_number duration_read_ms;
76         collected_number duration_write_ms;
77         collected_number busy_time_ms;
78     } prev_dstat;
79
80     // --------------------------------------------------------------------
81
82     if (last_loadavg_usec <= dt) {
83         if (likely(do_loadavg)) {
84             if (unlikely(GETSYSCTL("vm.loadavg", sysload))) {
85                 do_loadavg = 0;
86                 error("DISABLED: system.load");
87             } else {
88
89                 st = rrdset_find_bytype("system", "load");
90                 if (unlikely(!st)) {
91                     st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
92                     rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
93                     rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
94                     rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
95                 }
96                 else rrdset_next(st);
97
98                 rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000));
99                 rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000));
100                 rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000));
101                 rrdset_done(st);
102             }
103         }
104
105         last_loadavg_usec = st->update_every * 1000000ULL;
106     }
107     else last_loadavg_usec -= dt;
108
109     // --------------------------------------------------------------------
110
111     if (likely(do_all_processes | do_processes)) {
112         if (unlikely(GETSYSCTL("vm.vmtotal", vmtotal_data))) {
113             do_all_processes = 0;
114             error("DISABLED: system.active_processes");
115             do_processes = 0;
116             error("DISABLED: system.processes");
117         } else {
118             if (likely(do_processes)) {
119
120                 st = rrdset_find_bytype("system", "active_processes");
121                 if (unlikely(!st)) {
122                     st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
123                     rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
124                 }
125                 else rrdset_next(st);
126
127                 rrddim_set(st, "active", (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw));
128                 rrdset_done(st);
129             }
130             if (likely(do_processes)) {
131
132                 st = rrdset_find_bytype("system", "processes");
133                 if (unlikely(!st)) {
134                     st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
135
136                     rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
137                     rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
138                 }
139                 else rrdset_next(st);
140
141                 rrddim_set(st, "running", vmtotal_data.t_rq);
142                 rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw));
143                 rrdset_done(st);
144             }
145
146         }
147     }
148
149     // --------------------------------------------------------------------
150
151     if (likely(do_processes)) {
152
153             st = rrdset_find_bytype("system", "processes");
154             if (unlikely(!st)) {
155                 st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
156
157                 rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
158                 rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
159             }
160             else rrdset_next(st);
161
162             rrddim_set(st, "running", vmtotal_data.t_rq);
163             rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw));
164             rrdset_done(st);
165         }
166
167     // --------------------------------------------------------------------
168
169     if (likely(do_cpu)) {
170         if (unlikely(CPUSTATES != 5)) {
171             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
172             do_cpu = 0;
173             error("DISABLED: system.cpu");
174         } else {
175             if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) {
176                 do_cpu = 0;
177                 error("DISABLED: system.cpu");
178             } else {
179
180                 st = rrdset_find_bytype("system", "cpu");
181                 if (unlikely(!st)) {
182                     st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
183
184                     rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
185                     rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
186                     rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
187                     rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
188                     rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
189                     rrddim_hide(st, "idle");
190                 }
191                 else rrdset_next(st);
192
193                 rrddim_set(st, "user", cp_time[0]);
194                 rrddim_set(st, "nice", cp_time[1]);
195                 rrddim_set(st, "system", cp_time[2]);
196                 rrddim_set(st, "interrupt", cp_time[3]);
197                 rrddim_set(st, "idle", cp_time[4]);
198                 rrdset_done(st);
199             }
200         }
201     }
202
203     // --------------------------------------------------------------------
204
205     if (likely(do_cpu_cores)) {
206         if (unlikely(CPUSTATES != 5)) {
207             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
208             do_cpu_cores = 0;
209             error("DISABLED: cpu.cpuXX");
210         } else {
211             if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) {
212                 do_cpu_cores = 0;
213                 error("DISABLED: cpu.cpuXX");
214             } else {
215                 pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * ncpus);
216
217                 for (i = 0; i < ncpus; i++) {
218                     if (unlikely(getsysctl("kern.cp_times", pcpu_cp_time, sizeof(cp_time) * ncpus))) {
219                         do_cpu_cores = 0;
220                         error("DISABLED: cpu.cpuXX");
221                         break;
222                     }
223                     if (unlikely(ncpus > 9999)) {
224                         error("FREEBSD: There are more than 4 digits in cpu cores number");
225                         do_cpu_cores = 0;
226                         error("DISABLED: cpu.cpuXX");
227                         break;
228                     }
229                     snprintfz(cpuid, 8, "cpu%d", i);
230
231                     st = rrdset_find_bytype("cpu", cpuid);
232                     if (unlikely(!st)) {
233                         st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization", "percentage", 1000, update_every, RRDSET_TYPE_STACKED);
234
235                         rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
236                         rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
237                         rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
238                         rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
239                         rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
240                         rrddim_hide(st, "idle");
241                     }
242                     else rrdset_next(st);
243
244                     rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]);
245                     rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]);
246                     rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]);
247                     rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]);
248                     rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]);
249                     rrdset_done(st);
250                 }
251             }
252         }
253     }
254
255     // --------------------------------------------------------------------
256
257     if (likely(do_interrupts)) {
258         if (unlikely(sysctlbyname("hw.intrcnt", NULL, &intrcnt_size, NULL, 0) == -1)) {
259             error("FREEBSD: sysctl(hw.intrcnt...) failed: %s", strerror(errno));
260             do_interrupts = 0;
261             error("DISABLED: system.intr");
262         } else {
263             nintr = intrcnt_size / sizeof(u_long);
264             intrcnt = reallocz(intrcnt, nintr * sizeof(u_long));
265             if (unlikely(getsysctl("hw.intrcnt", intrcnt, nintr * sizeof(u_long)))){
266                 do_interrupts = 0;
267                 error("DISABLED: system.intr");
268             } else {
269                 for (i = 0; i < nintr; i++)
270                     totalintr += intrcnt[i];
271
272                 st = rrdset_find_bytype("system", "intr");
273                 if (unlikely(!st)) {
274                     st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Device Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
275                     st->isdetail = 1;
276
277                     rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
278                 }
279                 else rrdset_next(st);
280
281                 rrddim_set(st, "interrupts", totalintr);
282                 rrdset_done(st);
283             }
284         }
285     }
286
287     // --------------------------------------------------------------------
288
289     if (likely(do_context)) {
290         if (unlikely(GETSYSCTL("vm.stats.sys.v_swtch", u_int_data))) {
291             do_context = 0;
292             error("DISABLED: system.ctxt");
293         } else {
294
295             st = rrdset_find_bytype("system", "ctxt");
296             if (unlikely(!st)) {
297                 st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
298
299                 rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
300             }
301             else rrdset_next(st);
302
303             rrddim_set(st, "switches", u_int_data);
304             rrdset_done(st);
305         }
306     }
307
308     // --------------------------------------------------------------------
309
310     if (likely(do_forks)) {
311         if (unlikely(GETSYSCTL("vm.stats.vm.v_forks", u_int_data))) {
312             do_forks = 0;
313             error("DISABLED: system.forks");
314         } else {
315
316             st = rrdset_find_bytype("system", "forks");
317             if (unlikely(!st)) {
318                 st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
319                 st->isdetail = 1;
320
321                 rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
322             }
323             else rrdset_next(st);
324
325             rrddim_set(st, "started", u_int_data);
326             rrdset_done(st);
327         }
328     }
329
330     // --------------------------------------------------------------------
331
332     if (likely(do_disk_io)) {
333         if (unlikely(GETSYSCTL("kern.devstat.numdevs", numdevs))) {
334             do_disk_io = 0;
335             error("DISABLED: disk.io");
336         } else {
337             devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures
338             if (unlikely(getsysctl("kern.devstat.all", devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs))) {
339                 do_disk_io = 0;
340                 error("DISABLED: disk.io");
341             } else {
342                 dstat = devstat_data +sizeof(long); // skip generation number
343                 collected_number total_disk_reads = 0;
344                 collected_number total_disk_writes = 0;
345
346                 for (i = 0; i < numdevs; i++) {
347                     if ((dstat[i].device_type == (DEVSTAT_TYPE_IF_SCSI | DEVSTAT_TYPE_DIRECT)) || (dstat[i].device_type == (DEVSTAT_TYPE_IF_IDE | DEVSTAT_TYPE_DIRECT))) {
348
349                         // --------------------------------------------------------------------
350
351                         st = rrdset_find_bytype(RRD_TYPE_DISK, dstat[i].device_name);
352                         if (unlikely(!st)) {
353                             st = rrdset_create(RRD_TYPE_DISK, dstat[i].device_name, NULL, dstat[i].device_name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
354
355                             rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
356                             rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
357                         }
358                         else rrdset_next(st);
359
360                         total_disk_reads += dstat[i].bytes[DEVSTAT_READ];
361                         total_disk_writes += dstat[i].bytes[DEVSTAT_WRITE];
362                         prev_dstat.bytes_read = rrddim_set(st, "reads", dstat[i].bytes[DEVSTAT_READ]);
363                         prev_dstat.bytes_write = rrddim_set(st, "writes", dstat[i].bytes[DEVSTAT_WRITE]);
364                         rrdset_done(st);
365
366                         // --------------------------------------------------------------------
367
368                         st = rrdset_find_bytype("disk_ops", dstat[i].device_name);
369                         if (unlikely(!st)) {
370                             st = rrdset_create("disk_ops", dstat[i].device_name, NULL, dstat[i].device_name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
371                             st->isdetail = 1;
372
373                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
374                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
375                         }
376                         else rrdset_next(st);
377
378                         prev_dstat.operations_read = rrddim_set(st, "reads", dstat[i].operations[DEVSTAT_READ]);
379                         prev_dstat.operations_write = rrddim_set(st, "writes", dstat[i].operations[DEVSTAT_WRITE]);
380                         rrdset_done(st);
381
382                         // --------------------------------------------------------------------
383
384                         st = rrdset_find_bytype("disk_qops", dstat[i].device_name);
385                         if (unlikely(!st)) {
386                             st = rrdset_create("disk_qops", dstat[i].device_name, NULL, dstat[i].device_name, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
387                             st->isdetail = 1;
388
389                             rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
390                         }
391                         else rrdset_next(st);
392
393                         rrddim_set(st, "operations", dstat[i].start_count - dstat[i].end_count);
394                         rrdset_done(st);
395
396                         // --------------------------------------------------------------------
397
398                         st = rrdset_find_bytype("disk_util", dstat[i].device_name);
399                         if (unlikely(!st)) {
400                             st = rrdset_create("disk_util", dstat[i].device_name, NULL, dstat[i].device_name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
401                             st->isdetail = 1;
402
403                             rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
404                         }
405                         else rrdset_next(st);
406
407                         cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE;
408                         prev_dstat.busy_time_ms = rrddim_set(st, "utilization", cur_dstat.busy_time_ms);
409                         rrdset_done(st);
410
411                         // --------------------------------------------------------------------
412
413                         st = rrdset_find_bytype("disk_iotime", dstat[i].device_name);
414                         if (unlikely(!st)) {
415                             st = rrdset_create("disk_iotime", dstat[i].device_name, NULL, dstat[i].device_name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
416                             st->isdetail = 1;
417
418                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
419                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
420                         }
421                         else rrdset_next(st);
422
423                         cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
424                         cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
425                         prev_dstat.duration_read_ms = rrddim_set(st, "reads", cur_dstat.duration_read_ms);
426                         prev_dstat.duration_write_ms = rrddim_set(st, "writes", cur_dstat.duration_write_ms);
427                         rrdset_done(st);
428
429                         // --------------------------------------------------------------------
430                         // calculate differential charts
431                         // only if this is not the first time we run
432
433                         if (likely(dt)) {
434
435                             // --------------------------------------------------------------------
436
437                             st = rrdset_find_bytype("disk_await", dstat[i].device_name);
438                             if (unlikely(!st)) {
439                                 st = rrdset_create("disk_await", dstat[i].device_name, NULL, dstat[i].device_name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
440                                 st->isdetail = 1;
441
442                                 rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
443                                 rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
444                             }
445                             else rrdset_next(st);
446
447                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? 
448                                 (cur_dstat.duration_read_ms - prev_dstat.duration_read_ms) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
449                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
450                                 (cur_dstat.duration_write_ms - prev_dstat.duration_write_ms) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
451                             rrdset_done(st);
452
453                             // --------------------------------------------------------------------
454
455                             st = rrdset_find_bytype("disk_avgsz", dstat[i].device_name);
456                             if (unlikely(!st)) {
457                                 st = rrdset_create("disk_avgsz", dstat[i].device_name, NULL, dstat[i].device_name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
458                                 st->isdetail = 1;
459
460                                 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
461                                 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
462                             }
463                             else rrdset_next(st);
464
465                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ?
466                                 (dstat[i].bytes[DEVSTAT_READ] - prev_dstat.bytes_read) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
467                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
468                                 (dstat[i].bytes[DEVSTAT_WRITE] - prev_dstat.bytes_write) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
469                             rrdset_done(st);
470
471                             // --------------------------------------------------------------------
472
473                             st = rrdset_find_bytype("disk_svctm", dstat[i].device_name);
474                             if (unlikely(!st)) {
475                                 st = rrdset_create("disk_svctm", dstat[i].device_name, NULL, dstat[i].device_name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
476                                 st->isdetail = 1;
477
478                                 rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
479                             }
480                             else rrdset_next(st);
481
482                             rrddim_set(st, "svctm", ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) ?
483                                 (cur_dstat.busy_time_ms - prev_dstat.busy_time_ms) / ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) : 0);
484                             rrdset_done(st);
485
486                         }
487
488                     }
489
490                     // --------------------------------------------------------------------
491
492                     st = rrdset_find_bytype("system", "io");
493                     if (unlikely(!st)) {
494                         st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
495                         rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
496                         rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
497                     }
498                     else rrdset_next(st);
499
500                     rrddim_set(st, "in", total_disk_reads);
501                     rrddim_set(st, "out", total_disk_writes);
502                     rrdset_done(st);
503                 }
504             }
505         }
506     }
507
508     return 0;
509 }