Oracle bug 7556514
32-bit PVHVM guest running under 64-bit hypervisor will crash if "xm mem-set"
attempts to change its memory reservation.

Change the 32-bit PVHVM balloon driver to check if memory reservation changes
are supported.  This is done by issuing a zero page XENMEM_decrease_reservation
hypervisor call to see if it returns -ENOSYS.

Also fix two compiler warnings by changing:
        "#ifdef CONFIG_PROC_FS"
to:
        "#if defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST)"
for the definitions of balloon_pde, balloon_read() and balloon_write() to
match the conditional wrapper around their reference.

diff -uNrp linux-2.6.18.x86_64.orig/drivers/xen/balloon/balloon.c linux-2.6.18.x86_64/drivers/xen/balloon/balloon.c
--- linux-2.6.18.x86_64.orig/drivers/xen/balloon/balloon.c	2010-11-18 14:14:44.000000000 -0800
+++ linux-2.6.18.x86_64/drivers/xen/balloon/balloon.c	2010-11-18 16:33:45.000000000 -0800
@@ -70,9 +70,9 @@
 
 #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10))
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST)
 static struct proc_dir_entry *balloon_pde;
-#endif
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST) */
 
 static DECLARE_MUTEX(balloon_mutex);
 
@@ -139,6 +139,15 @@ static struct timer_list balloon_timer;
 #define WPRINTK(fmt, args...) \
 	printk(KERN_WARNING "xen_mem: " fmt, ##args)
 
+/*
+ * Oracle bug 7556514
+ * if PVHVM and guest is X86-32bit, need to check if mem reservation changes
+ * are supported by the hypervisor.
+ */
+#if defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64)
+static int mem_reservation_change_supported(void);
+#endif /* defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64) */
+
 /* balloon_append: add the given page to the balloon. */
 static void balloon_append(struct page *page)
 {
@@ -358,6 +367,14 @@ static void balloon_process(void *unused
 
 	down(&balloon_mutex);
 
+	 /* Oracle bug 7556514 */
+#if defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64)
+	if (mem_reservation_change_supported() == 0) {
+		up(&balloon_mutex);
+		return;
+	}
+#endif /* defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64) */
+
 	do {
 		credit = current_target() - current_pages;
 		if (credit > 0)
@@ -442,7 +459,7 @@ static int balloon_init_watcher(struct n
 	return NOTIFY_DONE;
 }
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST)
 static int balloon_write(struct file *file, const char __user *buffer,
 			 unsigned long count, void *data)
 {
@@ -464,6 +481,14 @@ static int balloon_write(struct file *fi
 		return -EFAULT;
 	memstring[sizeof(memstring)-1] = '\0';
 
+	 /* Oracle bug 7556514 */
+#if defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64)
+	if (mem_reservation_change_supported() == 0) {
+		/* memory reservation changes are not supported */
+		return -ENOSYS;
+	}
+#endif /* defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64) */
+
 	target_bytes = memparse(memstring, &endchar);
 	set_new_target(target_bytes >> PAGE_SHIFT);
 
@@ -490,7 +515,7 @@ static int balloon_read(char *page, char
 	*eof = 1;
 	return len;
 }
-#endif
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST) */
 
 static struct notifier_block xenstore_notifier;
 
@@ -529,7 +554,7 @@ static int __init balloon_init(void)
 
 	balloon_pde->read_proc  = balloon_read;
 	balloon_pde->write_proc = balloon_write;
-#endif
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_XEN_PRIVILEGED_GUEST) */
     
 #if defined(CONFIG_X86) && defined(CONFIG_XEN) 
 	/* Initialise the balloon with excess memory space. */
@@ -684,6 +709,77 @@ void balloon_release_driver_page(struct 
 	schedule_work(&balloon_worker);
 }
 
+/* Oracle bug 7556514 */
+#if defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64)
+/*
+ * mem_reservation_change_supported()
+ * Check to see if the hypervisor call XENMEM_decrease_reservation is supported
+ * for this guest.  For example, OVM xen (so far) has not supported it
+ * (or XENMEM_increase_reservation) from a 32-bit guest running under a
+ * 64-bit hypervisor.
+ * We assume that XENMEM_increase_reservation support is the same as support
+ * for XENMEM_decrease_reservation.
+ *
+ * We issue a XENMEM_decrease_reservation hv call specifying a change
+ * of zero pages.  We want to see if -ENOSYS is returned, indicating that
+ * the hv call is not supported for this guest.
+ * We assume that any other return value indicates the call is supported.
+ *
+ * Locking:
+ *	No locks need to be held on entry.
+ *	No locks acquired or released.
+ *		OK if "set change_supported" path is taken more than once
+ *		If CPUs race to set change_supported, they presumably will
+ *		write the same value.
+ *
+ * Return values:
+ *	0 - XENMEM_decrease_reservation is not supported
+ *	1 - XENMEM_decrease_reservation is supported
+ */
+static int mem_reservation_change_supported(void) {
+	int ret;
+	static unsigned int msg_issued = 0;
+	/* -1 == we have not checked if change is supported */
+	static int change_supported = -1;
+	struct xen_memory_reservation reservation = {
+		.nr_extents   = 0,
+		.extent_order = 0,
+		.domid        = DOMID_SELF
+	};
+
+	if (change_supported == 1) {
+		/* we have checked and changes are supported */
+		return 1;
+	}
+	else if (change_supported == 0) {
+		/* we have checked and changes not supported */
+		return 0;
+	}
+
+	/*
+	 * We have not checked if mem reservation changes are supported.
+	 * Attempt zero sized XENMEM_decrease_reservation to see if we
+	 * get -ENOSYS.
+	 */
+	ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
+	if (ret == -ENOSYS) {
+		/* memory reservation changes are not supported */
+		change_supported = 0;
+		if (!msg_issued) {
+			msg_issued = 1;
+			printk(KERN_WARNING
+			  "Balloon driver: Changing memory reservation is not supported on this guest.\n");
+			printk(KERN_WARNING
+			  "Balloon driver: 'xm mem-set' will have no effect.\n");
+		}
+		return 0;
+	}
+	/* memory reservation changes are supported */
+	change_supported = 1;
+	return 1;
+}
+#endif /*defined(CONFIG_XEN_PV_ON_HVM) && !defined(CONFIG_X86_64) */
+
 EXPORT_SYMBOL_GPL(balloon_update_driver_allowance);
 EXPORT_SYMBOL_GPL(alloc_empty_pages_and_pagevec);
 EXPORT_SYMBOL_GPL(free_empty_pages_and_pagevec);
