On Thu, Oct 25, 2012 at 06:38:26PM +0800, Gao feng wrote:
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 | 139 +++++++++++++++++++++++++++++++++++
src/lxc/lxc_cgroup.h | 2 +-
src/lxc/lxc_fuse.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++-
src/lxc/lxc_fuse.h | 14 ++++
4 files changed, 349 insertions(+), 4 deletions(-)
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
index 9a5ba1a..543a7fe 100644
--- a/src/lxc/lxc_cgroup.c
+++ b/src/lxc/lxc_cgroup.c
@@ -23,6 +23,8 @@
#include "lxc_cgroup.h"
#include "lxc_container.h"
+#include "lxc_fuse.h"
+#include "virfile.h"
#include "virterror_internal.h"
#include "logging.h"
#include "memory.h"
@@ -137,6 +139,143 @@ cleanup:
return ret;
}
+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 = -errno;
+ 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 (virStrToLong_ull(value + 1, NULL, 10, &stat_value) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (STREQ(line, "cache"))
+ meminfo[VIR_LXC_FUSE_CACHED] = stat_value >> 10;
+ else if (STREQ(line, "inactive_anon"))
+ meminfo[VIR_LXC_FUSE_INACTIVE_ANON] = stat_value >> 10;
+ else if (STREQ(line, "active_anon"))
+ meminfo[VIR_LXC_FUSE_ACTIVE_ANON] = stat_value >> 10;
+ else if (STREQ(line, "inactive_file"))
+ meminfo[VIR_LXC_FUSE_INACTIVE_FILE] = stat_value >> 10;
+ else if (STREQ(line, "active_file"))
+ meminfo[VIR_LXC_FUSE_ACTIVE_FILE] = stat_value >> 10;
+ else if (STREQ(line, "unevictable"))
+ meminfo[VIR_LXC_FUSE_UNEVICTABLE] = stat_value >> 10;
+ }
+ ret = 0;
+out:
+ VIR_FORCE_FCLOSE(statfd);
+out_free:
+ VIR_FREE(statFile);
There is no need for 2 separate labels here. Both of these
cleanup calls are safe to invoke unconditionally, assuming
the variables are initialized at the start of the method.
Also normal naming is to use 'cleanup' not 'out'.
+ 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[VIR_LXC_FUSE_MEMTOTAL]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup total"));
+ goto out;
+ }
+
+ ret = virLXCCgroupGetMemUsage(cgroup, &meminfo[VIR_LXC_FUSE_MEMUSAGE]);
+ if (ret < 0) {
+ virReportSystemError(-ret, "%s",
+ _("Unable to get memory cgroup stat usage"));
+ goto out;
+ }
+
+ virLXCCgroupGetMemSwapTotal(cgroup, &meminfo[VIR_LXC_FUSE_SWAPTOTAL]);
+ virLXCCgroupGetMemSwapUsage(cgroup, &meminfo[VIR_LXC_FUSE_SWAPUSAGE]);
+
+ ret = 0;
+out:
Again use 'cleanup' as the name instead of 'out'.
+ virCgroupFree(&cgroup);
+
+ return ret;
+}
typedef struct _virLXCCgroupDevicePolicy virLXCCgroupDevicePolicy;
typedef virLXCCgroupDevicePolicy *virLXCCgroupDevicePolicyPtr;
+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 if (STREQ(path, fuse_meminfo_path)) {
+ stat(mempath, &sb);
Error checking....
+ 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;
}
@@ -70,7 +94,153 @@ static int lxcProcReaddir(const char *path, void
*buf,
static int lxcProcOpen(const char *path ATTRIBUTE_UNUSED,
struct fuse_file_info *fi ATTRIBUTE_UNUSED)
Remove the ATTRIBUTE_UNUSED annotations when you add the code below.
{
- return -ENOENT;
+ if (!STREQ(path, fuse_meminfo_path))
+ return -ENOENT;
+
+ if ((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+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[1024];
+ unsigned long long meminfo[VIR_LXC_FUSE_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 (virAsprintf(&new_line, "MemTotal: %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_MEMTOTAL]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "MemFree") &&
+ (def->mem.hard_limit || def->mem.max_balloon)) {
+ if (virAsprintf(&new_line, "MemFree: %8llu KB\n",
+ (meminfo[VIR_LXC_FUSE_MEMTOTAL] -
meminfo[VIR_LXC_FUSE_MEMUSAGE])) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Buffers")) {
+ if (virAsprintf(&new_line, "Buffers: %8d KB\n", 0)
< 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Cached")) {
+ if (virAsprintf(&new_line, "Cached: %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_CACHED]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Active")) {
+ if (virAsprintf(&new_line, "Active: %8llu KB\n",
+ (meminfo[VIR_LXC_FUSE_ACTIVE_ANON] +
meminfo[VIR_LXC_FUSE_ACTIVE_FILE])) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Inactive")) {
+ if (virAsprintf(&new_line, "Inactive: %8llu KB\n",
+ (meminfo[VIR_LXC_FUSE_INACTIVE_ANON] +
meminfo[VIR_LXC_FUSE_INACTIVE_FILE])) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Active(anon)")) {
+ if (virAsprintf(&new_line, "Active(anon): %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_ACTIVE_ANON]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Inactive(anon)")) {
+ if (virAsprintf(&new_line, "Inactive(anon): %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_INACTIVE_ANON]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Active(file)")) {
+ if (virAsprintf(&new_line, "Active(file): %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_ACTIVE_FILE]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Inactive(file)")) {
+ if (virAsprintf(&new_line, "Inactive(file): %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_INACTIVE_FILE]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "Unevictable")) {
+ if (virAsprintf(&new_line, "Unevictable: %8llu KB\n",
+ meminfo[VIR_LXC_FUSE_UNEVICTABLE]) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "SwapTotal") &&
def->mem.swap_hard_limit) {
+ if (virAsprintf(&new_line, "SwapTotal: %8llu KB\n",
+ (meminfo[VIR_LXC_FUSE_SWAPTOTAL] -
meminfo[VIR_LXC_FUSE_MEMTOTAL])) < 0) {
+ goto out_oom;
+ }
+ } else if (STREQ(line, "SwapFree") &&
def->mem.swap_hard_limit) {
+ if (virAsprintf(&new_line, "SwapFree: %8llu KB\n",
+ (meminfo[VIR_LXC_FUSE_SWAPTOTAL] -
meminfo[VIR_LXC_FUSE_MEMTOTAL] -
+ meminfo[VIR_LXC_FUSE_SWAPUSAGE] +
meminfo[VIR_LXC_FUSE_MEMUSAGE])) < 0) {
+ goto out_oom;
+ }
No need for '{}' around any of the above if() statements since they all have
a single line body.
+ }
+ *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:
s/out/cleanup/
+ VIR_FORCE_FCLOSE(fd);
+ return res;
+
+out_oom:
s/out_oom/no_memory/
+ virReportOOMError();
+ res = -errno;
+ goto out;
}
static int lxcProcRead(const char *path ATTRIBUTE_UNUSED,
@@ -79,7 +249,29 @@ 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)) < 0))
+ goto out;
+ }
+
+out:
s/out/cleanup/
+ VIR_FREE(hostpath);
+ return res;
+
}
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|