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(a)cn.fujitsu.com>
---
src/lxc/lxc_cgroup.c | 160 +++++++++++++++++++++++++++++++++++
src/lxc/lxc_cgroup.h | 2 +-
src/lxc/lxc_fuse.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++---
src/lxc/lxc_fuse.h | 14 +++
4 files changed, 393 insertions(+), 13 deletions(-)
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
index ddd4901..e93c4a2 100644
--- a/src/lxc/lxc_cgroup.c
+++ b/src/lxc/lxc_cgroup.c
@@ -23,10 +23,12 @@
#include "lxc_cgroup.h"
#include "lxc_container.h"
+#include "lxc_fuse.h"
#include "virterror_internal.h"
#include "logging.h"
#include "memory.h"
#include "cgroup.h"
+#include "virfile.h"
#define VIR_FROM_THIS VIR_FROM_LXC
@@ -138,6 +140,164 @@ cleanup:
}
+static int virLXCCgroupGetMemSwapUsage(virCgroupPtr cgroup,
+ unsigned long long *usage)
+{
+ return virCgroupGetMemSwapUsage(cgroup, usage);
+}
+
+
+static int virLXCCgroupGetMemSwapTotal(virCgroupPtr cgroup,
+ unsigned long long *total)
+{
+ return virCgroupGetMemSwapHardLimit(cgroup, total);
+}
+
+
+static int virLXCCgroupGetMemUsage(virCgroupPtr cgroup,
+ unsigned long long *usage)
+{
+ int ret;
+ unsigned long memUsage;
+
+ ret = virCgroupGetMemoryUsage(cgroup, &memUsage);
+ *usage = (unsigned long long) memUsage;
+
+ return ret;
+}
+
+
+static int virLXCCgroupGetMemTotal(virCgroupPtr cgroup,
+ unsigned long long *total)
+{
+ return virCgroupGetMemoryHardLimit(cgroup, total);
+}
+
+
+static int virLXCCgroupGetMemStat(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 (STREQ(line, "cache")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[CACHED] = stat_value >> 10;
+ } else if (STREQ(line, "inactive_anon")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[INACTIVE_ANON] = stat_value >> 10;
+ } else if (STREQ(line, "active_anon")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[ACTIVE_ANON] = stat_value >> 10;
+ } else if (STREQ(line, "inactive_file")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[INACTIVE_FILE] = stat_value >> 10;
+ } else if (STREQ(line, "active_file")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[ACTIVE_FILE] = stat_value >> 10;
+ } else if (STREQ(line, "unevictable")) {
+ if ((ret = virStrToLong_ull(value + 1, NULL, 10, &stat_value)) < 0)
+ goto out;
+ meminfo[UNEVICTABLE] = stat_value >> 10;
+ }
+ }
+ ret = 0;
+out:
+ VIR_FORCE_FCLOSE(statfd);
+out_free:
+ VIR_FREE(statFile);
+ return ret;
+}
+
+
+int virLXCCgroupGetMeminfo(unsigned long long *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 out;
+ }
+
+ ret = virLXCCgroupGetMemTotal(cgroup, &meminfo[MEMTOTAL]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup total"));
+ goto out;
+ }
+
+ ret = virLXCCgroupGetMemUsage(cgroup, &meminfo[MEMUSAGE]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup stat usage"));
+ goto out;
+ }
+
+ ret = virLXCCgroupGetMemSwapTotal(cgroup, &meminfo[SWAPTOTAL]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup stat swaptotal"));
+ goto out;
+ }
+
+ ret = virLXCCgroupGetMemSwapUsage(cgroup, &meminfo[SWAPUSAGE]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup stat swapusage"));
+ goto out;
+ }
+
+ ret = 0;
+out:
+ 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 97bb12a..55553c1 100644
--- a/src/lxc/lxc_cgroup.h
+++ b/src/lxc/lxc_cgroup.h
@@ -25,5 +25,5 @@
# include "domain_conf.h"
int virLXCCgroupSetup(virDomainDefPtr def);
-
+int virLXCCgroupGetMeminfo(unsigned long long *meminfo);
#endif /* __VIR_LXC_CGROUP_H__ */
diff --git a/src/lxc/lxc_fuse.c b/src/lxc/lxc_fuse.c
index 3ffe82d..6f6e9ef 100644
--- a/src/lxc/lxc_fuse.c
+++ b/src/lxc/lxc_fuse.c
@@ -30,25 +30,48 @@
#include "virterror_internal.h"
#include "lxc_fuse.h"
+#include "lxc_cgroup.h"
+#include "virfile.h"
#define VIR_FROM_THIS VIR_FROM_LXC
#if HAVE_FUSE
+static const char *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 ((res = virAsprintf(&mempath, "/proc/%s", path)) < 0) {
+ virReportOOMError();
+ return res;
+ }
+
+ res = 0;
if (STREQ(path, "/")) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
+ } else if (STREQ(path, meminfo_path)) {
+ stat(mempath, &sb);
+ 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;
}
+ VIR_FREE(mempath);
return res;
}
@@ -57,28 +80,211 @@ static int lxcProcReaddir(const char *path, void *buf,
off_t offset ATTRIBUTE_UNUSED,
struct fuse_file_info *fi ATTRIBUTE_UNUSED)
{
- if (STREQ(path, "/"))
+ if (!STREQ(path, "/"))
return -ENOENT;
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)
{
- return -ENOENT;
+ if (!STREQ(path, meminfo_path))
+ return -ENOENT;
+
+ if ((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
}
-static int lxcProcRead(const char *path ATTRIBUTE_UNUSED,
- char *buf ATTRIBUTE_UNUSED,
- size_t size ATTRIBUTE_UNUSED,
- off_t offset ATTRIBUTE_UNUSED,
+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;
+
+ 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 = 0;
+ FILE *fd = NULL;
+ char line[1024];
+ unsigned long long meminfo[MEMMAX];
+ memset(meminfo, 0, sizeof(meminfo));
+
+ if ((res = virLXCCgroupGetMeminfo(meminfo)) < 0)
+ return res;
+
+ fd = fopen(hostpath, "r");
+ if (fd == NULL) {
+ virReportSystemError(errno, _("Cannot open %s"), hostpath);
+ res = -errno;
+ goto out;
+ }
+
+ fseek(fd, offset, SEEK_SET);
+
+ while (copied < 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 (STREQ(line, "MemTotal") &&
+ (def->mem.hard_limit || def->mem.max_balloon)) {
+ if ((res = virAsprintf(&new_line, "MemTotal: %8llu
KB\n",
+ meminfo[MEMTOTAL])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "MemFree") &&
+ (def->mem.hard_limit || def->mem.max_balloon)) {
+ if ((res = virAsprintf(&new_line, "MemFree: %8llu
KB\n",
+ (meminfo[MEMTOTAL] - meminfo[MEMUSAGE]))) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Buffers")) {
+ if ((res = virAsprintf(&new_line, "Buffers: %8d
KB\n", 0)) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Cached")) {
+ if ((res = virAsprintf(&new_line, "Cached: %8llu
KB\n",
+ meminfo[CACHED])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Active")) {
+ if ((res = virAsprintf(&new_line, "Active: %8llu
KB\n",
+ (meminfo[ACTIVE_ANON] + meminfo[ACTIVE_FILE]))) < 0)
{
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Inactive")) {
+ if ((res = virAsprintf(&new_line, "Inactive: %8llu
KB\n",
+ (meminfo[INACTIVE_ANON] + meminfo[INACTIVE_FILE]))) <
0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Active(anon)")) {
+ if ((res = virAsprintf(&new_line, "Active(anon): %8llu
KB\n",
+ meminfo[ACTIVE_ANON])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Inactive(anon)")) {
+ if ((res = virAsprintf(&new_line, "Inactive(anon): %8llu
KB\n",
+ meminfo[INACTIVE_ANON])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Active(file)")) {
+ if ((res = virAsprintf(&new_line, "Active(file): %8llu
KB\n",
+ meminfo[ACTIVE_FILE])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Inactive(file)")) {
+ if ((res = virAsprintf(&new_line, "Inactive(file): %8llu
KB\n",
+ meminfo[INACTIVE_FILE])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "Unevictable")) {
+ if ((res = virAsprintf(&new_line, "Unevictable: %8llu
KB\n",
+ meminfo[UNEVICTABLE])) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "SwapTotal") &&
def->mem.swap_hard_limit) {
+ if ((res = virAsprintf(&new_line, "SwapTotal: %8llu
KB\n",
+ (meminfo[SWAPTOTAL] - meminfo[MEMTOTAL]))) < 0){
+ virReportOOMError();
+ goto out;
+ }
+ } else if (STREQ(line, "SwapFree") &&
def->mem.swap_hard_limit) {
+ if ((res = virAsprintf(&new_line, "SwapFree: %8llu
KB\n",
+ (meminfo[SWAPTOTAL] - meminfo[MEMTOTAL] -
+ meminfo[SWAPUSAGE] + meminfo[MEMUSAGE]))) < 0) {
+ virReportOOMError();
+ goto out;
+ }
+ }
+ *ptr=':';
+ }
+
+ len = strlen(new_line);
+
+ if (copied + len > size)
+ len = size - copied;
+
+ memcpy(buf + copied, new_line, len);
+ copied += len;
+ memset(line, 0, sizeof(line));
+ if (new_line != line)
+ VIR_FREE(new_line);
+ }
+ res = copied;
+out:
+ VIR_FORCE_FCLOSE(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) {
+ virReportOOMError();
+ return res;
+ }
+
+ context = fuse_get_context();
+ def = (virDomainDefPtr)context->private_data;
+
+ if (STREQ(path, meminfo_path)) {
+ res = lxcProcReadMeminfo(hostpath, def, buf, size, offset);
+ } else {
+ res = -ENOENT;
+ goto out;
+ }
+
+ if (res < 0) {
+ if((res = lxcProcHostRead(hostpath, buf, size, offset)) < 0)
+ virReportSystemError(errno, "%s",
+ _("failed to show host's meminfo"));
+ }
+
+out:
+ VIR_FREE(hostpath);
+ return res;
}
static struct fuse_operations lxcProcOper = {
@@ -117,7 +323,7 @@ void lxcRegisterFuse(void *DomainDef)
argv[3] = (char *)"-f";
if (fuse_main(argc, argv, &lxcProcOper, def) < 0)
- virReportSystemError(errno, "%s", _("Cannot start fuse\n"));
+ virReportSystemError(errno, "%s", _("Cannot start fuse"));
cleanup:
VIR_FREE(name);
@@ -136,7 +342,7 @@ void lxcUnregisterFuse(virDomainDefPtr def)
if (umount(path) < 0)
virReportSystemError(errno, "%s",
- _("umount fuse filesystem failed\n"));
+ _("umount fuse filesystem failed"));
VIR_FREE(path);
}
diff --git a/src/lxc/lxc_fuse.h b/src/lxc/lxc_fuse.h
index d60c238..7db534b 100644
--- a/src/lxc/lxc_fuse.h
+++ b/src/lxc/lxc_fuse.h
@@ -35,6 +35,20 @@
#include "util.h"
#include "memory.h"
+enum {
+ MEMTOTAL,
+ MEMUSAGE,
+ CACHED,
+ ACTIVE_ANON,
+ INACTIVE_ANON,
+ ACTIVE_FILE,
+ INACTIVE_FILE,
+ UNEVICTABLE,
+ SWAPTOTAL,
+ SWAPUSAGE,
+ MEMMAX,
+};
+
extern void lxcRegisterFuse(void *DomainDef);
extern void lxcUnregisterFuse(virDomainDefPtr def);
#endif
--
1.7.7.6