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