Format BSD constrained sysctl usage
[collectd.git] / src / swap.c
1 /**
2  * collectd - src/swap.c
3  * Copyright (C) 2005-2014  Florian octo Forster
4  * Copyright (C) 2009       Stefan Völkel
5  * Copyright (C) 2009       Manuel Sanmartin
6  * Copyright (C) 2010       Aurélien Reynaud
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Florian octo Forster <octo at collectd.org>
23  *   Manuel Sanmartin
24  *   Aurélien Reynaud <collectd at wattapower.net>
25  **/
26
27 #if HAVE_CONFIG_H
28 #include "config.h"
29 #undef HAVE_CONFIG_H
30 #endif
31 /* avoid swap.h error "Cannot use swapctl in the large files compilation
32  * environment" */
33 #if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
34 #undef _FILE_OFFSET_BITS
35 #undef _LARGEFILE64_SOURCE
36 #endif
37
38 #include "collectd.h"
39
40 #include "plugin.h"
41 #include "utils/common/common.h"
42
43 #if HAVE_SYS_SWAP_H
44 #include <sys/swap.h>
45 #endif
46 #if HAVE_VM_ANON_H
47 #include <vm/anon.h>
48 #endif
49 #if HAVE_SYS_PARAM_H
50 #include <sys/param.h>
51 #endif
52 #if (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)) ||              \
53     defined(__OpenBSD__)
54 /* implies BSD variant */
55 #include <sys/sysctl.h>
56 #endif
57 #if HAVE_SYS_DKSTAT_H
58 #include <sys/dkstat.h>
59 #endif
60 #if HAVE_KVM_H
61 #include <kvm.h>
62 #endif
63
64 #if HAVE_STATGRAB_H
65 #include <statgrab.h>
66 #endif
67
68 #if HAVE_PERFSTAT
69 #include <libperfstat.h>
70 #include <sys/protosw.h>
71 #endif
72
73 #undef MAX
74 #define MAX(x, y) ((x) > (y) ? (x) : (y))
75
76 #if KERNEL_LINUX
77 #define SWAP_HAVE_REPORT_BY_DEVICE 1
78 static derive_t pagesize;
79 static bool report_bytes;
80 static bool report_by_device;
81 /* #endif KERNEL_LINUX */
82
83 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
84 #define SWAP_HAVE_REPORT_BY_DEVICE 1
85 static derive_t pagesize;
86 static bool report_by_device;
87 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
88
89 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
90 /* No global variables */
91 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
92
93 #elif defined(VM_SWAPUSAGE)
94 /* No global variables */
95 /* #endif defined(VM_SWAPUSAGE) */
96
97 #elif HAVE_LIBKVM_GETSWAPINFO
98 static kvm_t *kvm_obj;
99 int kvm_pagesize;
100 /* #endif HAVE_LIBKVM_GETSWAPINFO */
101
102 #elif HAVE_LIBSTATGRAB
103 /* No global variables */
104 /* #endif HAVE_LIBSTATGRAB */
105
106 #elif HAVE_PERFSTAT
107 static int pagesize;
108 /*# endif HAVE_PERFSTAT */
109
110 #else
111 #error "No applicable input method."
112 #endif /* HAVE_LIBSTATGRAB */
113
114 static bool values_absolute = true;
115 static bool values_percentage;
116 static bool report_io = true;
117
118 static int swap_config(oconfig_item_t *ci) /* {{{ */
119 {
120   for (int i = 0; i < ci->children_num; i++) {
121     oconfig_item_t *child = ci->children + i;
122     if (strcasecmp("ReportBytes", child->key) == 0)
123 #if KERNEL_LINUX
124       cf_util_get_boolean(child, &report_bytes);
125 #else
126       WARNING("swap plugin: The \"ReportBytes\" option "
127               "is only valid under Linux. "
128               "The option is going to be ignored.");
129 #endif
130     else if (strcasecmp("ReportByDevice", child->key) == 0)
131 #if SWAP_HAVE_REPORT_BY_DEVICE
132       cf_util_get_boolean(child, &report_by_device);
133 #else
134       WARNING("swap plugin: The \"ReportByDevice\" option "
135               "is not supported on this platform. "
136               "The option is going to be ignored.");
137 #endif /* SWAP_HAVE_REPORT_BY_DEVICE */
138     else if (strcasecmp("ValuesAbsolute", child->key) == 0)
139       cf_util_get_boolean(child, &values_absolute);
140     else if (strcasecmp("ValuesPercentage", child->key) == 0)
141       cf_util_get_boolean(child, &values_percentage);
142     else if (strcasecmp("ReportIO", child->key) == 0)
143       cf_util_get_boolean(child, &report_io);
144     else
145       WARNING("swap plugin: Unknown config option: \"%s\"", child->key);
146   }
147
148   return 0;
149 } /* }}} int swap_config */
150
151 static int swap_init(void) /* {{{ */
152 {
153 #if KERNEL_LINUX
154   pagesize = (derive_t)sysconf(_SC_PAGESIZE);
155   /* #endif KERNEL_LINUX */
156
157 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
158   /* getpagesize(3C) tells me this does not fail.. */
159   pagesize = (derive_t)getpagesize();
160   /* #endif HAVE_SWAPCTL */
161
162 #elif defined(VM_SWAPUSAGE)
163 /* No init stuff */
164 /* #endif defined(VM_SWAPUSAGE) */
165
166 #elif HAVE_LIBKVM_GETSWAPINFO
167   char errbuf[_POSIX2_LINE_MAX];
168
169   if (kvm_obj != NULL) {
170     kvm_close(kvm_obj);
171     kvm_obj = NULL;
172   }
173
174   kvm_pagesize = getpagesize();
175
176   kvm_obj = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
177
178   if (kvm_obj == NULL) {
179     ERROR("swap plugin: kvm_openfiles failed, %s", errbuf);
180     return -1;
181   }
182     /* #endif HAVE_LIBKVM_GETSWAPINFO */
183
184 #elif HAVE_LIBSTATGRAB
185 /* No init stuff */
186 /* #endif HAVE_LIBSTATGRAB */
187
188 #elif HAVE_PERFSTAT
189   pagesize = getpagesize();
190 #endif /* HAVE_PERFSTAT */
191
192   return 0;
193 } /* }}} int swap_init */
194
195 static void swap_submit_usage(char const *plugin_instance, /* {{{ */
196                               gauge_t used, gauge_t free,
197                               char const *other_name, gauge_t other_value) {
198   value_list_t vl = VALUE_LIST_INIT;
199
200   vl.values = &(value_t){.gauge = NAN};
201   vl.values_len = 1;
202   sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
203   if (plugin_instance != NULL)
204     sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
205   sstrncpy(vl.type, "swap", sizeof(vl.type));
206
207   if (values_absolute)
208     plugin_dispatch_multivalue(&vl, false, DS_TYPE_GAUGE, "used", used, "free",
209                                free, other_name, other_value, NULL);
210   if (values_percentage)
211     plugin_dispatch_multivalue(&vl, true, DS_TYPE_GAUGE, "used", used, "free",
212                                free, other_name, other_value, NULL);
213 } /* }}} void swap_submit_usage */
214
215 #if KERNEL_LINUX || HAVE_PERFSTAT
216 __attribute__((nonnull(1))) static void
217 swap_submit_derive(char const *type_instance, /* {{{ */
218                    derive_t value) {
219   value_list_t vl = VALUE_LIST_INIT;
220
221   vl.values = &(value_t){.derive = value};
222   vl.values_len = 1;
223   sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
224   sstrncpy(vl.type, "swap_io", sizeof(vl.type));
225   sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
226
227   plugin_dispatch_values(&vl);
228 } /* }}} void swap_submit_derive */
229 #endif
230
231 #if KERNEL_LINUX
232 static int swap_read_separate(void) /* {{{ */
233 {
234   FILE *fh;
235   char buffer[1024];
236
237   fh = fopen("/proc/swaps", "r");
238   if (fh == NULL) {
239     WARNING("swap plugin: fopen (/proc/swaps) failed: %s", STRERRNO);
240     return -1;
241   }
242
243   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
244     char *fields[8];
245     int numfields;
246     char *endptr;
247
248     char path[PATH_MAX];
249     gauge_t total;
250     gauge_t used;
251
252     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
253     if (numfields != 5)
254       continue;
255
256     sstrncpy(path, fields[0], sizeof(path));
257     escape_slashes(path, sizeof(path));
258
259     errno = 0;
260     endptr = NULL;
261     total = strtod(fields[2], &endptr);
262     if ((endptr == fields[2]) || (errno != 0))
263       continue;
264
265     errno = 0;
266     endptr = NULL;
267     used = strtod(fields[3], &endptr);
268     if ((endptr == fields[3]) || (errno != 0))
269       continue;
270
271     if (total < used)
272       continue;
273
274     swap_submit_usage(path, used * 1024.0, (total - used) * 1024.0, NULL, NAN);
275   }
276
277   fclose(fh);
278
279   return 0;
280 } /* }}} int swap_read_separate */
281
282 static int swap_read_combined(void) /* {{{ */
283 {
284   FILE *fh;
285   char buffer[1024];
286
287   gauge_t swap_used = NAN;
288   gauge_t swap_cached = NAN;
289   gauge_t swap_free = NAN;
290   gauge_t swap_total = NAN;
291
292   fh = fopen("/proc/meminfo", "r");
293   if (fh == NULL) {
294     WARNING("swap plugin: fopen (/proc/meminfo) failed: %s", STRERRNO);
295     return -1;
296   }
297
298   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
299     char *fields[8];
300     int numfields;
301
302     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
303     if (numfields < 2)
304       continue;
305
306     if (strcasecmp(fields[0], "SwapTotal:") == 0)
307       strtogauge(fields[1], &swap_total);
308     else if (strcasecmp(fields[0], "SwapFree:") == 0)
309       strtogauge(fields[1], &swap_free);
310     else if (strcasecmp(fields[0], "SwapCached:") == 0)
311       strtogauge(fields[1], &swap_cached);
312   }
313
314   fclose(fh);
315
316   if (isnan(swap_total) || isnan(swap_free))
317     return ENOENT;
318
319   /* Some systems, OpenVZ for example, don't provide SwapCached. */
320   if (isnan(swap_cached))
321     swap_used = swap_total - swap_free;
322   else
323     swap_used = swap_total - (swap_free + swap_cached);
324   assert(!isnan(swap_used));
325
326   if (swap_used < 0.0)
327     return EINVAL;
328
329   swap_submit_usage(NULL, swap_used * 1024.0, swap_free * 1024.0,
330                     isnan(swap_cached) ? NULL : "cached",
331                     isnan(swap_cached) ? NAN : swap_cached * 1024.0);
332   return 0;
333 } /* }}} int swap_read_combined */
334
335 static int swap_read_io(void) /* {{{ */
336 {
337   char buffer[1024];
338
339   uint8_t have_data = 0;
340   derive_t swap_in = 0;
341   derive_t swap_out = 0;
342
343   FILE *fh = fopen("/proc/vmstat", "r");
344   if (fh == NULL) {
345     WARNING("swap: fopen(/proc/vmstat): %s", STRERRNO);
346     return -1;
347   }
348
349   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
350     char *fields[8];
351     int numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
352
353     if (numfields != 2)
354       continue;
355
356     if (strcasecmp("pswpin", fields[0]) == 0) {
357       strtoderive(fields[1], &swap_in);
358       have_data |= 0x01;
359     } else if (strcasecmp("pswpout", fields[0]) == 0) {
360       strtoderive(fields[1], &swap_out);
361       have_data |= 0x02;
362     }
363   } /* while (fgets) */
364
365   fclose(fh);
366
367   if (have_data != 0x03)
368     return ENOENT;
369
370   if (report_bytes) {
371     swap_in = swap_in * pagesize;
372     swap_out = swap_out * pagesize;
373   }
374
375   swap_submit_derive("in", swap_in);
376   swap_submit_derive("out", swap_out);
377
378   return 0;
379 } /* }}} int swap_read_io */
380
381 static int swap_read(void) /* {{{ */
382 {
383   if (report_by_device)
384     swap_read_separate();
385   else
386     swap_read_combined();
387
388   if (report_io)
389     swap_read_io();
390
391   return 0;
392 } /* }}} int swap_read */
393 /* #endif KERNEL_LINUX */
394
395 /*
396  * Under Solaris, two mechanisms can be used to read swap statistics, swapctl
397  * and kstat. The former reads physical space used on a device, the latter
398  * reports the view from the virtual memory system. It was decided that the
399  * kstat-based information should be moved to the "vmem" plugin, but nobody
400  * with enough Solaris experience was available at that time to do this. The
401  * code below is still there for your reference but it won't be activated in
402  * *this* plugin again. --octo
403  */
404 #elif 0 && HAVE_LIBKSTAT
405 /* kstat-based read function */
406 static int swap_read_kstat(void) /* {{{ */
407 {
408   gauge_t swap_alloc;
409   gauge_t swap_resv;
410   gauge_t swap_avail;
411
412   struct anoninfo ai;
413
414   if (swapctl(SC_AINFO, &ai) == -1) {
415     ERROR("swap plugin: swapctl failed: %s", STRERRNO);
416     return -1;
417   }
418
419   /*
420    * Calculations from:
421    * http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/swap/swap.c
422    * Also see:
423    * http://www.itworld.com/Comp/2377/UIR980701perf/ (outdated?)
424    * /usr/include/vm/anon.h
425    *
426    * In short, swap -s shows: allocated + reserved = used, available
427    *
428    * However, Solaris does not allow to allocated/reserved more than the
429    * available swap (physical memory + disk swap), so the pedant may
430    * prefer: allocated + unallocated = reserved, available
431    *
432    * We map the above to: used + resv = n/a, free
433    *
434    * Does your brain hurt yet?  - Christophe Kalt
435    *
436    * Oh, and in case you wonder,
437    * swap_alloc = pagesize * ( ai.ani_max - ai.ani_free );
438    * can suffer from a 32bit overflow.
439    */
440   swap_alloc = (gauge_t)((ai.ani_max - ai.ani_free) * pagesize);
441   swap_resv = (gauge_t)((ai.ani_resv + ai.ani_free - ai.ani_max) * pagesize);
442   swap_avail = (gauge_t)((ai.ani_max - ai.ani_resv) * pagesize);
443
444   swap_submit_usage(NULL, swap_alloc, swap_avail, "reserved", swap_resv);
445   return 0;
446 } /* }}} int swap_read_kstat */
447   /* #endif 0 && HAVE_LIBKSTAT */
448
449 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
450 /* swapctl-based read function */
451 static int swap_read(void) /* {{{ */
452 {
453   swaptbl_t *s;
454   char *s_paths;
455   int swap_num;
456   int status;
457
458   gauge_t avail = 0;
459   gauge_t total = 0;
460
461   swap_num = swapctl(SC_GETNSWP, NULL);
462   if (swap_num < 0) {
463     ERROR("swap plugin: swapctl (SC_GETNSWP) failed with status %i.", swap_num);
464     return -1;
465   } else if (swap_num == 0)
466     return 0;
467
468   /* Allocate and initialize the swaptbl_t structure */
469   s = malloc(swap_num * sizeof(swapent_t) + sizeof(struct swaptable));
470   if (s == NULL) {
471     ERROR("swap plugin: malloc failed.");
472     return -1;
473   }
474
475   /* Memory to store the path names. We only use these paths when the
476    * separate option has been configured, but it's easier to just
477    * allocate enough memory in any case. */
478   s_paths = calloc(swap_num, PATH_MAX);
479   if (s_paths == NULL) {
480     ERROR("swap plugin: calloc failed.");
481     sfree(s);
482     return -1;
483   }
484   for (int i = 0; i < swap_num; i++)
485     s->swt_ent[i].ste_path = s_paths + (i * PATH_MAX);
486   s->swt_n = swap_num;
487
488   status = swapctl(SC_LIST, s);
489   if (status < 0) {
490     ERROR("swap plugin: swapctl (SC_LIST) failed: %s", STRERRNO);
491     sfree(s_paths);
492     sfree(s);
493     return -1;
494   } else if (swap_num < status) {
495     /* more elements returned than requested */
496     ERROR("swap plugin: I allocated memory for %i structure%s, "
497           "but swapctl(2) claims to have returned %i. "
498           "I'm confused and will give up.",
499           swap_num, (swap_num == 1) ? "" : "s", status);
500     sfree(s_paths);
501     sfree(s);
502     return -1;
503   } else if (swap_num > status)
504     /* less elements returned than requested */
505     swap_num = status;
506
507   for (int i = 0; i < swap_num; i++) {
508     char path[PATH_MAX];
509     gauge_t this_total;
510     gauge_t this_avail;
511
512     if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0)
513       continue;
514
515     this_total = (gauge_t)(s->swt_ent[i].ste_pages * pagesize);
516     this_avail = (gauge_t)(s->swt_ent[i].ste_free * pagesize);
517
518     /* Shortcut for the "combined" setting (default) */
519     if (!report_by_device) {
520       avail += this_avail;
521       total += this_total;
522       continue;
523     }
524
525     sstrncpy(path, s->swt_ent[i].ste_path, sizeof(path));
526     escape_slashes(path, sizeof(path));
527
528     swap_submit_usage(path, this_total - this_avail, this_avail, NULL, NAN);
529   } /* for (swap_num) */
530
531   if (total < avail) {
532     ERROR(
533         "swap plugin: Total swap space (%g) is less than free swap space (%g).",
534         total, avail);
535     sfree(s_paths);
536     sfree(s);
537     return -1;
538   }
539
540   /* If the "separate" option was specified (report_by_device == true) all
541    * values have already been dispatched from within the loop. */
542   if (!report_by_device)
543     swap_submit_usage(NULL, total - avail, avail, NULL, NAN);
544
545   sfree(s_paths);
546   sfree(s);
547   return 0;
548 } /* }}} int swap_read */
549   /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
550
551 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
552 static int swap_read(void) /* {{{ */
553 {
554   struct swapent *swap_entries;
555   int swap_num;
556   int status;
557
558   gauge_t used = 0;
559   gauge_t total = 0;
560
561   swap_num = swapctl(SWAP_NSWAP, NULL, 0);
562   if (swap_num < 0) {
563     ERROR("swap plugin: swapctl (SWAP_NSWAP) failed with status %i.", swap_num);
564     return -1;
565   } else if (swap_num == 0)
566     return 0;
567
568   swap_entries = calloc(swap_num, sizeof(*swap_entries));
569   if (swap_entries == NULL) {
570     ERROR("swap plugin: calloc failed.");
571     return -1;
572   }
573
574   status = swapctl(SWAP_STATS, swap_entries, swap_num);
575   if (status != swap_num) {
576     ERROR("swap plugin: swapctl (SWAP_STATS) failed with status %i.", status);
577     sfree(swap_entries);
578     return -1;
579   }
580
581 #if defined(DEV_BSIZE) && (DEV_BSIZE > 0)
582 #define C_SWAP_BLOCK_SIZE ((gauge_t)DEV_BSIZE)
583 #else
584 #define C_SWAP_BLOCK_SIZE 512.0
585 #endif
586
587   /* TODO: Report per-device stats. The path name is available from
588    * swap_entries[i].se_path */
589   for (int i = 0; i < swap_num; i++) {
590     if ((swap_entries[i].se_flags & SWF_ENABLE) == 0)
591       continue;
592
593     used += ((gauge_t)swap_entries[i].se_inuse) * C_SWAP_BLOCK_SIZE;
594     total += ((gauge_t)swap_entries[i].se_nblks) * C_SWAP_BLOCK_SIZE;
595   }
596
597   if (total < used) {
598     ERROR(
599         "swap plugin: Total swap space (%g) is less than used swap space (%g).",
600         total, used);
601     sfree(swap_entries);
602     return -1;
603   }
604
605   swap_submit_usage(NULL, used, total - used, NULL, NAN);
606
607   sfree(swap_entries);
608   return 0;
609 } /* }}} int swap_read */
610   /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
611
612 #elif defined(VM_SWAPUSAGE)
613 static int swap_read(void) /* {{{ */
614 {
615   int mib[3];
616   size_t mib_len;
617   struct xsw_usage sw_usage;
618   size_t sw_usage_len;
619
620   mib_len = 2;
621   mib[0] = CTL_VM;
622   mib[1] = VM_SWAPUSAGE;
623
624   sw_usage_len = sizeof(struct xsw_usage);
625
626   if (sysctl(mib, mib_len, &sw_usage, &sw_usage_len, NULL, 0) != 0)
627     return -1;
628
629   /* The returned values are bytes. */
630   swap_submit_usage(NULL, (gauge_t)sw_usage.xsu_used,
631                     (gauge_t)sw_usage.xsu_avail, NULL, NAN);
632
633   return 0;
634 } /* }}} int swap_read */
635   /* #endif VM_SWAPUSAGE */
636
637 #elif HAVE_LIBKVM_GETSWAPINFO
638 static int swap_read(void) /* {{{ */
639 {
640   struct kvm_swap data_s;
641   int status;
642
643   gauge_t used;
644   gauge_t total;
645
646   if (kvm_obj == NULL)
647     return -1;
648
649   /* only one structure => only get the grand total, no details */
650   status = kvm_getswapinfo(kvm_obj, &data_s, 1, 0);
651   if (status == -1)
652     return -1;
653
654   total = (gauge_t)data_s.ksw_total;
655   used = (gauge_t)data_s.ksw_used;
656
657   total *= (gauge_t)kvm_pagesize;
658   used *= (gauge_t)kvm_pagesize;
659
660   swap_submit_usage(NULL, used, total - used, NULL, NAN);
661
662   return 0;
663 } /* }}} int swap_read */
664   /* #endif HAVE_LIBKVM_GETSWAPINFO */
665
666 #elif HAVE_LIBSTATGRAB
667 static int swap_read(void) /* {{{ */
668 {
669   sg_swap_stats *swap;
670
671   swap = sg_get_swap_stats();
672   if (swap == NULL)
673     return -1;
674
675   swap_submit_usage(NULL, (gauge_t)swap->used, (gauge_t)swap->free, NULL, NAN);
676
677   return 0;
678 } /* }}} int swap_read */
679   /* #endif  HAVE_LIBSTATGRAB */
680
681 #elif HAVE_PERFSTAT
682 static int swap_read(void) /* {{{ */
683 {
684   perfstat_memory_total_t pmemory = {0};
685   int status;
686
687   gauge_t total;
688   gauge_t free;
689   gauge_t reserved;
690
691   status =
692       perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1);
693   if (status < 0) {
694     WARNING("swap plugin: perfstat_memory_total failed: %s", STRERRNO);
695     return -1;
696   }
697
698   total = (gauge_t)(pmemory.pgsp_total * pagesize);
699   free = (gauge_t)(pmemory.pgsp_free * pagesize);
700   reserved = (gauge_t)(pmemory.pgsp_rsvd * pagesize);
701
702   swap_submit_usage(NULL, total - free, free, "reserved", reserved);
703
704   if (report_io) {
705     swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
706     swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
707   }
708
709   return 0;
710 } /* }}} int swap_read */
711 #endif /* HAVE_PERFSTAT */
712
713 void module_register(void) {
714   plugin_register_complex_config("swap", swap_config);
715   plugin_register_init("swap", swap_init);
716   plugin_register_read("swap", swap_read);
717 } /* void module_register */