summaryrefslogtreecommitdiff
path: root/src/pmdas/linux/proc_meminfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/linux/proc_meminfo.c')
-rw-r--r--src/pmdas/linux/proc_meminfo.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/pmdas/linux/proc_meminfo.c b/src/pmdas/linux/proc_meminfo.c
new file mode 100644
index 0000000..471e2ce
--- /dev/null
+++ b/src/pmdas/linux/proc_meminfo.c
@@ -0,0 +1,188 @@
+/*
+ * Linux /proc/meminfo metrics cluster
+ *
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <ctype.h>
+#include "pmapi.h"
+#include "pmda.h"
+#include "indom.h"
+#include <sys/stat.h>
+#include "proc_meminfo.h"
+
+static proc_meminfo_t moff;
+extern size_t _pm_system_pagesize;
+
+static struct {
+ char *field;
+ int64_t *offset;
+} meminfo_fields[] = {
+ { "MemTotal", &moff.MemTotal },
+ { "MemFree", &moff.MemFree },
+ { "MemAvailable", &moff.MemAvailable },
+ { "MemShared", &moff.MemShared },
+ { "Buffers", &moff.Buffers },
+ { "Cached", &moff.Cached },
+ { "SwapCached", &moff.SwapCached },
+ { "Active", &moff.Active },
+ { "Inactive", &moff.Inactive },
+ { "Active(anon)", &moff.Active_anon },
+ { "Inactive(anon)", &moff.Inactive_anon },
+ { "Active(file)", &moff.Active_file },
+ { "Inactive(file)", &moff.Inactive_file },
+ { "Unevictable", &moff.Unevictable },
+ { "Mlocked", &moff.Mlocked },
+ { "HighTotal", &moff.HighTotal },
+ { "HighFree", &moff.HighFree },
+ { "LowTotal", &moff.LowTotal },
+ { "LowFree", &moff.LowFree },
+ { "MmapCopy", &moff.MmapCopy },
+ { "SwapTotal", &moff.SwapTotal },
+ { "SwapFree", &moff.SwapFree },
+ { "Dirty", &moff.Dirty },
+ { "Writeback", &moff.Writeback },
+ { "AnonPages", &moff.AnonPages },
+ { "Mapped", &moff.Mapped },
+ { "Shmem", &moff.Shmem },
+ { "Slab", &moff.Slab },
+ { "SReclaimable", &moff.SlabReclaimable },
+ { "SUnreclaim", &moff.SlabUnreclaimable },
+ { "KernelStack", &moff.KernelStack },
+ { "PageTables", &moff.PageTables },
+ { "Quicklists", &moff.Quicklists },
+ { "NFS_Unstable", &moff.NFS_Unstable },
+ { "Bounce", &moff.Bounce },
+ { "WritebackTmp", &moff.WritebackTmp },
+ { "CommitLimit", &moff.CommitLimit },
+ { "Committed_AS", &moff.Committed_AS },
+ { "VmallocTotal", &moff.VmallocTotal },
+ { "VmallocUsed", &moff.VmallocUsed },
+ { "VmallocChunk", &moff.VmallocChunk },
+ { "HardwareCorrupted", &moff.HardwareCorrupted },
+ { "AnonHugePages", &moff.AnonHugePages },
+ /* vendor kernel patches, some outdated now */
+ { "MemShared", &moff.MemShared },
+ { "ReverseMaps", &moff.ReverseMaps },
+ { "HugePages_Total", &moff.HugepagesTotal },
+ { "HugePages_Free", &moff.HugepagesFree },
+ { "HugePages_Rsvd", &moff.HugepagesRsvd },
+ { "HugePages_Surp", &moff.HugepagesSurp },
+ { "DirectMap4k", &moff.directMap4k },
+ { "DirectMap2M", &moff.directMap2M },
+ { "DirectMap1G", &moff.directMap1G },
+ { NULL, NULL }
+};
+
+#define MOFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)meminfo_fields[ii].offset - (__psint_t)&moff)
+
+int
+refresh_proc_meminfo(proc_meminfo_t *proc_meminfo)
+{
+ char buf[1024];
+ char *bufp;
+ int64_t *p;
+ int i;
+ FILE *fp;
+
+ for (i = 0; meminfo_fields[i].field != NULL; i++) {
+ p = MOFFSET(i, proc_meminfo);
+ *p = -1; /* marked as "no value available" */
+ }
+
+ if ((fp = linux_statsfile("/proc/meminfo", buf, sizeof(buf))) == NULL)
+ return -oserror();
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if ((bufp = strchr(buf, ':')) == NULL)
+ continue;
+ *bufp = '\0';
+ for (i=0; meminfo_fields[i].field != NULL; i++) {
+ if (strcmp(buf, meminfo_fields[i].field) != 0)
+ continue;
+ p = MOFFSET(i, proc_meminfo);
+ for (bufp++; *bufp; bufp++) {
+ if (isdigit((int)*bufp)) {
+ sscanf(bufp, "%llu", (unsigned long long *)p);
+ *p *= 1024; /* kbytes -> bytes */
+ break;
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+
+ /*
+ * MemAvailable is only in 3.x or later kernels but we can calculate it
+ * using other values, similar to upstream kernel commit 34e431b0ae.
+ * The environment variable is for QA purposes.
+ */
+ if (!MEMINFO_VALID_VALUE(proc_meminfo->MemAvailable) ||
+ getenv("PCP_QA_ESTIMATE_MEMAVAILABLE") != NULL) {
+ if (MEMINFO_VALID_VALUE(proc_meminfo->MemTotal) &&
+ MEMINFO_VALID_VALUE(proc_meminfo->MemFree) &&
+ MEMINFO_VALID_VALUE(proc_meminfo->Active_file) &&
+ MEMINFO_VALID_VALUE(proc_meminfo->Inactive_file) &&
+ MEMINFO_VALID_VALUE(proc_meminfo->SlabReclaimable)) {
+
+ int64_t pagecache;
+ int64_t wmark_low = 0;
+
+ /*
+ * sum for each zone->watermark[WMARK_LOW];
+ */
+ if ((fp = fopen("/proc/zoneinfo", "r")) != NULL) {
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if ((bufp = strstr(buf, "low ")) != NULL) {
+ int64_t low;
+ if (sscanf(bufp+4, "%lld", (long long int *)&low) == 1)
+ wmark_low += low;
+ }
+ }
+ fclose(fp);
+ wmark_low *= _pm_system_pagesize;
+ }
+
+ /*
+ * Free memory cannot be taken below the low watermark, before the
+ * system starts swapping.
+ */
+ proc_meminfo->MemAvailable = proc_meminfo->MemFree - wmark_low;
+
+ /*
+ * Not all the page cache can be freed, otherwise the system will
+ * start swapping. Assume at least half of the page cache, or the
+ * low watermark worth of cache, needs to stay.
+ */
+ pagecache = proc_meminfo->Active_file + proc_meminfo->Inactive_file;
+ pagecache -= MIN(pagecache / 2, wmark_low);
+ proc_meminfo->MemAvailable += pagecache;
+
+ /*
+ * Part of the reclaimable slab consists of items that are in use,
+ * and cannot be freed. Cap this estimate at the low watermark.
+ */
+ proc_meminfo->MemAvailable += proc_meminfo->SlabReclaimable;
+ proc_meminfo->MemAvailable -= MIN(proc_meminfo->SlabReclaimable / 2, wmark_low);
+
+ if (proc_meminfo->MemAvailable < 0)
+ proc_meminfo->MemAvailable = 0;
+ }
+ }
+
+ /* success */
+ return 0;
+}