with this patch,container's meminfo will be shown based on containers' mem cgroup. Right now,it's impossible to virtualize all values in meminfo, I collect some values such as MemTotal,MemFree,Cached,Active, Inactive,Active(anon),Inactive(anon),Active(file),Inactive(anon), Active(file),Inactive(file),Unevictable,SwapTotal,SwapFree. if I miss something, please let me know. Signed-off-by: Gao feng <gaofeng@xxxxxxxxxxxxxx> --- src/lxc/lxc_cgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++ src/lxc/lxc_cgroup.h | 3 +- src/lxc/lxc_fuse.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/lxc/lxc_fuse.h | 14 ++++ 4 files changed, 332 insertions(+), 6 deletions(-) diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 9a5ba1a..4c32368 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -23,6 +23,7 @@ #include "lxc_cgroup.h" #include "lxc_container.h" +#include "virfile.h" #include "virterror_internal.h" #include "logging.h" #include "memory.h" @@ -138,6 +139,148 @@ cleanup: } +static int virLXCCgroupGetMemSwapUsage(virCgroupPtr cgroup, + virLXCMeminfoPtr meminfo) +{ + return virCgroupGetMemSwapUsage(cgroup, &meminfo->swapusage); +} + + +static int virLXCCgroupGetMemSwapTotal(virCgroupPtr cgroup, + virLXCMeminfoPtr meminfo) +{ + return virCgroupGetMemSwapHardLimit(cgroup, &meminfo->swaptotal); +} + + +static int virLXCCgroupGetMemUsage(virCgroupPtr cgroup, + virLXCMeminfoPtr meminfo) +{ + int ret; + unsigned long memUsage; + + ret = virCgroupGetMemoryUsage(cgroup, &memUsage); + meminfo->memusage = (unsigned long long) memUsage; + + return ret; +} + + +static int virLXCCgroupGetMemTotal(virCgroupPtr cgroup, + virLXCMeminfoPtr meminfo) +{ + return virCgroupGetMemoryHardLimit(cgroup, &meminfo->memtotal); +} + + +static int virLXCCgroupGetMemStat(virCgroupPtr cgroup, + virLXCMeminfoPtr meminfo) +{ + int ret = 0; + FILE *statfd = NULL; + char *statFile = NULL; + char *line = NULL; + size_t n; + + ret = virCgroupPathOfController(cgroup, VIR_CGROUP_CONTROLLER_MEMORY, + "memory.stat", &statFile); + if (ret != 0) { + virReportSystemError(-ret, "%s", + _("cannot get the path of MEMORY cgroup controller")); + return ret; + } + + statfd = fopen(statFile, "r"); + if (statfd == NULL) { + ret = -errno; + goto cleanup; + } + + while (getline(&line, &n, statfd) > 0) { + + char *value = strchr(line, ' '); + char *nl = value ? strchr(line, '\n') : NULL; + unsigned long long stat_value; + + if (!value) + continue; + + if (nl) + *nl = '\0'; + + *value = '\0'; + + if (virStrToLong_ull(value + 1, NULL, 10, &stat_value) < 0) { + ret = -EINVAL; + goto cleanup; + } + if (STREQ(line, "cache")) + meminfo->cached = stat_value >> 10; + else if (STREQ(line, "inactive_anon")) + meminfo->inactive_anon = stat_value >> 10; + else if (STREQ(line, "active_anon")) + meminfo->active_anon = stat_value >> 10; + else if (STREQ(line, "inactive_file")) + meminfo->inactive_file = stat_value >> 10; + else if (STREQ(line, "active_file")) + meminfo->active_file = stat_value >> 10; + else if (STREQ(line, "unevictable")) + meminfo->unevictable = stat_value >> 10; + } + ret = 0; + +cleanup: + VIR_FREE(line); + VIR_FREE(statFile); + VIR_FORCE_FCLOSE(statfd); + return ret; +} + + +int virLXCCgroupGetMeminfo(virLXCMeminfoPtr meminfo) +{ + int ret; + virCgroupPtr cgroup; + + ret = virCgroupGetAppRoot(&cgroup); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get cgroup for container")); + return ret; + } + + ret = virLXCCgroupGetMemStat(cgroup, meminfo); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat info")); + goto cleanup; + } + + ret = virLXCCgroupGetMemTotal(cgroup, meminfo); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup total")); + goto cleanup; + } + + ret = virLXCCgroupGetMemUsage(cgroup, meminfo); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat usage")); + goto cleanup; + } + + virLXCCgroupGetMemSwapTotal(cgroup, meminfo); + virLXCCgroupGetMemSwapUsage(cgroup, meminfo); + + ret = 0; +cleanup: + virCgroupFree(&cgroup); + return ret; +} + + + typedef struct _virLXCCgroupDevicePolicy virLXCCgroupDevicePolicy; typedef virLXCCgroupDevicePolicy *virLXCCgroupDevicePolicyPtr; diff --git a/src/lxc/lxc_cgroup.h b/src/lxc/lxc_cgroup.h index 6fc68df..6961943 100644 --- a/src/lxc/lxc_cgroup.h +++ b/src/lxc/lxc_cgroup.h @@ -23,7 +23,8 @@ # define __VIR_LXC_CGROUP_H__ # include "domain_conf.h" +# include "lxc_fuse.h" int virLXCCgroupSetup(virDomainDefPtr def); - +int virLXCCgroupGetMeminfo(virLXCMeminfoPtr meminfo); #endif /* __VIR_LXC_CGROUP_H__ */ diff --git a/src/lxc/lxc_fuse.c b/src/lxc/lxc_fuse.c index 6d621ee..5a514d6 100644 --- a/src/lxc/lxc_fuse.c +++ b/src/lxc/lxc_fuse.c @@ -30,26 +30,54 @@ #include <mntent.h> #include "lxc_fuse.h" +#include "lxc_cgroup.h" #include "virterror_internal.h" #include "logging.h" +#include "virfile.h" +#include "buf.h" #define VIR_FROM_THIS VIR_FROM_LXC #if HAVE_FUSE +static const char *fuse_meminfo_path = "/meminfo"; + static int lxcProcGetattr(const char *path, struct stat *stbuf) { - int res = 0; + int res; + char *mempath = NULL; + struct stat sb; memset(stbuf, 0, sizeof(struct stat)); + if (virAsprintf(&mempath, "/proc/%s", path) < 0) { + virReportOOMError(); + return -errno; + } + + res = 0; if (STREQ(path, "/")) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; - } else { + } else if (STREQ(path, fuse_meminfo_path)) { + if (stat(mempath, &sb) < 0) { + res = -errno; + goto cleanup; + } + + stbuf->st_mode = sb.st_mode; + stbuf->st_nlink = 1; + stbuf->st_blksize = sb.st_blksize; + stbuf->st_blocks = sb.st_blocks; + stbuf->st_size = sb.st_size; + stbuf->st_atime = sb.st_atime; + stbuf->st_ctime = sb.st_ctime; + stbuf->st_mtime = sb.st_mtime; + } else res = -ENOENT; - } +cleanup: + VIR_FREE(mempath); return res; } @@ -63,6 +91,7 @@ static int lxcProcReaddir(const char *path, void *buf, filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); + filler(buf, fuse_meminfo_path + 1, NULL, 0); return 0; } @@ -70,7 +99,127 @@ static int lxcProcReaddir(const char *path, void *buf, static int lxcProcOpen(const char *path ATTRIBUTE_UNUSED, struct fuse_file_info *fi ATTRIBUTE_UNUSED) { - return -ENOENT; + if (!STREQ(path, fuse_meminfo_path)) + return -ENOENT; + + if ((fi->flags & 3) != O_RDONLY) + return -EACCES; + + return 0; +} + +static int lxcProcHostRead(char *path, char *buf, size_t size, off_t offset) +{ + int fd; + int res; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -errno; + + if ((res = pread(fd, buf, size, offset)) < 0) + res = -errno; + + VIR_FORCE_CLOSE(fd); + return res; +} + +static int lxcProcReadMeminfo(char *hostpath, virDomainDefPtr def, + char *buf, size_t size, off_t offset) +{ + int copied = 0; + int res; + FILE *fd = NULL; + char *line = NULL; + size_t n; + struct virLXCMeminfo meminfo; + virBuffer buffer = VIR_BUFFER_INITIALIZER; + virBufferPtr new_meminfo = &buffer; + + if ((res = virLXCCgroupGetMeminfo(&meminfo)) < 0) + return res; + + fd = fopen(hostpath, "r"); + if (fd == NULL) { + virReportSystemError(errno, _("Cannot open %s"), hostpath); + res = -errno; + goto cleanup; + } + + if (fseek(fd, offset, SEEK_SET) < 0) { + virReportSystemError(errno, "%s", _("fseek failed")); + res = -errno; + goto cleanup; + } + + res = -1; + while (copied < size && getline(&line, &n, fd) > 0) { + char *ptr = strchr(line, ':'); + if (ptr) { + *ptr = '\0'; + + if (STREQ(line, "MemTotal") && + (def->mem.hard_limit || def->mem.max_balloon)) + virBufferAsprintf(new_meminfo, "MemTotal: %8llu KB\n", + meminfo.memtotal); + else if (STREQ(line, "MemFree") && + (def->mem.hard_limit || def->mem.max_balloon)) + virBufferAsprintf(new_meminfo, "MemFree: %8llu KB\n", + (meminfo.memtotal - meminfo.memusage)); + else if (STREQ(line, "Buffers")) + virBufferAsprintf(new_meminfo, "Buffers: %8d KB\n", 0); + else if (STREQ(line, "Cached")) + virBufferAsprintf(new_meminfo, "Cached: %8llu KB\n", + meminfo.cached); + else if (STREQ(line, "Active")) + virBufferAsprintf(new_meminfo, "Active: %8llu KB\n", + (meminfo.active_anon + meminfo.active_file)); + else if (STREQ(line, "Inactive")) + virBufferAsprintf(new_meminfo, "Inactive: %8llu KB\n", + (meminfo.inactive_anon + meminfo.inactive_file)); + else if (STREQ(line, "Active(anon)")) + virBufferAsprintf(new_meminfo, "Active(anon): %8llu KB\n", + meminfo.active_anon); + else if (STREQ(line, "Inactive(anon)")) + virBufferAsprintf(new_meminfo, "Inactive(anon): %8llu KB\n", + meminfo.inactive_anon); + else if (STREQ(line, "Active(file)")) + virBufferAsprintf(new_meminfo, "Active(file): %8llu KB\n", + meminfo.active_file); + else if (STREQ(line, "Inactive(file)")) + virBufferAsprintf(new_meminfo, "Inactive(file): %8llu KB\n", + meminfo.inactive_file); + else if (STREQ(line, "Unevictable")) + virBufferAsprintf(new_meminfo, "Unevictable: %8llu KB\n", + meminfo.unevictable); + else if (STREQ(line, "SwapTotal") && def->mem.swap_hard_limit) + virBufferAsprintf(new_meminfo, "SwapTotal: %8llu KB\n", + (meminfo.swaptotal - meminfo.memtotal)); + else if (STREQ(line, "SwapFree") && def->mem.swap_hard_limit) + virBufferAsprintf(new_meminfo, "SwapFree: %8llu KB\n", + (meminfo.swaptotal - meminfo.memtotal - + meminfo.swapusage + meminfo.memusage)); + else { + *ptr = ':'; + virBufferAdd(new_meminfo, line, -1); + } + + if (virBufferError(new_meminfo)) + goto cleanup; + + copied += strlen(line); + if (copied > size) + copied = size; + } + } + res = copied; + memcpy(buf, virBufferCurrentContent(new_meminfo), copied); + +cleanup: + VIR_FREE(line); + virBufferFreeAndReset(new_meminfo); + VIR_FORCE_FCLOSE(fd); + return res; } static int lxcProcRead(const char *path ATTRIBUTE_UNUSED, @@ -79,7 +228,26 @@ static int lxcProcRead(const char *path ATTRIBUTE_UNUSED, off_t offset ATTRIBUTE_UNUSED, struct fuse_file_info *fi ATTRIBUTE_UNUSED) { - return -ENOENT; + int res = -ENOENT; + char *hostpath = NULL; + struct fuse_context *context = NULL; + virDomainDefPtr def = NULL; + + if (virAsprintf(&hostpath, "/proc/%s", path) < 0) { + virReportOOMError(); + return -errno; + } + + context = fuse_get_context(); + def = (virDomainDefPtr)context->private_data; + + if (STREQ(path, fuse_meminfo_path)) { + if ((res = lxcProcReadMeminfo(hostpath, def, buf, size, offset)) < 0) + res = lxcProcHostRead(hostpath, buf, size, offset); + } + + VIR_FREE(hostpath); + return res; } static struct fuse_operations lxcProcOper = { diff --git a/src/lxc/lxc_fuse.h b/src/lxc/lxc_fuse.h index f2d2cbb..93a5373 100644 --- a/src/lxc/lxc_fuse.h +++ b/src/lxc/lxc_fuse.h @@ -35,6 +35,20 @@ #include "util.h" #include "memory.h" +struct virLXCMeminfo { + unsigned long long memtotal; + unsigned long long memusage; + unsigned long long cached; + unsigned long long active_anon; + unsigned long long inactive_anon; + unsigned long long active_file; + unsigned long long inactive_file; + unsigned long long unevictable; + unsigned long long swaptotal; + unsigned long long swapusage; +}; +typedef struct virLXCMeminfo *virLXCMeminfoPtr; + struct virLXCFuse { virDomainDefPtr def; virThread thread; -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list