diff options
author | Zoltan Kiss <zoltan.kiss@citrix.com> | 2014-07-08 14:49:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-08 23:48:36 -0400 |
commit | f51de24356e49e4dcb5095e87717065580912120 (patch) | |
tree | 33439ff1f49a3d2d61455f0c6585b3369088b25c /drivers/net/xen-netback | |
parent | a37934fc0d0c087dd120dba229077048f1abfd37 (diff) |
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>
Diffstat (limited to 'drivers/net/xen-netback')
-rw-r--r-- | drivers/net/xen-netback/common.h | 11 | ||||
-rw-r--r-- | drivers/net/xen-netback/interface.c | 2 | ||||
-rw-r--r-- | drivers/net/xen-netback/netback.c | 11 | ||||
-rw-r--r-- | drivers/net/xen-netback/xenbus.c | 178 |
4 files changed, 200 insertions, 2 deletions
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 2532ce85d718..28c98229e95f 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <xen/interface/grant_table.h> | 44 | #include <xen/interface/grant_table.h> |
45 | #include <xen/grant_table.h> | 45 | #include <xen/grant_table.h> |
46 | #include <xen/xenbus.h> | 46 | #include <xen/xenbus.h> |
47 | #include <linux/debugfs.h> | ||
47 | 48 | ||
48 | typedef unsigned int pending_ring_idx_t; | 49 | typedef unsigned int pending_ring_idx_t; |
49 | #define INVALID_PENDING_RING_IDX (~0U) | 50 | #define INVALID_PENDING_RING_IDX (~0U) |
@@ -224,6 +225,10 @@ struct xenvif { | |||
224 | struct xenvif_queue *queues; | 225 | struct xenvif_queue *queues; |
225 | unsigned int num_queues; /* active queues, resource allocated */ | 226 | unsigned int num_queues; /* active queues, resource allocated */ |
226 | 227 | ||
228 | #ifdef CONFIG_DEBUG_FS | ||
229 | struct dentry *xenvif_dbg_root; | ||
230 | #endif | ||
231 | |||
227 | /* Miscellaneous private stuff. */ | 232 | /* Miscellaneous private stuff. */ |
228 | struct net_device *dev; | 233 | struct net_device *dev; |
229 | }; | 234 | }; |
@@ -297,10 +302,16 @@ static inline pending_ring_idx_t nr_pending_reqs(struct xenvif_queue *queue) | |||
297 | /* Callback from stack when TX packet can be released */ | 302 | /* Callback from stack when TX packet can be released */ |
298 | void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success); | 303 | void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success); |
299 | 304 | ||
305 | irqreturn_t xenvif_interrupt(int irq, void *dev_id); | ||
306 | |||
300 | extern bool separate_tx_rx_irq; | 307 | extern bool separate_tx_rx_irq; |
301 | 308 | ||
302 | extern unsigned int rx_drain_timeout_msecs; | 309 | extern unsigned int rx_drain_timeout_msecs; |
303 | extern unsigned int rx_drain_timeout_jiffies; | 310 | extern unsigned int rx_drain_timeout_jiffies; |
304 | extern unsigned int xenvif_max_queues; | 311 | extern unsigned int xenvif_max_queues; |
305 | 312 | ||
313 | #ifdef CONFIG_DEBUG_FS | ||
314 | extern struct dentry *xen_netback_dbg_root; | ||
315 | #endif | ||
316 | |||
306 | #endif /* __XEN_NETBACK__COMMON_H__ */ | 317 | #endif /* __XEN_NETBACK__COMMON_H__ */ |
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 9e97c7ca0ddd..ef75b45e5085 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c | |||
@@ -102,7 +102,7 @@ static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id) | |||
102 | return IRQ_HANDLED; | 102 | return IRQ_HANDLED; |
103 | } | 103 | } |
104 | 104 | ||
105 | static irqreturn_t xenvif_interrupt(int irq, void *dev_id) | 105 | irqreturn_t xenvif_interrupt(int irq, void *dev_id) |
106 | { | 106 | { |
107 | xenvif_tx_interrupt(irq, dev_id); | 107 | xenvif_tx_interrupt(irq, dev_id); |
108 | xenvif_rx_interrupt(irq, dev_id); | 108 | xenvif_rx_interrupt(irq, dev_id); |
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 1844a47636b6..77127ca08ca4 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c | |||
@@ -1987,6 +1987,13 @@ static int __init netback_init(void) | |||
1987 | 1987 | ||
1988 | rx_drain_timeout_jiffies = msecs_to_jiffies(rx_drain_timeout_msecs); | 1988 | rx_drain_timeout_jiffies = msecs_to_jiffies(rx_drain_timeout_msecs); |
1989 | 1989 | ||
1990 | #ifdef CONFIG_DEBUG_FS | ||
1991 | xen_netback_dbg_root = debugfs_create_dir("xen-netback", NULL); | ||
1992 | if (IS_ERR_OR_NULL(xen_netback_dbg_root)) | ||
1993 | pr_warn("Init of debugfs returned %ld!\n", | ||
1994 | PTR_ERR(xen_netback_dbg_root)); | ||
1995 | #endif /* CONFIG_DEBUG_FS */ | ||
1996 | |||
1990 | return 0; | 1997 | return 0; |
1991 | 1998 | ||
1992 | failed_init: | 1999 | failed_init: |
@@ -1997,6 +2004,10 @@ module_init(netback_init); | |||
1997 | 2004 | ||
1998 | static void __exit netback_fini(void) | 2005 | static void __exit netback_fini(void) |
1999 | { | 2006 | { |
2007 | #ifdef CONFIG_DEBUG_FS | ||
2008 | if (!IS_ERR_OR_NULL(xen_netback_dbg_root)) | ||
2009 | debugfs_remove_recursive(xen_netback_dbg_root); | ||
2010 | #endif /* CONFIG_DEBUG_FS */ | ||
2000 | xenvif_xenbus_fini(); | 2011 | xenvif_xenbus_fini(); |
2001 | } | 2012 | } |
2002 | module_exit(netback_fini); | 2013 | module_exit(netback_fini); |
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 3d85acd84bad..580517d857bf 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c | |||
@@ -44,6 +44,175 @@ static void unregister_hotplug_status_watch(struct backend_info *be); | |||
44 | static void set_backend_state(struct backend_info *be, | 44 | static void set_backend_state(struct backend_info *be, |
45 | enum xenbus_state state); | 45 | enum xenbus_state state); |
46 | 46 | ||
47 | #ifdef CONFIG_DEBUG_FS | ||
48 | struct dentry *xen_netback_dbg_root = NULL; | ||
49 | |||
50 | static int xenvif_read_io_ring(struct seq_file *m, void *v) | ||
51 | { | ||
52 | struct xenvif_queue *queue = m->private; | ||
53 | struct xen_netif_tx_back_ring *tx_ring = &queue->tx; | ||
54 | struct xen_netif_rx_back_ring *rx_ring = &queue->rx; | ||
55 | |||
56 | if (tx_ring->sring) { | ||
57 | struct xen_netif_tx_sring *sring = tx_ring->sring; | ||
58 | |||
59 | seq_printf(m, "Queue %d\nTX: nr_ents %u\n", queue->id, | ||
60 | tx_ring->nr_ents); | ||
61 | seq_printf(m, "req prod %u (%d) cons %u (%d) event %u (%d)\n", | ||
62 | sring->req_prod, | ||
63 | sring->req_prod - sring->rsp_prod, | ||
64 | tx_ring->req_cons, | ||
65 | tx_ring->req_cons - sring->rsp_prod, | ||
66 | sring->req_event, | ||
67 | sring->req_event - sring->rsp_prod); | ||
68 | seq_printf(m, "rsp prod %u (base) pvt %u (%d) event %u (%d)\n", | ||
69 | sring->rsp_prod, | ||
70 | tx_ring->rsp_prod_pvt, | ||
71 | tx_ring->rsp_prod_pvt - sring->rsp_prod, | ||
72 | sring->rsp_event, | ||
73 | sring->rsp_event - sring->rsp_prod); | ||
74 | seq_printf(m, "pending prod %u pending cons %u nr_pending_reqs %u\n", | ||
75 | queue->pending_prod, | ||
76 | queue->pending_cons, | ||
77 | nr_pending_reqs(queue)); | ||
78 | seq_printf(m, "dealloc prod %u dealloc cons %u dealloc_queue %u\n\n", | ||
79 | queue->dealloc_prod, | ||
80 | queue->dealloc_cons, | ||
81 | queue->dealloc_prod - queue->dealloc_cons); | ||
82 | } | ||
83 | |||
84 | if (rx_ring->sring) { | ||
85 | struct xen_netif_rx_sring *sring = rx_ring->sring; | ||
86 | |||
87 | seq_printf(m, "RX: nr_ents %u\n", rx_ring->nr_ents); | ||
88 | seq_printf(m, "req prod %u (%d) cons %u (%d) event %u (%d)\n", | ||
89 | sring->req_prod, | ||
90 | sring->req_prod - sring->rsp_prod, | ||
91 | rx_ring->req_cons, | ||
92 | rx_ring->req_cons - sring->rsp_prod, | ||
93 | sring->req_event, | ||
94 | sring->req_event - sring->rsp_prod); | ||
95 | seq_printf(m, "rsp prod %u (base) pvt %u (%d) event %u (%d)\n\n", | ||
96 | sring->rsp_prod, | ||
97 | rx_ring->rsp_prod_pvt, | ||
98 | rx_ring->rsp_prod_pvt - sring->rsp_prod, | ||
99 | sring->rsp_event, | ||
100 | sring->rsp_event - sring->rsp_prod); | ||
101 | } | ||
102 | |||
103 | seq_printf(m, "NAPI state: %lx NAPI weight: %d TX queue len %u\n" | ||
104 | "Credit timer_pending: %d, credit: %lu, usec: %lu\n" | ||
105 | "remaining: %lu, expires: %lu, now: %lu\n", | ||
106 | queue->napi.state, queue->napi.weight, | ||
107 | skb_queue_len(&queue->tx_queue), | ||
108 | timer_pending(&queue->credit_timeout), | ||
109 | queue->credit_bytes, | ||
110 | queue->credit_usec, | ||
111 | queue->remaining_credit, | ||
112 | queue->credit_timeout.expires, | ||
113 | jiffies); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | #define XENVIF_KICK_STR "kick" | ||
119 | |||
120 | static ssize_t | ||
121 | xenvif_write_io_ring(struct file *filp, const char __user *buf, size_t count, | ||
122 | loff_t *ppos) | ||
123 | { | ||
124 | struct xenvif_queue *queue = | ||
125 | ((struct seq_file *)filp->private_data)->private; | ||
126 | int len; | ||
127 | char write[sizeof(XENVIF_KICK_STR)]; | ||
128 | |||
129 | /* don't allow partial writes and check the length */ | ||
130 | if (*ppos != 0) | ||
131 | return 0; | ||
132 | if (count < sizeof(XENVIF_KICK_STR) - 1) | ||
133 | return -ENOSPC; | ||
134 | |||
135 | len = simple_write_to_buffer(write, | ||
136 | sizeof(write), | ||
137 | ppos, | ||
138 | buf, | ||
139 | count); | ||
140 | if (len < 0) | ||
141 | return len; | ||
142 | |||
143 | if (!strncmp(write, XENVIF_KICK_STR, sizeof(XENVIF_KICK_STR) - 1)) | ||
144 | xenvif_interrupt(0, (void *)queue); | ||
145 | else { | ||
146 | pr_warn("Unknown command to io_ring_q%d. Available: kick\n", | ||
147 | queue->id); | ||
148 | count = -EINVAL; | ||
149 | } | ||
150 | return count; | ||
151 | } | ||
152 | |||
153 | static int xenvif_dump_open(struct inode *inode, struct file *filp) | ||
154 | { | ||
155 | int ret; | ||
156 | void *queue = NULL; | ||
157 | |||
158 | if (inode->i_private) | ||
159 | queue = inode->i_private; | ||
160 | ret = single_open(filp, xenvif_read_io_ring, queue); | ||
161 | filp->f_mode |= FMODE_PWRITE; | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | static const struct file_operations xenvif_dbg_io_ring_ops_fops = { | ||
166 | .owner = THIS_MODULE, | ||
167 | .open = xenvif_dump_open, | ||
168 | .read = seq_read, | ||
169 | .llseek = seq_lseek, | ||
170 | .release = single_release, | ||
171 | .write = xenvif_write_io_ring, | ||
172 | }; | ||
173 | |||
174 | static void xenvif_debugfs_addif(struct xenvif_queue *queue) | ||
175 | { | ||
176 | struct dentry *pfile; | ||
177 | struct xenvif *vif = queue->vif; | ||
178 | int i; | ||
179 | |||
180 | if (IS_ERR_OR_NULL(xen_netback_dbg_root)) | ||
181 | return; | ||
182 | |||
183 | vif->xenvif_dbg_root = debugfs_create_dir(vif->dev->name, | ||
184 | xen_netback_dbg_root); | ||
185 | if (!IS_ERR_OR_NULL(vif->xenvif_dbg_root)) { | ||
186 | for (i = 0; i < vif->num_queues; ++i) { | ||
187 | char filename[sizeof("io_ring_q") + 4]; | ||
188 | |||
189 | snprintf(filename, sizeof(filename), "io_ring_q%d", i); | ||
190 | pfile = debugfs_create_file(filename, | ||
191 | S_IRUSR | S_IWUSR, | ||
192 | vif->xenvif_dbg_root, | ||
193 | &vif->queues[i], | ||
194 | &xenvif_dbg_io_ring_ops_fops); | ||
195 | if (IS_ERR_OR_NULL(pfile)) | ||
196 | pr_warn("Creation of io_ring file returned %ld!\n", | ||
197 | PTR_ERR(pfile)); | ||
198 | } | ||
199 | } else | ||
200 | netdev_warn(vif->dev, | ||
201 | "Creation of vif debugfs dir returned %ld!\n", | ||
202 | PTR_ERR(vif->xenvif_dbg_root)); | ||
203 | } | ||
204 | |||
205 | static void xenvif_debugfs_delif(struct xenvif *vif) | ||
206 | { | ||
207 | if (IS_ERR_OR_NULL(xen_netback_dbg_root)) | ||
208 | return; | ||
209 | |||
210 | if (!IS_ERR_OR_NULL(vif->xenvif_dbg_root)) | ||
211 | debugfs_remove_recursive(vif->xenvif_dbg_root); | ||
212 | vif->xenvif_dbg_root = NULL; | ||
213 | } | ||
214 | #endif /* CONFIG_DEBUG_FS */ | ||
215 | |||
47 | static int netback_remove(struct xenbus_device *dev) | 216 | static int netback_remove(struct xenbus_device *dev) |
48 | { | 217 | { |
49 | struct backend_info *be = dev_get_drvdata(&dev->dev); | 218 | struct backend_info *be = dev_get_drvdata(&dev->dev); |
@@ -246,8 +415,12 @@ static void backend_create_xenvif(struct backend_info *be) | |||
246 | 415 | ||
247 | static void backend_disconnect(struct backend_info *be) | 416 | static void backend_disconnect(struct backend_info *be) |
248 | { | 417 | { |
249 | if (be->vif) | 418 | if (be->vif) { |
419 | #ifdef CONFIG_DEBUG_FS | ||
420 | xenvif_debugfs_delif(be->vif); | ||
421 | #endif /* CONFIG_DEBUG_FS */ | ||
250 | xenvif_disconnect(be->vif); | 422 | xenvif_disconnect(be->vif); |
423 | } | ||
251 | } | 424 | } |
252 | 425 | ||
253 | static void backend_connect(struct backend_info *be) | 426 | static void backend_connect(struct backend_info *be) |
@@ -560,6 +733,9 @@ static void connect(struct backend_info *be) | |||
560 | be->vif->num_queues = queue_index; | 733 | be->vif->num_queues = queue_index; |
561 | goto err; | 734 | goto err; |
562 | } | 735 | } |
736 | #ifdef CONFIG_DEBUG_FS | ||
737 | xenvif_debugfs_addif(queue); | ||
738 | #endif /* CONFIG_DEBUG_FS */ | ||
563 | } | 739 | } |
564 | 740 | ||
565 | /* Initialisation completed, tell core driver the number of | 741 | /* Initialisation completed, tell core driver the number of |