Skip to contentSkip to conversation

add self-ballooning to balloon driver

PATCH New Dan Magenheimer 1 participant 1 message Apr 24, 2008 Xen hypervisor interface

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 add self-ballooning to balloon driver

The attached patch adds a "self-ballooning" feature to the balloon driver. Currently it only works when the balloon driver is compiled into the kernel (e.g. not as a pv-on-hvm driver) because, alas, the key kernel global variable is not exported.

It can be turned on with "echo 1 > /proc/xen/balloon" and off again with "echo 0 > /proc/xen/balloon". It defaults off.

I've tested it with a program that randomly eats memory and it seems to work pretty well balancing two domains and surrendering lots of memory (but not too much) when a domain is idle. The only deleterious effect I've seen is that performance suffers for a second or so when a big memory app is launched on a previously ballooned-down system (due to swapping). But this is a small price to pay in some real world environments.

It obviously shouldn't be used on a domain that doesn't have a (virtual) swap disk configured. If you foresee other issues, please let me know.

I've got lots of ideas for building on this (for example rate-limiting memory surrendering) but thought I'd submit the basic patch for others to try/test.

Dan

===================================
If Xen could save time in a bottle / then clocks wouldn't virtually skew / It would save every tick / for VMs that aren't quick /
and Xen then would send them anew
(with apologies to the late great Jim Croce)

drivers/xen/balloon/balloon.c +51 -9
diff -r 7f8b544237bf 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,6 +79,13 @@ 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)];
 
@@ -86,11 +94,14 @@ extern unsigned long totalram_pages;
 
 #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. */
@@ -381,6 +392,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 +412,16 @@ static void balloon_process(void *unused
 {
 	int need_sleep = 0;
 	long credit;
+	unsigned long target_pages;
 
 	mutex_lock(&balloon_mutex);
 
+	if (selfballoon) {
+		target_pages = atomic_read(&vm_committed_space);
+		balloon_set_new_target(target_pages);
+//		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,18 +436,17 @@ 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);
 }
 
@@ -433,7 +460,7 @@ static void watch_target(struct xenbus_w
 			 const char **vec, unsigned int len)
 {
 	unsigned long long new_target;
-	int err;
+	int err = 1;
 
 	err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target);
 	if (err != 1) {
@@ -444,7 +471,7 @@ 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,
@@ -466,11 +493,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 +508,19 @@ 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) {
+		selfballoon = 0;
+		IPRINTK("self-ballooning disabled\n");
+	}
+	else if (target_bytes == 1) {
+		selfballoon = 1;
+		vm_pages = atomic_read(&vm_committed_space);
+		IPRINTK("self-ballooning enabled, new target=%ld kB\n",
+			vm_pages << (PAGE_SHIFT - 10));
+		balloon_start_new_target(vm_pages);
+	}
+	else
+		balloon_start_new_target(target_bytes >> PAGE_SHIFT);
 
 	return count;
 }
@@ -492,12 +532,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));

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