Skip to contentSkip to conversation

balloon: selfballooning and post memory info via xenbus

PATCH In review Dan Magenheimer 1 participant 2 messages May 5, 2008 – May 8, 2008 Xen hypervisor interface

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 balloon: selfballooning and post memory info via xenbus

OK, here's the promised patch. The overall objective of the patch is to enable limited memory load-balancing capabilities as a step toward allowing limited memory overcommit. With this and some other minor hackery, I was able to run as many as 15 lightly loaded 512MB domains on a 2GB system (yes, veerrrryyy slooowwwlly).

Review/comments appreciated.

With this patch, balloon.c communicates (limited) useful memory usage information via xenbus. It also implements "selfballooning" which applies the memory information locally to immediately adjust the balloon, giving up memory when it is not needed and asking for it back when it is needed, implementing a first-come-first-served system-wide ballooning "policy". When a domain asks for memory but none is available, it must use its own configured swap disk, resulting in (potentially severe) performance degradation. Naturally, it is not recommended to turn on selfballooning in a domain that has no swap disk or if performance is more important than increasing the number of VMs runnable on a physical machine.

A key assumption is that the Linux variable vm_committed_space is a reasonable first approximation of memory needed by a domain. This approximation will probably improve over time, but is a good start for now. The variable is bound on the lower end by the recently submitted minimum_target() algorithm patch; thus O-O-M conditions should not occur.

The code is a bit complicated in a couple of places because of race conditions involving xenstored startup relative to turning on selfballooning locally. Because the key variable (vm_committed_space) is not exported by Linux, I implemented a horrible hack which still allows the code to work in a module, however I fully expect that this part of the patch will not be accepted (which will limit the functionality to pvm domains only... probably OK for now).

Existing balloon functionality which is unchanged:
- Set target for VM from domain0
- Set target inside VM by writing to /proc/xen/balloon Existing balloon info on xenbus which is unchanged:
- /local/domain/X/memory/target
To turn on selfballooning:
- Inside a VM: "echo 1 > /proc/xen/balloon"
- From domain0: "xenstore-write /local/domain/X/memory/selfballoon 1" To turn off selfballooning:
- Inside a VM: "echo 0 > /proc/xen/balloon"
- From domain0: "xenstore-write /local/domain/X/memory/selfballoon 0" New balloon info now on xenbus:
- /local/domain/X/memory/selfballoon [0 or 1]
- /local/domain/X/memory/actual [kB] *
- /local/domain/X/memory/minimum [kB] *
- /local/domain/X/memory/selftarget [kB] * (only valid if selfballoon==1)
* writeable only by balloon driver in X when either selfballooning is first enabled, or target is changed by domain0

Thanks,
Dan

===================================
Thanks... for the memory
I really could use more / My throughput's on the floor The balloon is flat / My swap disk's fat / I've OOM's in store Overcommitted so much
(with apologies to the late great Bob Hope)

drivers/xen/balloon/balloon.c +153 -8
diff -r a947207da4e4 drivers/xen/balloon/balloon.c
@@ -6,6 +6,7 @@
  * Copyright (c) 2003, B Dragovic
  * Copyright (c) 2003-2004, M Williamson, K Fraser
  * Copyright (c) 2005 Dan M. Smith, IBM Corporation
+ * Copyright (c) 2008 Dan Magenheimer, Oracle Corp (added self-ballooning)
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License version 2
@@ -78,19 +79,32 @@ DEFINE_SPINLOCK(balloon_lock);
 
 struct balloon_stats balloon_stats;
 
+/*
+ * use vm_committed_space as an approximation for balloon target, repeating
+ * every second.  Disabled if compiled as MODULE because kernel doesn't
+ * currently export vm_committed_space
+ */
+static int selfballoon = 0;
+
 /* We increase/decrease in batches which fit in a page */
 static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)];
 
 /* VM /proc information for memory */
 extern unsigned long totalram_pages;
 
+/* avoid possible write_proc race with xenstored startup */
+static int xenstored_ready = 0;
+
 #ifndef MODULE
 extern unsigned long totalhigh_pages;
+extern atomic_t vm_committed_space;
 #define inc_totalhigh_pages() (totalhigh_pages++)
 #define dec_totalhigh_pages() (totalhigh_pages--)
 #else
 #define inc_totalhigh_pages() ((void)0)
 #define dec_totalhigh_pages() ((void)0)
+#undef atomic_read
+#define atomic_read(x) (0)
 #endif
 
 /* List of ballooned pages, threaded through the mem_map array. */
@@ -119,6 +133,58 @@ static struct timer_list balloon_timer;
 	printk(KERN_INFO "xen_mem: " fmt, ##args)
 #define WPRINTK(fmt, args...) \
 	printk(KERN_WARNING "xen_mem: " fmt, ##args)
+
+#ifndef MODULE
+static inline long get_vm_committed(void)
+{
+	return atomic_read(&vm_committed_space);
+}
+#else
+/* vm_committed_space is not exported, so use horrible hack to get same */
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/fsnotify.h>
+#include <linux/file.h>
+static long get_vm_committed(void)
+{
+	ssize_t len;
+	int fd;
+	char buf[1000], *p, *q;
+	struct file *f;
+	static openfailed = 0;
+
+	/* the following is messy because sys_open isn't exported */
+	if (!(f = filp_open("/proc/meminfo", O_RDONLY, 0)) ||
+	    ((fd = get_unused_fd()) < 0)) {
+		/* first open attempt often fails, be resilient */
+		if (!open_failed) {
+			WPRINTK("Unable to open /proc/meminfo, retrying...\n");
+			open_failed = 1;
+		}
+		return 0;
+	}
+	fsnotify_open(f->f_dentry);
+	fd_install(fd,f);
+	/* end of messiness */
+	if ((len = sys_read(fd, buf, 999)) < 0) {
+		WPRINTK("Unable to read from /proc/meminfo\n");
+		return 0;
+	}
+	sys_close(fd);
+	buf[len] = '\0';
+	p = strstr(buf,"Committed_AS");
+	if (!p) {
+		WPRINTK("No Committed_AS in /proc/meminfo\n");
+		return 0;
+	}
+	while (*p < '0' || *p > '9') p++;
+	q = p;
+	while (*q >= '0' && *q <= '9') q++;
+	*q = '\0';
+	return (memparse(p, &q) >> (PAGE_SHIFT - 10));
+}
+#endif
 
 /* balloon_append: add the given page to the balloon. */
 static void balloon_append(struct page *page)
@@ -381,6 +447,16 @@ static int decrease_reservation(unsigned
 	return need_sleep;
 }
 
+/* Resets the Xen limit and sets new target. */
+void balloon_set_new_target(unsigned long target)
+{
+	if (!target) return; /* compiled as module, selfballoon on */
+	/* No need for lock. Not read-modify-write updates. */
+	bs.hard_limit   = ~0UL;
+	bs.target_pages = max(target, minimum_target());
+}
+
+
 /*
  * We avoid multiple worker processes conflicting via the balloon mutex.
  * We may of course race updates of the target counts (which are protected
@@ -391,9 +467,26 @@ static void balloon_process(void *unused
 {
 	int need_sleep = 0;
 	long credit;
+	unsigned long target_pages;
 
 	mutex_lock(&balloon_mutex);
 
+	if (xenstored_ready) {
+		(void)xenbus_printf(XBT_NIL, "memory", "actual", "%lu",
+			totalram_pages << (PAGE_SHIFT - 10));
+		(void)xenbus_printf(XBT_NIL, "memory", "minimum", "%lu",
+			minimum_target() << (PAGE_SHIFT - 10));
+	}
+
+	if (selfballoon) {
+		target_pages = get_vm_committed();
+		balloon_set_new_target(target_pages);
+		if (xenstored_ready)
+			(void)xenbus_printf(XBT_NIL, "memory", "selftarget",
+				"%lu", target_pages << (PAGE_SHIFT - 10));
+//		IPRINTK("setting new self-ballooning target=%ld kB\n",
+//			target_pages << (PAGE_SHIFT - 10));
+	}
 	do {
 		credit = current_target() - bs.current_pages;
 		if (credit > 0)
@@ -408,19 +501,40 @@ static void balloon_process(void *unused
 	} while ((credit != 0) && !need_sleep);
 
 	/* Schedule more work if there is some still to be done. */
-	if (current_target() != bs.current_pages)
+	if ((current_target() != bs.current_pages) || selfballoon)
 		mod_timer(&balloon_timer, jiffies + HZ);
 
 	mutex_unlock(&balloon_mutex);
 }
 
 /* Resets the Xen limit, sets new target, and kicks off processing. */
-void balloon_set_new_target(unsigned long target)
+void balloon_start_new_target(unsigned long target)
 {
-	/* No need for lock. Not read-modify-write updates. */
-	bs.hard_limit   = ~0UL;
-	bs.target_pages = max(target, minimum_target());
+	balloon_set_new_target(target);
 	schedule_work(&balloon_worker);
+}
+
+static struct xenbus_watch selfballoon_watch =
+{
+	.node = "memory/selfballoon"
+};
+
+/* React to a change in the selfballoon key */
+static void watch_selfballoon(struct xenbus_watch *watch,
+			 const char **vec, unsigned int len)
+{
+	unsigned long enable, vm_pages;
+	int err;
+
+	err = xenbus_scanf(XBT_NIL, "memory", "selfballoon", "%lu", &enable);
+	if (err != 1) {
+		/* This is ok (for domain0 at least) - so just return */
+		return;
+	}
+	selfballoon = enable;
+	if (!enable) return;
+	vm_pages = get_vm_committed();
+	balloon_start_new_target(vm_pages);
 }
 
 static struct xenbus_watch target_watch =
@@ -444,18 +558,34 @@ static void watch_target(struct xenbus_w
 	/* The given memory/target value is in KiB, so it needs converting to
 	 * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10.
 	 */
-	balloon_set_new_target(new_target >> (PAGE_SHIFT - 10));
+	balloon_start_new_target(new_target >> (PAGE_SHIFT - 10));
 }
 
 static int balloon_init_watcher(struct notifier_block *notifier,
 				unsigned long event,
 				void *data)
 {
+	unsigned long enable, vm_pages;
 	int err;
 
+	err = xenbus_scanf(XBT_NIL, "memory", "selfballoon", "%lu", &enable);
+	if (err == 1) {
+		selfballoon = enable;
+		if (enable) {
+			vm_pages = get_vm_committed();
+			balloon_start_new_target(vm_pages);
+		}
+	}
+	else
+		(void)xenbus_printf(XBT_NIL, "memory", "selfballoon", "%lu",
+			selfballoon ? 1UL : 0UL);
+
 	err = register_xenbus_watch(&target_watch);
+	err |= register_xenbus_watch(&selfballoon_watch);
 	if (err)
 		printk(KERN_ERR "Failed to set balloon watcher\n");
+	else xenstored_ready = 1;
+
 
 	return NOTIFY_DONE;
 }
@@ -466,11 +596,12 @@ static int balloon_write(struct file *fi
 {
 	char memstring[64], *endchar;
 	unsigned long long target_bytes;
+	unsigned long vm_pages;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (count <= 1)
+	if (!count)
 		return -EBADMSG; /* runt */
 	if (count > sizeof(memstring))
 		return -EFBIG;   /* too long */
@@ -480,7 +611,18 @@ static int balloon_write(struct file *fi
 	memstring[sizeof(memstring)-1] = '\0';
 
 	target_bytes = memparse(memstring, &endchar);
-	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
+	if (target_bytes <= 1) {
+		selfballoon = target_bytes;
+		if (xenstored_ready) { /* let watch_selfballoon handle */
+			(void)xenbus_printf(XBT_NIL, "memory",
+				"selfballoon", "%lu", selfballoon ? 1UL : 0UL);
+		} else if (selfballoon) {
+			vm_pages = get_vm_committed();
+			balloon_start_new_target(vm_pages);
+		}
+	}
+	else
+		balloon_start_new_target(target_bytes >> PAGE_SHIFT);
 
 	return count;
 }
@@ -492,12 +634,14 @@ static int balloon_read(char *page, char
 
 	len = sprintf(
 		page,
+		"Self-ballooning:    %s\n"
 		"Current allocation: %8lu kB\n"
 		"Requested target:   %8lu kB\n"
 		"Low-mem balloon:    %8lu kB\n"
 		"High-mem balloon:   %8lu kB\n"
 		"Driver pages:       %8lu kB\n"
 		"Xen hard limit:     ",
+		(selfballoon?"on":"off"),
 		PAGES2KB(bs.current_pages), PAGES2KB(bs.target_pages), 
 		PAGES2KB(bs.balloon_low), PAGES2KB(bs.balloon_high),
 		PAGES2KB(bs.driver_pages));
@@ -564,6 +708,7 @@ static int __init balloon_init(void)
 #endif
 
 	target_watch.callback = watch_target;
+	selfballoon_watch.callback = watch_selfballoon;
 	xenstore_notifier.notifier_call = balloon_init_watcher;
 
 	register_xenstore_notifier(&xenstore_notifier);

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel

#1 lore raw

Awfully quiet out there... comments please?

Here's a second version. This one adds hysteresis, both "up" and "down".

This smooths changes in balloon size to better accomodate spiky or sine-wave-ish memory loads.

It can also be used as a rudimentary QoS device to modify the "eagerness" of some domains in attempting to increase or decrease memory.

By setting downhysteresis (when selfballooning) to a value of n between 2 and 99, memory will be reduced by (current - selftarget)/n every second whenever current > selftarget. By setting uphysteresis (when selfballooning) to a value of n between 2 and 99, memory will be increased by (current - selftarget)/n every second whenever current < selftarget. Both default off. It is expected that downhysterisis will often be a largish value and uphysteresis will often be a smallish value, so as to respond rapidly to increases in memory usage and more gradually to decreases in memory usage, which is as desired.

Dan

Quoted 72 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]]On Behalf Of Dan Magenheimer
Sent: Monday, May 05, 2008 4:45 PM
To: Xen-Devel (E-mail)
Subject: [Xen-devel] [PATCH] balloon: selfballooning and post memory info via xenbus

OK, here's the promised patch. The overall objective of the patch is to enable limited memory load-balancing capabilities as a step toward allowing limited memory overcommit. With this and some other minor hackery, I was able to run as many as 15 lightly loaded 512MB domains on a 2GB system (yes, veerrrryyy slooowwwlly).

Review/comments appreciated.

With this patch, balloon.c communicates (limited) useful memory usage information via xenbus. It also implements "selfballooning" which applies the memory information locally to immediately adjust the balloon, giving up memory when it is not needed and asking for it back when it is needed, implementing a first-come-first-served system-wide ballooning "policy". When a domain asks for memory but none is available, it must use its own configured swap disk, resulting in (potentially severe) performance degradation. Naturally, it is not recommended to turn on selfballooning in a domain that has no swap disk or if performance is more important than increasing the number of VMs runnable on a physical machine.

A key assumption is that the Linux variable vm_committed_space is a reasonable first approximation of memory needed by a domain. This approximation will probably improve over time, but is a good start for now. The variable is bound on the lower end by the recently submitted minimum_target() algorithm patch; thus O-O-M conditions should not occur.

The code is a bit complicated in a couple of places because of race conditions involving xenstored startup relative to turning on selfballooning locally. Because the key variable (vm_committed_space) is not exported by Linux, I implemented a horrible hack which still allows the code to work in a module, however I fully expect that this part of the patch will not be accepted (which will limit the functionality to pvm domains only... probably OK for now).

Existing balloon functionality which is unchanged:
- Set target for VM from domain0
- Set target inside VM by writing to /proc/xen/balloon Existing balloon info on xenbus which is unchanged:
- /local/domain/X/memory/target
To turn on selfballooning:
- Inside a VM: "echo 1 > /proc/xen/balloon"
- From domain0: "xenstore-write /local/domain/X/memory/selfballoon 1" To turn off selfballooning:
- Inside a VM: "echo 0 > /proc/xen/balloon"
- From domain0: "xenstore-write /local/domain/X/memory/selfballoon 0" New balloon info now on xenbus:
- /local/domain/X/memory/selfballoon [0 or 1]
- /local/domain/X/memory/actual [kB] *
- /local/domain/X/memory/minimum [kB] *
- /local/domain/X/memory/selftarget [kB] * (only valid if selfballoon==1)
* writeable only by balloon driver in X when either selfballooning is first enabled, or target is changed by domain0

Thanks,
Dan

Quoted 7 lines

===================================
Thanks... for the memory
I really could use more / My throughput's on the floor The balloon is flat / My swap disk's fat / I've OOM's in store Overcommitted so much
(with apologies to the late great Bob Hope)

drivers/xen/balloon/balloon.c +236 -10
diff -r e39cf97647af drivers/xen/balloon/balloon.c
@@ -6,6 +6,7 @@
  * Copyright (c) 2003, B Dragovic
  * Copyright (c) 2003-2004, M Williamson, K Fraser
  * Copyright (c) 2005 Dan M. Smith, IBM Corporation
+ * Copyright (c) 2008 Dan Magenheimer, Oracle Corp (added self-ballooning)
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License version 2
@@ -78,19 +79,36 @@ DEFINE_SPINLOCK(balloon_lock);
 
 struct balloon_stats balloon_stats;
 
+/*
+ * use vm_committed_space as an approximation for balloon target, repeating
+ * every second.  Disabled if compiled as MODULE because kernel doesn't
+ * currently export vm_committed_space
+ */
+static int selfballoon = 0;
+
 /* We increase/decrease in batches which fit in a page */
 static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)];
 
 /* VM /proc information for memory */
 extern unsigned long totalram_pages;
 
+/* hysteresis to control rate of increase/decrease */
+unsigned int down_hyster = 0;
+unsigned int up_hyster = 0;
+
+/* avoid possible write_proc race with xenstored startup */
+static int xenstored_ready = 0;
+
 #ifndef MODULE
 extern unsigned long totalhigh_pages;
+extern atomic_t vm_committed_space;
 #define inc_totalhigh_pages() (totalhigh_pages++)
 #define dec_totalhigh_pages() (totalhigh_pages--)
 #else
 #define inc_totalhigh_pages() ((void)0)
 #define dec_totalhigh_pages() ((void)0)
+#undef atomic_read
+#define atomic_read(x) (0)
 #endif
 
 /* List of ballooned pages, threaded through the mem_map array. */
@@ -119,6 +137,58 @@ static struct timer_list balloon_timer;
 	printk(KERN_INFO "xen_mem: " fmt, ##args)
 #define WPRINTK(fmt, args...) \
 	printk(KERN_WARNING "xen_mem: " fmt, ##args)
+
+#ifndef MODULE
+static inline long get_vm_committed(void)
+{
+	return atomic_read(&vm_committed_space);
+}
+#else
+/* vm_committed_space is not exported, so use horrible hack to get same */
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/fsnotify.h>
+#include <linux/file.h>
+static long get_vm_committed(void)
+{
+	ssize_t len;
+	int fd;
+	char buf[1000], *p, *q;
+	struct file *f;
+	static openfailed = 0;
+
+	/* the following is messy because sys_open isn't exported */
+	if (!(f = filp_open("/proc/meminfo", O_RDONLY, 0)) ||
+	    ((fd = get_unused_fd()) < 0)) {
+		/* first open attempt often fails, be resilient */
+		if (!open_failed) {
+			WPRINTK("Unable to open /proc/meminfo, retrying...\n");
+			open_failed = 1;
+		}
+		return 0;
+	}
+	fsnotify_open(f->f_dentry);
+	fd_install(fd,f);
+	/* end of messiness */
+	if ((len = sys_read(fd, buf, 999)) < 0) {
+		WPRINTK("Unable to read from /proc/meminfo\n");
+		return 0;
+	}
+	sys_close(fd);
+	buf[len] = '\0';
+	p = strstr(buf,"Committed_AS");
+	if (!p) {
+		WPRINTK("No Committed_AS in /proc/meminfo\n");
+		return 0;
+	}
+	while (*p < '0' || *p > '9') p++;
+	q = p;
+	while (*q >= '0' && *q <= '9') q++;
+	*q = '\0';
+	return (memparse(p, &q) >> (PAGE_SHIFT - 10));
+}
+#endif
 
 /* balloon_append: add the given page to the balloon. */
 static void balloon_append(struct page *page)
@@ -381,6 +451,23 @@ static int decrease_reservation(unsigned
 	return need_sleep;
 }
 
+/* Resets the Xen limit, sets new target, optionally schedules work */
+void balloon_set_new_target(unsigned long target, int start)
+{
+	if (!target) return; /* compiled as module, selfballoon on */
+	/* No need for lock. Not read-modify-write updates. */
+	bs.hard_limit   = ~0UL;
+	if (down_hyster && target < totalram_pages)
+		target = totalram_pages -
+			((totalram_pages - target) / down_hyster);
+	else if (up_hyster && target > totalram_pages)
+		target = totalram_pages +
+			((target - totalram_pages) / up_hyster);
+	bs.target_pages = max(target, minimum_target());
+	if (start) schedule_work(&balloon_worker);
+}
+
+
 /*
  * We avoid multiple worker processes conflicting via the balloon mutex.
  * We may of course race updates of the target counts (which are protected
@@ -391,9 +478,26 @@ static void balloon_process(void *unused
 {
 	int need_sleep = 0;
 	long credit;
+	unsigned long target_pages;
 
 	mutex_lock(&balloon_mutex);
 
+	if (xenstored_ready) {
+		(void)xenbus_printf(XBT_NIL, "memory", "actual", "%lu",
+			totalram_pages << (PAGE_SHIFT - 10));
+		(void)xenbus_printf(XBT_NIL, "memory", "minimum", "%lu",
+			minimum_target() << (PAGE_SHIFT - 10));
+	}
+
+	if (selfballoon) {
+		target_pages = get_vm_committed();
+		balloon_set_new_target(target_pages, 0);
+		if (xenstored_ready)
+			(void)xenbus_printf(XBT_NIL, "memory", "selftarget",
+				"%lu", target_pages << (PAGE_SHIFT - 10));
+//		IPRINTK("setting new self-ballooning target=%ld kB\n",
+//			target_pages << (PAGE_SHIFT - 10));
+	}
 	do {
 		credit = current_target() - bs.current_pages;
 		if (credit > 0)
@@ -408,19 +512,73 @@ static void balloon_process(void *unused
 	} while ((credit != 0) && !need_sleep);
 
 	/* Schedule more work if there is some still to be done. */
-	if (current_target() != bs.current_pages)
+	if ((current_target() != bs.current_pages) || selfballoon)
 		mod_timer(&balloon_timer, jiffies + HZ);
 
 	mutex_unlock(&balloon_mutex);
 }
 
-/* Resets the Xen limit, sets new target, and kicks off processing. */
-void balloon_set_new_target(unsigned long target)
+static struct xenbus_watch selfballoon_watch =
 {
-	/* No need for lock. Not read-modify-write updates. */
-	bs.hard_limit   = ~0UL;
-	bs.target_pages = max(target, minimum_target());
-	schedule_work(&balloon_worker);
+	.node = "memory/selfballoon"
+};
+
+/* React to a change in the selfballoon key */
+static void watch_selfballoon(struct xenbus_watch *watch,
+			 const char **vec, unsigned int len)
+{
+	unsigned long enable, vm_pages;
+	int err;
+
+	err = xenbus_scanf(XBT_NIL, "memory", "selfballoon", "%lu", &enable);
+	if (err != 1) {
+		/* This is ok (for domain0 at least) - so just return */
+		return;
+	}
+	selfballoon = enable;
+	if (!enable) return;
+	vm_pages = get_vm_committed();
+	balloon_set_new_target(vm_pages,1);
+}
+
+static struct xenbus_watch uphysteresis_watch =
+{
+	.node = "memory/uphysteresis"
+};
+
+/* React to a change in the uphysteresis key */
+static void watch_uphysteresis(struct xenbus_watch *watch,
+			 const char **vec, unsigned int len)
+{
+	unsigned int hyster;
+	int err;
+
+	err = xenbus_scanf(XBT_NIL, "memory", "uphysteresis", "%u", &hyster);
+	if (err != 1) {
+		/* This is ok (for domain0 at least) - so just return */
+		return;
+	}
+	up_hyster = min(hyster,100);
+}
+
+static struct xenbus_watch downhysteresis_watch =
+{
+	.node = "memory/downhysteresis"
+};
+
+/* React to a change in the downhysteresis key */
+static void watch_downhysteresis(struct xenbus_watch *watch,
+			 const char **vec, unsigned int len)
+{
+	unsigned int hyster;
+	int err;
+
+	err = xenbus_scanf(XBT_NIL, "memory", "downhysteresis", "%u", &hyster);
+	if (err != 1) {
+		/* This is ok (for domain0 at least) - so just return */
+		return;
+	}
+	down_hyster = min(hyster,100);
 }
 
 static struct xenbus_watch target_watch =
@@ -444,18 +602,53 @@ static void watch_target(struct xenbus_w
 	/* The given memory/target value is in KiB, so it needs converting to
 	 * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10.
 	 */
-	balloon_set_new_target(new_target >> (PAGE_SHIFT - 10));
+	balloon_set_new_target(new_target >> (PAGE_SHIFT - 10), 1);
 }
 
 static int balloon_init_watcher(struct notifier_block *notifier,
 				unsigned long event,
 				void *data)
 {
+	unsigned long enable, vm_pages;
+	unsigned int value;
 	int err;
 
+	/* ensure xenbus values and /proc/xen/balloon are consistent */
+	err = xenbus_scanf(XBT_NIL, "memory", "selfballoon", "%lu", &enable);
+	if (err == 1) {
+		selfballoon = enable;
+		if (enable) {
+			vm_pages = get_vm_committed();
+			balloon_set_new_target(vm_pages, 1);
+		}
+	}
+	else
+		(void)xenbus_printf(XBT_NIL, "memory", "selfballoon", "%lu",
+			selfballoon ? 1UL : 0UL);
+
+	err = xenbus_scanf(XBT_NIL, "memory", "downhysteresis", "%u", &value);
+	if (err == 1)
+		down_hyster = value;
+	else
+		(void)xenbus_printf(XBT_NIL, "memory", "downhysteresis",
+			"%u", down_hyster);
+
+
+	err = xenbus_scanf(XBT_NIL, "memory", "uphysteresis", "%u", &value);
+	if (err == 1)
+		up_hyster = value;
+	else
+		(void)xenbus_printf(XBT_NIL, "memory", "uphysteresis",
+			"%u", up_hyster);
+
 	err = register_xenbus_watch(&target_watch);
+	err |= register_xenbus_watch(&selfballoon_watch);
+	err |= register_xenbus_watch(&uphysteresis_watch);
+	err |= register_xenbus_watch(&downhysteresis_watch);
 	if (err)
 		printk(KERN_ERR "Failed to set balloon watcher\n");
+	else xenstored_ready = 1;
+
 
 	return NOTIFY_DONE;
 }
@@ -466,11 +659,12 @@ static int balloon_write(struct file *fi
 {
 	char memstring[64], *endchar;
 	unsigned long long target_bytes;
+	unsigned long vm_pages;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (count <= 1)
+	if (!count)
 		return -EBADMSG; /* runt */
 	if (count > sizeof(memstring))
 		return -EFBIG;   /* too long */
@@ -480,7 +674,32 @@ static int balloon_write(struct file *fi
 	memstring[sizeof(memstring)-1] = '\0';
 
 	target_bytes = memparse(memstring, &endchar);
-	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
+	if (target_bytes <= 1) {
+		selfballoon = target_bytes;
+		if (xenstored_ready) { /* let watch_selfballoon handle */
+			(void)xenbus_printf(XBT_NIL, "memory",
+				"selfballoon", "%lu", selfballoon ? 1UL : 0UL);
+		} else if (selfballoon) {
+			vm_pages = get_vm_committed();
+			balloon_set_new_target(vm_pages, 1);
+		}
+	}
+	else if (target_bytes <= 100) {
+		/* set downhysteresis to n, n==100 sets to zero */
+		down_hyster = (target_bytes==100) ? 0 : target_bytes;
+		if (xenstored_ready)
+			(void)xenbus_printf(XBT_NIL, "memory",
+				"downhysteresis", "%u", down_hyster);
+	}
+	else if (target_bytes <= 200) {
+		/* set uphysteresis to n-100, n==200 sets to zero */
+		up_hyster = (target_bytes==200) ? 0 : target_bytes-100;
+		if (xenstored_ready)
+			(void)xenbus_printf(XBT_NIL, "memory",
+				"uphysteresis", "%u", up_hyster);
+	}
+	else
+		balloon_set_new_target(target_bytes >> PAGE_SHIFT, 1);
 
 	return count;
 }
@@ -492,12 +711,16 @@ static int balloon_read(char *page, char
 
 	len = sprintf(
 		page,
+		"Self-ballooning:    %s\n"
+		"Downward hysteresis: %3u\n"
+		"Upward hysteresis: %3u\n"
 		"Current allocation: %8lu kB\n"
 		"Requested target:   %8lu kB\n"
 		"Low-mem balloon:    %8lu kB\n"
 		"High-mem balloon:   %8lu kB\n"
 		"Driver pages:       %8lu kB\n"
 		"Xen hard limit:     ",
+		(selfballoon?"on":"off"), down_hyster, up_hyster,
 		PAGES2KB(bs.current_pages), PAGES2KB(bs.target_pages), 
 		PAGES2KB(bs.balloon_low), PAGES2KB(bs.balloon_high),
 		PAGES2KB(bs.driver_pages));
@@ -564,6 +787,9 @@ static int __init balloon_init(void)
 #endif
 
 	target_watch.callback = watch_target;
+	selfballoon_watch.callback = watch_selfballoon;
+	downhysteresis_watch.callback = watch_downhysteresis;
+	uphysteresis_watch.callback = watch_uphysteresis;
 	xenstore_notifier.notifier_call = balloon_init_watcher;
 
 	register_xenstore_notifier(&xenstore_notifier);
drivers/xen/balloon/common.h +1 -1
diff -r e39cf97647af drivers/xen/balloon/common.h
@@ -53,6 +53,6 @@ int balloon_sysfs_init(void);
 int balloon_sysfs_init(void);
 void balloon_sysfs_exit(void);
 
-void balloon_set_new_target(unsigned long target);
+void balloon_set_new_target(unsigned long target, int start);
 
 #endif /* __XEN_BALLOON_COMMON_H__ */
drivers/xen/balloon/sysfs.c +1 -1
diff -r e39cf97647af drivers/xen/balloon/sysfs.c
@@ -79,7 +79,7 @@ static ssize_t store_target_kb(struct sy
 	strcpy(memstring, buf);
 	
 	target_bytes = memparse(memstring, &endchar);
-	balloon_set_new_target(target_bytes >> PAGE_SHIFT);
+	balloon_set_new_target(target_bytes >> PAGE_SHIFT, 1);
 	
 	return count;
 }

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel