Subject: 32-on-64 blkif protocol negotiation fallback for old guests.
From: kraxel@suse.de
References: 244055
Patch-mainline: never.

See the comment below.  Oh well.

--- sle11sp1-2010-03-29.orig/drivers/xen/Kconfig	2010-03-26 08:39:39.000000000 +0100
+++ sle11sp1-2010-03-29/drivers/xen/Kconfig	2010-03-29 09:12:44.000000000 +0200
@@ -29,6 +29,9 @@ config XEN_PRIVCMD
 	def_bool y
 	depends on PROC_FS
 
+config XEN_DOMCTL
+	tristate
+
 config XEN_XENBUS_DEV
 	def_bool y
 	depends on PROC_FS
@@ -48,6 +51,7 @@ config XEN_BLKDEV_BACKEND
 	tristate "Block-device backend driver"
         depends on XEN_BACKEND
 	default XEN_BACKEND
+	select XEN_DOMCTL
 	help
 	  The block-device backend driver allows the kernel to export its
 	  block devices to other guests via a high-performance shared-memory
@@ -57,6 +61,7 @@ config XEN_BLKDEV_TAP
 	tristate "Block-device tap backend driver"
 	depends on XEN_BACKEND
 	default XEN_BACKEND
+	select XEN_DOMCTL
 	help
 	  The block tap driver is an alternative to the block back driver
 	  and allows VM block requests to be redirected to userspace through
--- sle11sp1-2010-03-29.orig/drivers/xen/blkback/xenbus.c	2010-03-22 12:53:24.000000000 +0100
+++ sle11sp1-2010-03-29/drivers/xen/blkback/xenbus.c	2010-03-22 12:53:34.000000000 +0100
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/kthread.h>
 #include "common.h"
+#include "../core/domctl.h"
 
 #undef DPRINTK
 #define DPRINTK(fmt, args...)				\
@@ -492,8 +493,10 @@ static int connect_ring(struct backend_i
 	be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
 	err = xenbus_gather(XBT_NIL, dev->otherend, "protocol",
 			    "%63s", protocol, NULL);
-	if (err)
-		strcpy(protocol, "unspecified, assuming native");
+	if (err) {
+		strcpy(protocol, "unspecified");
+		be->blkif->blk_protocol = xen_guest_blkif_protocol(be->blkif->domid);
+	}
 	else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE))
 		be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
 	else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32))
--- sle11sp1-2010-03-29.orig/drivers/xen/blktap/xenbus.c	2010-01-27 14:59:26.000000000 +0100
+++ sle11sp1-2010-03-29/drivers/xen/blktap/xenbus.c	2010-01-27 15:00:09.000000000 +0100
@@ -39,6 +39,7 @@
 #include <linux/kthread.h>
 #include <xen/xenbus.h>
 #include "common.h"
+#include "../core/domctl.h"
 
 
 struct backend_info
@@ -432,8 +433,10 @@ static int connect_ring(struct backend_i
 	be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
 	err = xenbus_gather(XBT_NIL, dev->otherend, "protocol",
 			    "%63s", protocol, NULL);
-	if (err)
-		strcpy(protocol, "unspecified, assuming native");
+	if (err) {
+		strcpy(protocol, "unspecified");
+		be->blkif->blk_protocol = xen_guest_blkif_protocol(be->blkif->domid);
+	}
 	else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE))
 		be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
 	else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32))
--- sle11sp1-2010-03-29.orig/drivers/xen/core/Makefile	2009-11-06 10:52:02.000000000 +0100
+++ sle11sp1-2010-03-29/drivers/xen/core/Makefile	2010-01-04 16:17:00.000000000 +0100
@@ -12,4 +12,7 @@ obj-$(CONFIG_XEN_SYSFS)		+= xen_sysfs.o
 obj-$(CONFIG_XEN_SMPBOOT)	+= smpboot.o
 obj-$(CONFIG_SMP)		+= spinlock.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o
+obj-$(CONFIG_XEN_DOMCTL)	+= domctl.o
+CFLAGS_domctl.o			:= -D__XEN_PUBLIC_XEN_H__ -D__XEN_PUBLIC_GRANT_TABLE_H__
+CFLAGS_domctl.o			+= -D__XEN_TOOLS__ -imacros xen/interface/domctl.h
 obj-$(CONFIG_XEN_XENCOMM)	+= xencomm.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sle11sp1-2010-03-29/drivers/xen/core/domctl.c	2010-01-04 16:15:58.000000000 +0100
@@ -0,0 +1,120 @@
+/*
+ * !!!  dirty hack alert  !!!
+ *
+ * Problem: old guests kernels don't have a "protocol" node
+ *          in the frontend xenstore directory, so mixing
+ *          32 and 64bit domains doesn't work.
+ *
+ * Upstream plans to solve this in the tools, by letting them
+ * create a protocol node.  Which certainly makes sense.
+ * But it isn't trivial and isn't done yet.  Too bad.
+ *
+ * So for the time being we use the get_address_size domctl
+ * hypercall for a pretty good guess.  Not nice as the domctl
+ * hypercall isn't supposed to be used by the kernel.  Because
+ * we don't want to have dependencies between dom0 kernel and
+ * xen kernel versions.  Now we have one.  Ouch.
+ */
+#undef __XEN_PUBLIC_XEN_H__
+#undef __XEN_PUBLIC_GRANT_TABLE_H__
+#undef __XEN_TOOLS__
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/hypervisor.h>
+#include <xen/blkif.h>
+
+#include "domctl.h"
+
+/* stuff copied from xen/interface/domctl.h, which we can't
+ * include directly for the reasons outlined above .... */
+
+typedef struct xen_domctl_address_size {
+	uint32_t size;
+} xen_domctl_address_size_t;
+
+typedef __attribute__((aligned(8))) uint64_t uint64_aligned_t;
+
+union xen_domctl {
+	/* v4: sles10 sp1: xen 3.0.4 + 32-on-64 patches */
+	struct {
+		uint32_t cmd;
+		uint32_t interface_version;
+		domid_t  domain;
+		union {
+			/* left out lots of other struct xen_domctl_foobar */
+			struct xen_domctl_address_size       address_size;
+			uint64_t                             dummy_align;
+			uint8_t                              dummy_pad[128];
+		};
+	} v4;
+
+	/* v5: upstream: xen 3.1, v6: upstream: xen 4.0 */
+	struct {
+		uint32_t cmd;
+		uint32_t interface_version;
+		domid_t  domain;
+		union {
+			struct xen_domctl_address_size       address_size;
+			uint64_aligned_t                     dummy_align;
+			uint8_t                              dummy_pad[128];
+		};
+	} v5, v6;
+};
+
+/* The actual code comes here */
+
+static inline int hypervisor_domctl(void *domctl)
+{
+	return _hypercall1(int, domctl, domctl);
+}
+
+int xen_guest_address_size(int domid)
+{
+	union xen_domctl domctl;
+	int low, ret;
+
+#define guest_address_size(ver) do {					\
+	memset(&domctl, 0, sizeof(domctl));				\
+	domctl.v##ver.cmd = XEN_DOMCTL_get_address_size;		\
+	domctl.v##ver.interface_version = low = ver;			\
+	domctl.v##ver.domain = domid;					\
+	ret = hypervisor_domctl(&domctl) ?: domctl.v##ver.address_size.size; \
+	if (ret == 32 || ret == 64) {					\
+		printk("v" #ver " domctl worked ok: dom%d is %d-bit\n",	\
+		       domid, ret);					\
+		return ret;						\
+	}								\
+} while (0)
+
+	BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 6);
+	guest_address_size(6);
+#if CONFIG_XEN_COMPAT < 0x040000
+	guest_address_size(5);
+#endif
+#if CONFIG_XEN_COMPAT < 0x030100
+	guest_address_size(4);
+#endif
+
+	ret = BITS_PER_LONG;
+	printk("v%d...6 domctls failed, assuming dom%d is native: %d\n",
+	       low, domid, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(xen_guest_address_size);
+
+int xen_guest_blkif_protocol(int domid)
+{
+	int address_size = xen_guest_address_size(domid);
+
+	if (address_size == BITS_PER_LONG)
+		return BLKIF_PROTOCOL_NATIVE;
+	if (address_size == 32)
+		return BLKIF_PROTOCOL_X86_32;
+	if (address_size == 64)
+		return BLKIF_PROTOCOL_X86_64;
+	return BLKIF_PROTOCOL_NATIVE;
+}
+EXPORT_SYMBOL_GPL(xen_guest_blkif_protocol);
+
+MODULE_LICENSE("GPL");
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sle11sp1-2010-03-29/drivers/xen/core/domctl.h	2008-09-15 15:10:39.000000000 +0200
@@ -0,0 +1,2 @@
+int xen_guest_address_size(int domid);
+int xen_guest_blkif_protocol(int domid);
