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_fuse.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 332 insertions(+), 8 deletions(-) diff --git a/src/lxc/lxc_fuse.c b/src/lxc/lxc_fuse.c index 5fdacab..69b4f2e 100644 --- a/src/lxc/lxc_fuse.c +++ b/src/lxc/lxc_fuse.c @@ -32,22 +32,52 @@ #include "lxc_fuse.h" #include "util.h" #include "memory.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_LXC #if HAVE_FUSE +enum { + MEMTOTAL, + MEMUSAGE, + CACHED, + ACTIVE_ANON, + INACTIVE_ANON, + ACTIVE_FILE, + INACTIVE_FILE, + UNEVICTABLE, + SWAPTOTAL, + SWAPUSAGE, + MEMMAX, +}; + +static const char *meminfo_path = "/meminfo"; static int lxcProcGetattr(const char *path, struct stat *stbuf) { int res = 0; + char *mempath = NULL; + struct stat sb; + virAsprintf(&mempath, "/proc/%s",path); memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; + } else if (strcmp(path, meminfo_path) == 0) { + stat(mempath, &sb); + stbuf->st_mode = S_IFREG | 0444; + 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; + VIR_FREE(mempath); return res; } @@ -61,23 +91,317 @@ static int lxcProcReaddir(const char *path, void *buf, filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); + filler(buf, meminfo_path + 1, NULL, 0); return 0; } -static int lxcProcOpen(const char *path ATTRIBUTE_UNUSED, - struct fuse_file_info *fi ATTRIBUTE_UNUSED) +static int lxcProcOpen(const char *path, + struct fuse_file_info *fi) +{ + if (strcmp(path, meminfo_path) != 0) + return -ENOENT; + + if ((fi->flags & 3) != O_RDONLY) + return -EACCES; + + return 0; +} + +static int lxcGetMemCgroupSwapUsage(virCgroupPtr cgroup, unsigned long long *usage) +{ + return virCgroupGetMemSwapUsage(cgroup, usage); +} + +static int lxcGetMemCgroupSwapTotal(virCgroupPtr cgroup, unsigned long long *total) +{ + return virCgroupGetMemSwapHardLimit(cgroup, total); +} + +static int lxcGetMemCgroupMemUsage(virCgroupPtr cgroup, unsigned long long *usage) +{ + int ret; + unsigned long memUsage; + + ret = virCgroupGetMemoryUsage(cgroup, &memUsage); + *usage = (unsigned long long) memUsage; + + return ret; +} + +static int lxcGetMemCgroupMemTotal(virCgroupPtr cgroup, unsigned long long *total) { - return -ENOENT; + return virCgroupGetMemoryHardLimit(cgroup, total); } -static int lxcProcRead(const char *path ATTRIBUTE_UNUSED, - char *buf ATTRIBUTE_UNUSED, - size_t size ATTRIBUTE_UNUSED, - off_t offset ATTRIBUTE_UNUSED, +static int lxcGetMemCgroupStat(virCgroupPtr cgroup, unsigned long long *meminfo) +{ + int ret = 0; + FILE *statfd = NULL; + char *statFile = NULL; + char line[1024]; + + 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 = -ENOENT; + goto out_free; + } + + while (fgets(line, sizeof(line), statfd) != NULL) { + 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 (strcmp(line, "cache") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[CACHED] = stat_value >> 10; + } else if (strcmp(line, "inactive_anon") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[INACTIVE_ANON] = stat_value >> 10; + } else if (strcmp(line, "active_anon") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[ACTIVE_ANON] = stat_value >> 10; + } else if (strcmp(line, "inactive_file") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[INACTIVE_FILE] = stat_value >> 10; + } else if (strcmp(line, "active_file") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[ACTIVE_FILE] = stat_value >> 10; + } else if (strcmp(line, "unevictable") == 0) { + if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0) + goto out; + meminfo[UNEVICTABLE] = stat_value >> 10; + } + } + ret = 0; +out: + pclose(statfd); +out_free: + VIR_FREE(statFile); + return ret; +} + +static int lxcGetMeminfo(virDomainDefPtr def, unsigned long long *meminfo) +{ + int ret; + virCgroupPtr driver; + virCgroupPtr cgroup; + + ret = virCgroupForDriver("lxc", &driver, 1, 0); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get cgroup for driver")); + return ret; + } + + ret = virCgroupForDomain(driver, def->name, &cgroup, 0); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get cgroup for domain")); + goto out_driver; + } + + ret = lxcGetMemCgroupStat(cgroup, meminfo); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat info\n")); + goto out; + } + + ret = lxcGetMemCgroupMemTotal(cgroup, &meminfo[MEMTOTAL]); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup total\n")); + goto out; + } + + ret = lxcGetMemCgroupMemUsage(cgroup, &meminfo[MEMUSAGE]); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat usage\n")); + goto out; + } + + ret = lxcGetMemCgroupSwapTotal(cgroup, &meminfo[SWAPTOTAL]); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat swaptotal\n")); + goto out; + } + + ret = lxcGetMemCgroupSwapUsage(cgroup, &meminfo[SWAPUSAGE]); + if (ret < 0) { + virReportSystemError(-ret, "%s", + _("Unable to get memory cgroup stat swapusage\n")); + goto out; + } + + ret = 0; +out: + virCgroupFree(&cgroup); + +out_driver: + virCgroupFree(&driver); + + return ret; +} + +static int lxcProcReadMeminfo(char *hostpath, virDomainDefPtr def, + char *buf, size_t size, off_t offset) +{ + int res; + FILE *fd = NULL; + char line[1024]; + unsigned long long meminfo[MEMMAX]; + + if ((res = lxcGetMeminfo(def, meminfo)) < 0) { + virReportSystemError(errno, "%s", _("Get Memory Cgroup info failed\n")); + return res; + } + + fd = fopen(hostpath, "r"); + if (fd == NULL) { + virReportSystemError(errno, "Cannot fopen %s\n", hostpath); + res = -errno; + goto out; + } + + fseek(fd, offset, SEEK_SET); + + while (res < size && fgets(line, sizeof(line), fd) != NULL) { + int len = 0; + char *new_line = NULL; + char *ptr = strchr(line, ':'); + if (ptr) { + *ptr = '\0'; + new_line = line; + + if (strcmp(line, "MemTotal") == 0 && + (def->mem.hard_limit || def->mem.max_balloon)) { + virAsprintf(&new_line, "MemTotal: %8llu KB\n", + meminfo[MEMTOTAL]); + } else if (strcmp(line, "MemFree") == 0 && + (def->mem.hard_limit || def->mem.max_balloon)) { + virAsprintf(&new_line, "MemFree: %8llu KB\n", + (meminfo[MEMTOTAL] - meminfo[MEMUSAGE])); + } else if (strcmp(line, "Buffers") == 0) { + virAsprintf(&new_line, "Buffers: %8d KB\n", 0); + } else if (strcmp(line, "Cached") == 0 && meminfo[CACHED]) { + virAsprintf(&new_line, "Cached: %8llu KB\n", + meminfo[CACHED]); + } else if (strcmp(line, "Active") == 0 && meminfo[ACTIVE_ANON]) { + virAsprintf(&new_line, "Active: %8llu KB\n", + (meminfo[ACTIVE_ANON] + meminfo[ACTIVE_FILE])); + } else if (strcmp(line, "Inactive") == 0 && meminfo[INACTIVE_ANON]) { + virAsprintf(&new_line, "Inactive: %8llu KB\n", + (meminfo[INACTIVE_ANON] + meminfo[INACTIVE_FILE])); + } else if (strcmp(line, "Active(anon)") == 0 && meminfo[ACTIVE_ANON]) { + virAsprintf(&new_line, "Active(anon): %8llu KB\n", + meminfo[ACTIVE_ANON]); + } else if (strcmp(line, "Inactive(anon)") == 0 && meminfo[INACTIVE_ANON]) { + virAsprintf(&new_line, "Inactive(anon): %8llu KB\n", + meminfo[INACTIVE_ANON]); + } else if (strcmp(line, "Active(file)") == 0 && meminfo[ACTIVE_FILE]) { + virAsprintf(&new_line, "Active(file): %8llu KB\n", + meminfo[ACTIVE_FILE]); + } else if (strcmp(line, "Inactive(file)") == 0 && meminfo[INACTIVE_FILE]) { + virAsprintf(&new_line, "Inactive(file): %8llu KB\n", + meminfo[INACTIVE_FILE]); + } else if (strcmp(line, "Unevictable") == 0 && meminfo[UNEVICTABLE]) { + virAsprintf(&new_line, "Unevictable: %8llu KB\n", + meminfo[UNEVICTABLE]); + } else if (strcmp(line, "SwapTotal") == 0 && def->mem.swap_hard_limit) { + virAsprintf(&new_line, "SwapTotal: %8llu KB\n", + (meminfo[SWAPTOTAL] - meminfo[MEMTOTAL])); + } else if (strcmp(line, "SwapFree") == 0 && def->mem.swap_hard_limit) { + virAsprintf(&new_line, "SwapFree: %8llu KB\n", + (meminfo[SWAPTOTAL] - meminfo[MEMTOTAL] + - meminfo[SWAPUSAGE] + meminfo[MEMUSAGE])); + } + *ptr=':'; + } + + len = strlen(new_line); + + if (res + len > size) + len = size - res; + + memcpy(buf + res, new_line, len); + res += len; + memset(line, 0, sizeof(line)); + if (new_line != line) + VIR_FREE(new_line); + } +out: + VIR_FORCE_FCLOSE(fd); + return res; +} + +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; + + res = pread(fd, buf, size, offset); + if (res == -1) + res = -errno; + close(fd); + return res; +} + +static int lxcProcRead(const char *path, + char *buf, + size_t size, + off_t offset, struct fuse_file_info *fi ATTRIBUTE_UNUSED) { - return -ENOENT; + int res = 0; + char *hostpath = NULL; + struct fuse_context *context = NULL; + virDomainDefPtr def = NULL; + + if ((res = virAsprintf(&hostpath, "/proc/%s", path)) < 0) { + virReportSystemError(errno, "%s", _("Cannot get host path\n")); + return res; + } + + context = fuse_get_context(); + def = (virDomainDefPtr)context->private_data; + + if (strcmp(path, meminfo_path) == 0) { + res = lxcProcReadMeminfo(hostpath, def, buf, size, offset); + } else + return -ENOENT; + + if (res < 0) { + if((res = lxcProcHostRead(hostpath, buf, size, offset)) < 0) + virReportSystemError(errno, "%s", _("failed to show host's meminfo\n")); + } + VIR_FREE(hostpath); + return res; } static struct fuse_operations lxcProcOper = { -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list