diff options
Diffstat (limited to 'src/pmdas/linux/proc_meminfo.c')
-rw-r--r-- | src/pmdas/linux/proc_meminfo.c | 188 |
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; +} |