From: http://xenbits.xen.org/hg/XCP/linux-2.6.32.pq.hg/file/.../blkback_multi_page_ring
Author: someone@citrix.com
Subject: blkback: allow using multiple page frontend communication ring
Patch-mainline: n/a

[jb: bumped max order to 4 (to match frontend patch), some other cleanup]
[jb: only map as many ring pages as are actually going to be used]
Acked-by: jbeulich@suse.com

--- sle11sp3.orig/drivers/xen/blkback/blkback.c	2014-08-07 14:31:46.000000000 +0200
+++ sle11sp3/drivers/xen/blkback/blkback.c	2013-06-07 10:52:00.000000000 +0200
@@ -39,6 +39,7 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 #include <linux/list.h>
+#include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/loop.h>
 #include <linux/falloc.h>
@@ -58,9 +59,10 @@
  * 
  * This will increase the chances of being able to write whole tracks.
  * 64 should be enough to keep us competitive with Linux.
+ * 128 is required to make blkif_max_ring_page_order = 2 a useful default.
  */
-static unsigned int blkif_reqs = 64;
-module_param_named(reqs, blkif_reqs, uint, 0);
+static unsigned int blkif_reqs = 4 * BITS_PER_LONG;
+module_param_named(reqs, blkif_reqs, uint, 0444);
 MODULE_PARM_DESC(reqs, "Number of blkback requests to allocate");
 
 /* Run-time switchable: /sys/module/blkback/parameters/ */
@@ -69,6 +71,33 @@ static unsigned int debug_lvl;
 module_param(log_stats, bool, 0644);
 module_param(debug_lvl, uint, 0644);
 
+/* Order of maximum shared ring size advertised to the front end. */
+unsigned int blkif_max_ring_page_order/* XXX = sizeof(long) / 4*/;
+
+static int set_max_ring_order(const char *buf, struct kernel_param *kp)
+{
+	unsigned long order;
+	int err = strict_strtoul(buf, 0, &order);
+
+	if (err || order > BLKIF_MAX_RING_PAGE_ORDER)
+		return -EINVAL;
+
+	if (blkif_reqs < BLK_RING_SIZE(order))
+		pr_warn("WARNING: I/O request space (%u reqs) < ring order %lu, "
+			"consider increasing " KBUILD_MODNAME ".reqs to >= %lu.\n",
+			blkif_reqs, order,
+			roundup_pow_of_two(BLK_RING_SIZE(order)));
+
+	blkif_max_ring_page_order = order;
+
+	return 0;
+}
+
+module_param_call(max_ring_page_order,
+		  set_max_ring_order, param_get_uint,
+		  &blkif_max_ring_page_order, 0644);
+MODULE_PARM_DESC(max_ring_page_order, "log2 of maximum ring size (in pages)");
+
 /*
  * Each outstanding request that we've passed to the lower device layers has a 
  * 'pending_req' allocated to it. Each buffer_head that completes decrements 
--- sle11sp3.orig/drivers/xen/blkback/common.h	2013-06-07 10:51:53.000000000 +0200
+++ sle11sp3/drivers/xen/blkback/common.h	2013-06-07 10:52:01.000000000 +0200
@@ -60,7 +60,9 @@ struct vbd {
 	sector_t       size;        /* Cached size parameter */
 };
 
-struct backend_info;
+#define BLKIF_MAX_RING_PAGE_ORDER 4
+#define BLKIF_MAX_RING_PAGES (1 << BLKIF_MAX_RING_PAGE_ORDER)
+#define BLK_RING_SIZE(order) __CONST_RING_SIZE(blkif, PAGE_SIZE << (order))
 
 typedef struct blkif_st {
 	/* Unique identifier for this interface. */
@@ -113,10 +115,14 @@ struct backend_info
 	char *mode;
 };
 
+extern unsigned int blkif_max_ring_page_order;
+
 blkif_t *blkif_alloc(domid_t domid);
 void blkif_disconnect(blkif_t *blkif);
 void blkif_free(blkif_t *blkif);
-int blkif_map(blkif_t *blkif, grant_ref_t, evtchn_port_t);
+unsigned int blkif_ring_size(enum blkif_protocol, unsigned int);
+int blkif_map(blkif_t *blkif, const grant_ref_t [],
+	      unsigned int nr_refs, evtchn_port_t);
 void vbd_resize(blkif_t *blkif);
 
 #define blkif_get(_b) (atomic_inc(&(_b)->refcnt))
--- sle11sp3.orig/drivers/xen/blkback/interface.c	2013-06-07 10:51:13.000000000 +0200
+++ sle11sp3/drivers/xen/blkback/interface.c	2013-06-07 10:52:01.000000000 +0200
@@ -58,8 +58,30 @@ blkif_t *blkif_alloc(domid_t domid)
 	return blkif;
 }
 
-int blkif_map(blkif_t *blkif, grant_ref_t ring_ref, evtchn_port_t evtchn)
+unsigned int blkif_ring_size(enum blkif_protocol protocol,
+			     unsigned int ring_size)
 {
+	unsigned long size = (unsigned long)ring_size << PAGE_SHIFT;
+
+#define BLKBK_RING_SIZE(p) PFN_UP(offsetof(struct blkif_##p##_sring, \
+		ring[__CONST_RING_SIZE(blkif_##p, size)]))
+	switch (protocol) {
+	case BLKIF_PROTOCOL_NATIVE:
+		return BLKBK_RING_SIZE(native);
+	case BLKIF_PROTOCOL_X86_32:
+		return BLKBK_RING_SIZE(x86_32);
+	case BLKIF_PROTOCOL_X86_64:
+		return BLKBK_RING_SIZE(x86_64);
+	}
+#undef BLKBK_RING_SIZE
+	BUG();
+	return 0;
+}
+
+int blkif_map(blkif_t *blkif, const grant_ref_t ring_ref[],
+	      unsigned int nr_refs, evtchn_port_t evtchn)
+{
+	unsigned long size = (unsigned long)nr_refs << PAGE_SHIFT;
 	struct vm_struct *area;
 	int err;
 
@@ -67,7 +89,7 @@ int blkif_map(blkif_t *blkif, grant_ref_
 	if (blkif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(blkif->be->dev, &ring_ref, 1);
+	area = xenbus_map_ring_valloc(blkif->be->dev, ring_ref, nr_refs);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	blkif->blk_ring_area = area;
@@ -75,7 +97,7 @@ int blkif_map(blkif_t *blkif, grant_ref_
 	switch (blkif->blk_protocol) {
 #define BLKBK_RING_INIT(p) ({ \
 		struct blkif_##p##_sring *sring = area->addr; \
-		BACK_RING_INIT(&blkif->blk_rings.p, sring, PAGE_SIZE); \
+		BACK_RING_INIT(&blkif->blk_rings.p, sring, size); \
 	})
 	case BLKIF_PROTOCOL_NATIVE:
 		BLKBK_RING_INIT(native);
--- sle11sp3.orig/drivers/xen/blkback/xenbus.c	2013-01-08 17:22:45.000000000 +0100
+++ sle11sp3/drivers/xen/blkback/xenbus.c	2013-01-08 17:24:20.000000000 +0100
@@ -324,6 +324,11 @@ static int blkback_probe(struct xenbus_d
 	if (err)
 		goto fail;
 
+	err = xenbus_printf(XBT_NIL, dev->nodename, "max-ring-page-order",
+			    "%u", blkif_max_ring_page_order);
+	if (err)
+		xenbus_dev_error(dev, err, "writing max-ring-page-order");
+
 	err = xenbus_switch_state(dev, XenbusStateInitWait);
 	if (err)
 		goto fail;
@@ -556,21 +561,22 @@ again:
 static int connect_ring(struct backend_info *be)
 {
 	struct xenbus_device *dev = be->dev;
-	unsigned int ring_ref, evtchn;
+	unsigned int ring_ref[BLKIF_MAX_RING_PAGES], evtchn, ring_size;
 	char *protocol;
 	int err;
 
 	DPRINTK("%s", dev->otherend);
 
-	err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%u", &ring_ref,
-			    "event-channel", "%u", &evtchn, NULL);
-	if (err) {
-		xenbus_dev_fatal(dev, err,
-				 "reading %s/ring-ref and event-channel",
+	err = xenbus_scanf(XBT_NIL, dev->otherend, "event-channel", "%u",
+			   &evtchn);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, -EINVAL, "reading %s/event-channel",
 				 dev->otherend);
-		return err;
+		return -EINVAL;
 	}
 
+	pr_info("blkback: event-channel %u\n", evtchn);
+
 	be->blkif->blk_protocol = BLKIF_PROTOCOL_NATIVE;
 	protocol = xenbus_read(XBT_NIL, dev->otherend, "protocol", NULL);
 	if (IS_ERR(protocol))
@@ -584,20 +590,62 @@ static int connect_ring(struct backend_i
 		be->blkif->blk_protocol = BLKIF_PROTOCOL_X86_64;
 #endif
 	else if (0 != strcmp(protocol, XEN_IO_PROTO_ABI_NATIVE)) {
-		xenbus_dev_fatal(dev, err, "unknown fe protocol %s", protocol);
+		xenbus_dev_fatal(dev, -EINVAL, "unknown fe protocol %s",
+				 protocol);
 		kfree(protocol);
-		return -1;
+		return -EINVAL;
 	}
-	pr_info("blkback: ring-ref %u, event-channel %u, protocol %d (%s)\n",
-		ring_ref, evtchn, be->blkif->blk_protocol,
+
+	pr_info("blkback: protocol %d (%s)\n",
+		be->blkif->blk_protocol,
 		protocol ?: "unspecified, assuming native");
 	kfree(protocol);
 
+	err = xenbus_scanf(XBT_NIL, dev->otherend, "ring-page-order", "%u",
+			   &ring_size);
+	if (err != 1) {
+		err = xenbus_scanf(XBT_NIL, dev->otherend, "ring-ref",
+				   "%u", ring_ref);
+		if (err != 1) {
+			xenbus_dev_fatal(dev, -EINVAL, "reading %s/ring-ref",
+					 dev->otherend);
+			return -EINVAL;
+		}
+		pr_info("blkback: ring-ref %u\n", *ring_ref);
+		ring_size = 1;
+	} else if (ring_size > blkif_max_ring_page_order) {
+		xenbus_dev_fatal(dev, -EINVAL,
+				 "%s/ring-page-order (%u) too big",
+				 dev->otherend, ring_size);
+		return -EINVAL;
+	} else {
+		unsigned int i;
+
+		ring_size = blkif_ring_size(be->blkif->blk_protocol,
+					    1U << ring_size);
+
+		for (i = 0; i < ring_size; i++) {
+			char ring_ref_name[16];
+
+			snprintf(ring_ref_name, sizeof (ring_ref_name),
+				 "ring-ref%u", i);
+			err = xenbus_scanf(XBT_NIL, dev->otherend,
+					   ring_ref_name, "%u",
+					   ring_ref + i);
+			if (err != 1) {
+				xenbus_dev_fatal(dev, -EINVAL, "reading %s/%s",
+						 dev->otherend, ring_ref_name);
+				return -EINVAL;
+			}
+
+			pr_info("blkback: ring-ref%u %u\n", i, ring_ref[i]);
+		}
+	}
+
 	/* Map the shared frame, irq etc. */
-	err = blkif_map(be->blkif, ring_ref, evtchn);
+	err = blkif_map(be->blkif, ring_ref, ring_size, evtchn);
 	if (err) {
-		xenbus_dev_fatal(dev, err, "mapping ring-ref %u port %u",
-				 ring_ref, evtchn);
+		xenbus_dev_fatal(dev, err, "mapping ring-refs and evtchn");
 		return err;
 	}
 
