From: jbeulich@suse.com
Subject: extend base interfaces to support multi-page rings for frontend/backend communication
Patch-mainline: n/a

--- sle11sp4.orig/drivers/xen/blkback/interface.c	2013-06-07 10:25:50.000000000 +0200
+++ sle11sp4/drivers/xen/blkback/interface.c	2013-06-07 10:51:13.000000000 +0200
@@ -67,7 +67,7 @@ int blkif_map(blkif_t *blkif, grant_ref_
 	if (blkif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(blkif->be->dev, ring_ref);
+	area = xenbus_map_ring_valloc(blkif->be->dev, &ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	blkif->blk_ring_area = area;
--- sle11sp4.orig/drivers/xen/blktap/interface.c	2013-06-07 10:15:22.000000000 +0200
+++ sle11sp4/drivers/xen/blktap/interface.c	2013-06-07 10:51:16.000000000 +0200
@@ -66,7 +66,7 @@ int tap_blkif_map(blkif_t *blkif, struct
 	if (blkif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(dev, ring_ref);
+	area = xenbus_map_ring_valloc(dev, &ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	blkif->blk_ring_area = area;
--- sle11sp4.orig/drivers/xen/core/gnttab.c	2012-12-04 14:05:06.000000000 +0100
+++ sle11sp4/drivers/xen/core/gnttab.c	2012-03-12 14:04:06.000000000 +0100
@@ -300,6 +300,31 @@ void gnttab_end_foreign_access(grant_ref
 }
 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
 
+void gnttab_multi_end_foreign_access(unsigned int nr, grant_ref_t refs[],
+				     struct page *pages[])
+{
+	for (; nr--; ++refs) {
+		if (*refs != GRANT_INVALID_REF) {
+			if (gnttab_end_foreign_access_ref(*refs))
+				put_free_entry(*refs);
+			else if (pages) {
+				gnttab_add_deferred(*refs, *pages);
+				*pages = NULL;
+			} else
+				gnttab_add_deferred(*refs, NULL);
+			*refs = GRANT_INVALID_REF;
+		}
+		if (pages) {
+			if (*pages) {
+				__free_page(*pages);
+				*pages = NULL;
+			}
+			++pages;
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(gnttab_multi_end_foreign_access);
+
 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
 {
 	int ref;
--- sle11sp4.orig/drivers/xen/netback/interface.c	2013-02-19 15:22:16.000000000 +0100
+++ sle11sp4/drivers/xen/netback/interface.c	2013-02-19 15:55:56.000000000 +0100
@@ -260,11 +260,11 @@ int netif_map(struct backend_info *be, g
 	if (netif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(be->dev, tx_ring_ref);
+	area = xenbus_map_ring_valloc(be->dev, &tx_ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	netif->tx_comms_area = area;
-	area = xenbus_map_ring_valloc(be->dev, rx_ring_ref);
+	area = xenbus_map_ring_valloc(be->dev, &rx_ring_ref, 1);
 	if (IS_ERR(area)) {
 		err = PTR_ERR(area);
 		goto err_rx;
--- sle11sp4.orig/drivers/xen/pciback/xenbus.c	2012-10-18 17:18:29.000000000 +0200
+++ sle11sp4/drivers/xen/pciback/xenbus.c	2012-10-19 16:25:00.000000000 +0200
@@ -89,7 +89,7 @@ static int pciback_do_attach(struct pcib
 		"Attaching to frontend resources - gnt_ref=%d evtchn=%d\n",
 		gnt_ref, remote_evtchn);
 
-	area = xenbus_map_ring_valloc(pdev->xdev, gnt_ref);
+	area = xenbus_map_ring_valloc(pdev->xdev, &gnt_ref, 1);
 	if (IS_ERR(area)) {
 		err = PTR_ERR(area);
 		goto out;
--- sle11sp4.orig/drivers/xen/scsiback/interface.c	2013-06-07 10:19:13.000000000 +0200
+++ sle11sp4/drivers/xen/scsiback/interface.c	2013-06-07 10:51:23.000000000 +0200
@@ -73,7 +73,7 @@ int scsiback_init_sring(struct vscsibk_i
 		return -1;
 	}
 
-	area = xenbus_map_ring_valloc(info->dev, ring_ref);
+	area = xenbus_map_ring_valloc(info->dev, &ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	info->ring_area = area;
--- sle11sp4.orig/drivers/xen/tpmback/interface.c	2011-04-11 15:00:49.000000000 +0200
+++ sle11sp4/drivers/xen/tpmback/interface.c	2011-10-10 11:58:47.000000000 +0200
@@ -88,7 +88,7 @@ int tpmif_map(tpmif_t *tpmif, grant_ref_
 	if (tpmif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(tpmif->bi->dev, ring_ref);
+	area = xenbus_map_ring_valloc(tpmif->bi->dev, &ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	tpmif->tx_area = area;
--- sle11sp4.orig/drivers/xen/usbback/interface.c	2014-11-10 12:19:53.000000000 +0100
+++ sle11sp4/drivers/xen/usbback/interface.c	2013-06-07 10:51:29.000000000 +0200
@@ -114,11 +114,11 @@ int usbif_map(usbif_t *usbif, grant_ref_
 	if (usbif->irq)
 		return 0;
 
-	area = xenbus_map_ring_valloc(usbif->xbdev, urb_ring_ref);
+	area = xenbus_map_ring_valloc(usbif->xbdev, &urb_ring_ref, 1);
 	if (IS_ERR(area))
 		return PTR_ERR(area);
 	usbif->urb_ring_area = area;
-	area = xenbus_map_ring_valloc(usbif->xbdev, conn_ring_ref);
+	area = xenbus_map_ring_valloc(usbif->xbdev, &conn_ring_ref, 1);
 	if (IS_ERR(area)) {
 		err = PTR_ERR(area);
 		goto fail_alloc;
--- sle11sp4.orig/drivers/xen/xenbus/xenbus_backend_client.c	2011-01-31 17:49:31.000000000 +0100
+++ sle11sp4/drivers/xen/xenbus/xenbus_backend_client.c	2012-04-05 11:04:11.000000000 +0200
@@ -32,36 +32,88 @@
 
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <xen/gnttab.h>
 #include <xen/xenbus.h>
 
+static int unmap_ring_vfree(struct xenbus_device *dev, struct vm_struct *area,
+			    unsigned int nr, grant_handle_t handles[])
+{
+	unsigned int i;
+	int err = 0;
+
+	for (i = 0; i < nr; ++i) {
+		struct gnttab_unmap_grant_ref op;
+
+		gnttab_set_unmap_op(&op,
+				    (unsigned long)area->addr + i * PAGE_SIZE,
+				    GNTMAP_host_map, handles[i]);
+
+		if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+					      &op, 1))
+			BUG();
+		if (op.status == GNTST_okay)
+			continue;
+
+		xenbus_dev_error(dev, op.status,
+				 "unmapping page %u (handle %#x)",
+				 i, handles[i]);
+		err = -EINVAL;
+	}
+
+	if (!err) {
+		free_vm_area(area);
+		kfree(handles);
+	}
+
+	return err;
+}
+
 /* Based on Rusty Russell's skeleton driver's map_page */
-struct vm_struct *xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t gnt_ref)
+struct vm_struct *xenbus_map_ring_valloc(struct xenbus_device *dev,
+					 const grant_ref_t refs[],
+					 unsigned int nr)
 {
-	struct gnttab_map_grant_ref op;
+	grant_handle_t *handles = kmalloc(nr * sizeof(*handles), GFP_KERNEL);
 	struct vm_struct *area;
+	unsigned int i;
 
-	area = alloc_vm_area(PAGE_SIZE);
-	if (!area)
+	if (!handles)
 		return ERR_PTR(-ENOMEM);
 
-	gnttab_set_map_op(&op, (unsigned long)area->addr, GNTMAP_host_map,
-			  gnt_ref, dev->otherend_id);
+	area = alloc_vm_area(nr * PAGE_SIZE);
+	if (!area) {
+		kfree(handles);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	for (i = 0; i < nr; ++i) {
+		struct gnttab_map_grant_ref op;
+
+		gnttab_set_map_op(&op,
+				  (unsigned long)area->addr + i * PAGE_SIZE,
+				  GNTMAP_host_map, refs[i],
+				  dev->otherend_id);
 	
-	gnttab_check_GNTST_eagain_do_while(GNTTABOP_map_grant_ref, &op);
+		gnttab_check_GNTST_eagain_do_while(GNTTABOP_map_grant_ref,
+						   &op);
 
-	if (op.status != GNTST_okay) {
-		free_vm_area(area);
+		if (op.status == GNTST_okay) {
+			handles[i] = op.handle;
+			continue;
+		}
+
+		unmap_ring_vfree(dev, area, i, handles);
 		xenbus_dev_fatal(dev, op.status,
-				 "mapping in shared page %d from domain %d",
-				 gnt_ref, dev->otherend_id);
+				 "mapping page %u (ref %#x, dom%d)",
+				 i, refs[i], dev->otherend_id);
 		BUG_ON(!IS_ERR(ERR_PTR(op.status)));
 		return ERR_PTR(-EINVAL);
 	}
 
-	/* Stuff the handle in an unused field */
-	area->phys_addr = (unsigned long)op.handle;
+	/* Stuff the handle array in an unused field. */
+	area->phys_addr = (unsigned long)handles;
 
 	return area;
 }
@@ -71,22 +123,9 @@ EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc
 /* Based on Rusty Russell's skeleton driver's unmap_page */
 int xenbus_unmap_ring_vfree(struct xenbus_device *dev, struct vm_struct *area)
 {
-	struct gnttab_unmap_grant_ref op;
-
-	gnttab_set_unmap_op(&op, (unsigned long)area->addr, GNTMAP_host_map,
-			    (grant_handle_t)area->phys_addr);
-
-	if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
-		BUG();
-
-	if (op.status == GNTST_okay)
-		free_vm_area(area);
-	else
-		xenbus_dev_error(dev, op.status,
-				 "unmapping page at handle %d error %d",
-				 (int16_t)area->phys_addr, op.status);
-
-	return op.status == GNTST_okay ? 0 : -EINVAL;
+	return unmap_ring_vfree(dev, area,
+				get_vm_area_size(area) >> PAGE_SHIFT,
+				(void *)(unsigned long)area->phys_addr);
 }
 EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree);
 
--- sle11sp4.orig/drivers/xen/xenbus/xenbus_client.c	2011-02-01 15:04:27.000000000 +0100
+++ sle11sp4/drivers/xen/xenbus/xenbus_client.c	2012-03-09 09:01:57.000000000 +0100
@@ -356,6 +356,30 @@ int xenbus_grant_ring(struct xenbus_devi
 }
 EXPORT_SYMBOL_GPL(xenbus_grant_ring);
 
+int xenbus_multi_grant_ring(struct xenbus_device *dev, unsigned int nr,
+			    struct page *pages[], grant_ref_t refs[])
+{
+	unsigned int i;
+	int err = -EINVAL;
+
+	for (i = 0; i < nr; ++i) {
+		unsigned long pfn = page_to_pfn(pages[i]);
+
+		err = gnttab_grant_foreign_access(dev->otherend_id,
+						  pfn_to_mfn(pfn), 0);
+		if (err < 0) {
+			xenbus_dev_fatal(dev, err,
+					 "granting access to ring page #%u",
+					 i);
+			break;
+		}
+		refs[i] = err;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(xenbus_multi_grant_ring);
+
 
 /**
  * Allocate an event channel for the given xenbus_device, assigning the newly
--- sle11sp4.orig/include/xen/gnttab.h	2012-10-19 15:09:28.000000000 +0200
+++ sle11sp4/include/xen/gnttab.h	2012-04-05 11:24:27.000000000 +0200
@@ -71,6 +71,8 @@ int gnttab_end_foreign_access_ref(grant_
  * some time later.  page may be 0, in which case no freeing will occur.
  */
 void gnttab_end_foreign_access(grant_ref_t ref, unsigned long page);
+void gnttab_multi_end_foreign_access(unsigned int nr, grant_ref_t [],
+				     struct page *[]);
 
 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn);
 
--- sle11sp4.orig/include/xen/xenbus.h	2011-04-13 15:43:04.000000000 +0200
+++ sle11sp4/include/xen/xenbus.h	2012-10-19 16:25:14.000000000 +0200
@@ -257,6 +257,9 @@ int xenbus_switch_state(struct xenbus_de
  */
 int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
 
+int xenbus_multi_grant_ring(struct xenbus_device *, unsigned int nr,
+			    struct page *[], grant_ref_t []);
+
 /**
  * Map a page of memory into this domain from another domain's grant table.
  * xenbus_map_ring_valloc allocates a page of virtual address space, maps the
@@ -266,7 +269,8 @@ int xenbus_grant_ring(struct xenbus_devi
  * XenbusStateClosing and the error message will be saved in XenStore.
  */
 struct vm_struct *xenbus_map_ring_valloc(struct xenbus_device *dev,
-					 grant_ref_t ref);
+					 const grant_ref_t refs[],
+					 unsigned int nr_refs);
 int xenbus_map_ring(struct xenbus_device *dev, grant_ref_t gnt_ref,
 			   grant_handle_t *handle, void *vaddr);
 
