From: Dongxiao Xu <dongxiao.xu@intel.com>
Subject: [PATCH 2/3] Netback: Multiple tasklets support.
Patch-mainline: n/a

  Now netback uses one pair of tasklets for Tx/Rx data transaction. Netback
  tasklet could only run at one CPU at a time, and it is used to serve all the
  netfronts. Therefore it has become a performance bottle neck. This patch is to
  use multiple tasklet pairs to replace the current single pair in dom0.

  Assuming that Dom0 has CPUNR VCPUs, we define CPUNR kinds of tasklets pair
  (CPUNR for Tx, and CPUNR for Rx). Each pare of tasklets serve specific group of
  netfronts. Also for those global and static variables, we duplicated them for
  each group in order to avoid the spinlock.

Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com>

jb: some cleanups
Acked-by: jbeulich@novell.com

--- head-2011-02-17.orig/drivers/xen/netback/common.h	2011-02-17 10:33:48.000000000 +0100
+++ head-2011-02-17/drivers/xen/netback/common.h	2011-02-09 16:21:50.000000000 +0100
@@ -55,6 +55,7 @@
 typedef struct netif_st {
 	/* Unique identifier for this interface. */
 	domid_t          domid;
+	unsigned int     group;
 	unsigned int     handle;
 
 	u8               fe_dev_addr[6];
@@ -262,6 +263,7 @@ struct xen_netbk {
 
 	struct page **mmap_pages;
 
+	atomic_t nr_groups;
 	unsigned int alloc_index;
 
 	struct pending_tx_info pending_tx_info[MAX_PENDING_REQS];
@@ -289,4 +291,8 @@ struct xen_netbk {
 
 	unsigned long mfn_list[MAX_MFN_ALLOC];
 };
+
+extern struct xen_netbk *xen_netbk;
+extern unsigned int netbk_nr_groups;
+
 #endif /* __NETIF__BACKEND__COMMON_H__ */
--- head-2011-02-17.orig/drivers/xen/netback/interface.c	2011-02-17 10:33:17.000000000 +0100
+++ head-2011-02-17/drivers/xen/netback/interface.c	2011-02-17 10:34:28.000000000 +0100
@@ -54,14 +54,36 @@ module_param_named(queue_length, netbk_q
 
 static void __netif_up(netif_t *netif)
 {
+	unsigned int group = 0;
+	unsigned int min_groups = atomic_read(&xen_netbk[0].nr_groups);
+	unsigned int i;
+
+	/* Find the list which contains least number of domains. */
+	for (i = 1; i < netbk_nr_groups; i++) {
+		unsigned int nr_groups = atomic_read(&xen_netbk[i].nr_groups);
+
+		if (nr_groups < min_groups) {
+			group = i;
+			min_groups = nr_groups;
+		}
+	}
+
+	atomic_inc(&xen_netbk[group].nr_groups);
+	netif->group = group;
+
 	enable_irq(netif->irq);
 	netif_schedule_work(netif);
 }
 
 static void __netif_down(netif_t *netif)
 {
+	struct xen_netbk *netbk = xen_netbk + netif->group;
+
 	disable_irq(netif->irq);
 	netif_deschedule_work(netif);
+
+	netif->group = UINT_MAX;
+	atomic_dec(&netbk->nr_groups);
 }
 
 static int net_open(struct net_device *dev)
@@ -250,6 +272,7 @@ netif_t *netif_alloc(struct device *pare
 	netif = netdev_priv(dev);
 	memset(netif, 0, sizeof(*netif));
 	netif->domid  = domid;
+	netif->group = UINT_MAX;
 	netif->handle = handle;
 	netif->can_sg = 1;
 	netif->csum = 1;
--- head-2011-02-17.orig/drivers/xen/netback/netback.c	2011-03-01 11:53:28.000000000 +0100
+++ head-2011-02-17/drivers/xen/netback/netback.c	2011-03-01 11:53:33.000000000 +0100
@@ -44,10 +44,10 @@
 
 /*define NETBE_DEBUG_INTERRUPT*/
 
-static struct xen_netbk *__read_mostly xen_netbk;
-static const unsigned int netbk_nr_groups = 1;
+struct xen_netbk *__read_mostly xen_netbk;
+unsigned int __read_mostly netbk_nr_groups;
 
-#define GET_GROUP_INDEX(netif) (0)
+#define GET_GROUP_INDEX(netif) ((netif)->group)
 
 static void netif_idx_release(struct xen_netbk *, u16 pending_idx);
 static void make_tx_response(netif_t *netif, 
@@ -136,6 +136,8 @@ MODULE_PARM_DESC(copy_skb, "Copy data re
 static int MODPARM_permute_returns = 0;
 module_param_named(permute_returns, MODPARM_permute_returns, bool, S_IRUSR|S_IWUSR);
 MODULE_PARM_DESC(permute_returns, "Randomly permute the order in which TX responses are sent to the frontend");
+module_param_named(groups, netbk_nr_groups, uint, 0);
+MODULE_PARM_DESC(groups, "Specify the number of tasklet pairs to use");
 
 int netbk_copy_skb_mode;
 
@@ -406,11 +408,13 @@ static u16 netbk_gop_frag(netif_t *netif
 		    (idx = netif_page_index(page)) < MAX_PENDING_REQS &&
 		    (group = netif_page_group(page)) < netbk_nr_groups) {
 			struct pending_tx_info *src_pend;
+			unsigned int grp;
 
 			netbk = &xen_netbk[group];
 			BUG_ON(netbk->mmap_pages[idx] != page);
 			src_pend = &netbk->pending_tx_info[idx];
-			BUG_ON(group != GET_GROUP_INDEX(src_pend->netif));
+			grp = GET_GROUP_INDEX(src_pend->netif);
+			BUG_ON(group != grp && grp != UINT_MAX);
 			copy_gop->source.domid = src_pend->netif->domid;
 			copy_gop->source.u.ref = src_pend->req.gref;
 			copy_gop->flags |= GNTCOPY_source_gref;
@@ -1558,9 +1562,20 @@ static void netif_page_release(struct pa
 irqreturn_t netif_be_int(int irq, void *dev_id)
 {
 	netif_t *netif = dev_id;
+	unsigned int group = GET_GROUP_INDEX(netif);
+
+	if (unlikely(group >= netbk_nr_groups)) {
+		/*
+		 * Short of having a way to bind the IRQ in disabled mode
+		 * (IRQ_NOAUTOEN), we have to ignore the first invocation(s)
+		 * (before we got assigned to a group).
+		 */
+		BUG_ON(group != UINT_MAX);
+		return IRQ_HANDLED;
+	}
 
 	add_to_net_schedule_list_tail(netif);
-	maybe_schedule_tx_action(GET_GROUP_INDEX(netif));
+	maybe_schedule_tx_action(group);
 
 	if (netif_schedulable(netif) && !netbk_queue_full(netif))
 		netif_wake_queue(netif->dev);
@@ -1677,13 +1692,24 @@ static int __init netback_init(void)
 	if (!is_running_on_xen())
 		return -ENODEV;
 
-	xen_netbk = __vmalloc(netbk_nr_groups * sizeof(*xen_netbk),
-			      GFP_KERNEL|__GFP_HIGHMEM|__GFP_ZERO,
-			      PAGE_KERNEL);
+	group = netbk_nr_groups;
+	if (!netbk_nr_groups)
+		netbk_nr_groups = (num_online_cpus() + 1) / 2;
+	if (netbk_nr_groups > MAX_GROUPS)
+		netbk_nr_groups = MAX_GROUPS;
+
+	do {
+		xen_netbk = __vmalloc(netbk_nr_groups * sizeof(*xen_netbk),
+				      GFP_KERNEL|__GFP_HIGHMEM|__GFP_ZERO,
+				      PAGE_KERNEL);
+	} while (!xen_netbk && (netbk_nr_groups >>= 1));
 	if (!xen_netbk) {
 		pr_err("%s: out of memory\n", __func__);
 		return -ENOMEM;
 	}
+	if (group && netbk_nr_groups != group)
+		pr_warning("netback: only using %u (instead of %u) groups\n",
+			   netbk_nr_groups, group);
 
 	/* We can increase reservation by this much in net_rx_action(). */
 	balloon_update_driver_allowance(netbk_nr_groups * NET_RX_RING_SIZE);
