xen-netback: Adding debugfs "io_ring_qX" files

This patch adds debugfs capabilities to netback. There used to be a similar
patch floating around for classic kernel, but it used procfs. It is based on a
very similar blkback patch.
It creates xen-netback/[vifname]/io_ring_q[queueno] files, reading them output
various ring variables etc. Writing "kick" into it imitates an interrupt
happened, it can be useful to check whether the ring is just stalled due to a
missed interrupt.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 3d85acd..580517d 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -44,6 +44,175 @@
 static void set_backend_state(struct backend_info *be,
 			      enum xenbus_state state);
 
+#ifdef CONFIG_DEBUG_FS
+struct dentry *xen_netback_dbg_root = NULL;
+
+static int xenvif_read_io_ring(struct seq_file *m, void *v)
+{
+	struct xenvif_queue *queue = m->private;
+	struct xen_netif_tx_back_ring *tx_ring = &queue->tx;
+	struct xen_netif_rx_back_ring *rx_ring = &queue->rx;
+
+	if (tx_ring->sring) {
+		struct xen_netif_tx_sring *sring = tx_ring->sring;
+
+		seq_printf(m, "Queue %d\nTX: nr_ents %u\n", queue->id,
+			   tx_ring->nr_ents);
+		seq_printf(m, "req prod %u (%d) cons %u (%d) event %u (%d)\n",
+			   sring->req_prod,
+			   sring->req_prod - sring->rsp_prod,
+			   tx_ring->req_cons,
+			   tx_ring->req_cons - sring->rsp_prod,
+			   sring->req_event,
+			   sring->req_event - sring->rsp_prod);
+		seq_printf(m, "rsp prod %u (base) pvt %u (%d) event %u (%d)\n",
+			   sring->rsp_prod,
+			   tx_ring->rsp_prod_pvt,
+			   tx_ring->rsp_prod_pvt - sring->rsp_prod,
+			   sring->rsp_event,
+			   sring->rsp_event - sring->rsp_prod);
+		seq_printf(m, "pending prod %u pending cons %u nr_pending_reqs %u\n",
+			   queue->pending_prod,
+			   queue->pending_cons,
+			   nr_pending_reqs(queue));
+		seq_printf(m, "dealloc prod %u dealloc cons %u dealloc_queue %u\n\n",
+			   queue->dealloc_prod,
+			   queue->dealloc_cons,
+			   queue->dealloc_prod - queue->dealloc_cons);
+	}
+
+	if (rx_ring->sring) {
+		struct xen_netif_rx_sring *sring = rx_ring->sring;
+
+		seq_printf(m, "RX: nr_ents %u\n", rx_ring->nr_ents);
+		seq_printf(m, "req prod %u (%d) cons %u (%d) event %u (%d)\n",
+			   sring->req_prod,
+			   sring->req_prod - sring->rsp_prod,
+			   rx_ring->req_cons,
+			   rx_ring->req_cons - sring->rsp_prod,
+			   sring->req_event,
+			   sring->req_event - sring->rsp_prod);
+		seq_printf(m, "rsp prod %u (base) pvt %u (%d) event %u (%d)\n\n",
+			   sring->rsp_prod,
+			   rx_ring->rsp_prod_pvt,
+			   rx_ring->rsp_prod_pvt - sring->rsp_prod,
+			   sring->rsp_event,
+			   sring->rsp_event - sring->rsp_prod);
+	}
+
+	seq_printf(m, "NAPI state: %lx NAPI weight: %d TX queue len %u\n"
+		   "Credit timer_pending: %d, credit: %lu, usec: %lu\n"
+		   "remaining: %lu, expires: %lu, now: %lu\n",
+		   queue->napi.state, queue->napi.weight,
+		   skb_queue_len(&queue->tx_queue),
+		   timer_pending(&queue->credit_timeout),
+		   queue->credit_bytes,
+		   queue->credit_usec,
+		   queue->remaining_credit,
+		   queue->credit_timeout.expires,
+		   jiffies);
+
+	return 0;
+}
+
+#define XENVIF_KICK_STR "kick"
+
+static ssize_t
+xenvif_write_io_ring(struct file *filp, const char __user *buf, size_t count,
+		     loff_t *ppos)
+{
+	struct xenvif_queue *queue =
+		((struct seq_file *)filp->private_data)->private;
+	int len;
+	char write[sizeof(XENVIF_KICK_STR)];
+
+	/* don't allow partial writes and check the length */
+	if (*ppos != 0)
+		return 0;
+	if (count < sizeof(XENVIF_KICK_STR) - 1)
+		return -ENOSPC;
+
+	len = simple_write_to_buffer(write,
+				     sizeof(write),
+				     ppos,
+				     buf,
+				     count);
+	if (len < 0)
+		return len;
+
+	if (!strncmp(write, XENVIF_KICK_STR, sizeof(XENVIF_KICK_STR) - 1))
+		xenvif_interrupt(0, (void *)queue);
+	else {
+		pr_warn("Unknown command to io_ring_q%d. Available: kick\n",
+			queue->id);
+		count = -EINVAL;
+	}
+	return count;
+}
+
+static int xenvif_dump_open(struct inode *inode, struct file *filp)
+{
+	int ret;
+	void *queue = NULL;
+
+	if (inode->i_private)
+		queue = inode->i_private;
+	ret = single_open(filp, xenvif_read_io_ring, queue);
+	filp->f_mode |= FMODE_PWRITE;
+	return ret;
+}
+
+static const struct file_operations xenvif_dbg_io_ring_ops_fops = {
+	.owner = THIS_MODULE,
+	.open = xenvif_dump_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = xenvif_write_io_ring,
+};
+
+static void xenvif_debugfs_addif(struct xenvif_queue *queue)
+{
+	struct dentry *pfile;
+	struct xenvif *vif = queue->vif;
+	int i;
+
+	if (IS_ERR_OR_NULL(xen_netback_dbg_root))
+		return;
+
+	vif->xenvif_dbg_root = debugfs_create_dir(vif->dev->name,
+						  xen_netback_dbg_root);
+	if (!IS_ERR_OR_NULL(vif->xenvif_dbg_root)) {
+		for (i = 0; i < vif->num_queues; ++i) {
+			char filename[sizeof("io_ring_q") + 4];
+
+			snprintf(filename, sizeof(filename), "io_ring_q%d", i);
+			pfile = debugfs_create_file(filename,
+						    S_IRUSR | S_IWUSR,
+						    vif->xenvif_dbg_root,
+						    &vif->queues[i],
+						    &xenvif_dbg_io_ring_ops_fops);
+			if (IS_ERR_OR_NULL(pfile))
+				pr_warn("Creation of io_ring file returned %ld!\n",
+					PTR_ERR(pfile));
+		}
+	} else
+		netdev_warn(vif->dev,
+			    "Creation of vif debugfs dir returned %ld!\n",
+			    PTR_ERR(vif->xenvif_dbg_root));
+}
+
+static void xenvif_debugfs_delif(struct xenvif *vif)
+{
+	if (IS_ERR_OR_NULL(xen_netback_dbg_root))
+		return;
+
+	if (!IS_ERR_OR_NULL(vif->xenvif_dbg_root))
+		debugfs_remove_recursive(vif->xenvif_dbg_root);
+	vif->xenvif_dbg_root = NULL;
+}
+#endif /* CONFIG_DEBUG_FS */
+
 static int netback_remove(struct xenbus_device *dev)
 {
 	struct backend_info *be = dev_get_drvdata(&dev->dev);
@@ -246,8 +415,12 @@
 
 static void backend_disconnect(struct backend_info *be)
 {
-	if (be->vif)
+	if (be->vif) {
+#ifdef CONFIG_DEBUG_FS
+		xenvif_debugfs_delif(be->vif);
+#endif /* CONFIG_DEBUG_FS */
 		xenvif_disconnect(be->vif);
+	}
 }
 
 static void backend_connect(struct backend_info *be)
@@ -560,6 +733,9 @@
 			be->vif->num_queues = queue_index;
 			goto err;
 		}
+#ifdef CONFIG_DEBUG_FS
+		xenvif_debugfs_addif(queue);
+#endif /* CONFIG_DEBUG_FS */
 	}
 
 	/* Initialisation completed, tell core driver the number of