aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netback/interface.c
diff options
context:
space:
mode:
authorIan Campbell <Ian.Campbell@citrix.com>2011-03-14 20:06:18 -0400
committerDavid S. Miller <davem@davemloft.net>2011-03-15 22:38:03 -0400
commitf942dc2552b8bfdee607be867b12a8971bb9cd85 (patch)
treeba7d264f94d9e6938ef4e36f93e179162e12cf20 /drivers/net/xen-netback/interface.c
parente0da2481fc00e031c04480b9dc88fae9eff39a19 (diff)
xen network backend driver
netback is the host side counterpart to the frontend driver in drivers/net/xen-netfront.c. The PV protocol is also implemented by frontend drivers in other OSes too, such as the BSDs and even Windows. The patch is based on the driver from the xen.git pvops kernel tree but has been put through the checkpatch.pl wringer plus several manual cleanup passes and review iterations. The driver has been moved from drivers/xen/netback to drivers/net/xen-netback. One major change from xen.git is that the guest transmit path (i.e. what looks like receive to netback) has been significantly reworked to remove the dependency on the out of tree PageForeign page flag (a core kernel patch which enables a per page destructor callback on the final put_page). This page flag was used in order to implement a grant map based transmit path (where guest pages are mapped directly into SKB frags). Instead this version of netback uses grant copy operations into regular memory belonging to the backend domain. Reinstating the grant map functionality is something which I would like to revisit in the future. Note that this driver depends on 2e820f58f7ad "xen/irq: implement bind_interdomain_evtchn_to_irqhandler for backend drivers" which is in linux next via the "xen-two" tree and is intended for the 2.6.39 merge window: git://git.kernel.org/pub/scm/linux/kernel/git/konrad/xen.git stable/backends this branch has only that single commit since 2.6.38-rc2 and is safe for cross merging into the net branch. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Reviewed-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/xen-netback/interface.c')
-rw-r--r--drivers/net/xen-netback/interface.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
new file mode 100644
index 000000000000..de569cc19da4
--- /dev/null
+++ b/drivers/net/xen-netback/interface.c
@@ -0,0 +1,424 @@
1/*
2 * Network-device interface management.
3 *
4 * Copyright (c) 2004-2005, Keir Fraser
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation; or, when distributed
9 * separately from the Linux kernel or incorporated into other
10 * software packages, subject to the following license:
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this source file (the "Software"), to deal in the Software without
14 * restriction, including without limitation the rights to use, copy, modify,
15 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
16 * and to permit persons to whom the Software is furnished to do so, subject to
17 * the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28 * IN THE SOFTWARE.
29 */
30
31#include "common.h"
32
33#include <linux/ethtool.h>
34#include <linux/rtnetlink.h>
35#include <linux/if_vlan.h>
36
37#include <xen/events.h>
38#include <asm/xen/hypercall.h>
39
40#define XENVIF_QUEUE_LENGTH 32
41
42void xenvif_get(struct xenvif *vif)
43{
44 atomic_inc(&vif->refcnt);
45}
46
47void xenvif_put(struct xenvif *vif)
48{
49 if (atomic_dec_and_test(&vif->refcnt))
50 wake_up(&vif->waiting_to_free);
51}
52
53int xenvif_schedulable(struct xenvif *vif)
54{
55 return netif_running(vif->dev) && netif_carrier_ok(vif->dev);
56}
57
58static int xenvif_rx_schedulable(struct xenvif *vif)
59{
60 return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif);
61}
62
63static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
64{
65 struct xenvif *vif = dev_id;
66
67 if (vif->netbk == NULL)
68 return IRQ_NONE;
69
70 xen_netbk_schedule_xenvif(vif);
71
72 if (xenvif_rx_schedulable(vif))
73 netif_wake_queue(vif->dev);
74
75 return IRQ_HANDLED;
76}
77
78static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
79{
80 struct xenvif *vif = netdev_priv(dev);
81
82 BUG_ON(skb->dev != dev);
83
84 if (vif->netbk == NULL)
85 goto drop;
86
87 /* Drop the packet if the target domain has no receive buffers. */
88 if (!xenvif_rx_schedulable(vif))
89 goto drop;
90
91 /* Reserve ring slots for the worst-case number of fragments. */
92 vif->rx_req_cons_peek += xen_netbk_count_skb_slots(vif, skb);
93 xenvif_get(vif);
94
95 if (vif->can_queue && xen_netbk_must_stop_queue(vif))
96 netif_stop_queue(dev);
97
98 xen_netbk_queue_tx_skb(vif, skb);
99
100 return NETDEV_TX_OK;
101
102 drop:
103 vif->dev->stats.tx_dropped++;
104 dev_kfree_skb(skb);
105 return NETDEV_TX_OK;
106}
107
108void xenvif_receive_skb(struct xenvif *vif, struct sk_buff *skb)
109{
110 netif_rx_ni(skb);
111}
112
113void xenvif_notify_tx_completion(struct xenvif *vif)
114{
115 if (netif_queue_stopped(vif->dev) && xenvif_rx_schedulable(vif))
116 netif_wake_queue(vif->dev);
117}
118
119static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
120{
121 struct xenvif *vif = netdev_priv(dev);
122 return &vif->dev->stats;
123}
124
125static void xenvif_up(struct xenvif *vif)
126{
127 xen_netbk_add_xenvif(vif);
128 enable_irq(vif->irq);
129 xen_netbk_check_rx_xenvif(vif);
130}
131
132static void xenvif_down(struct xenvif *vif)
133{
134 disable_irq(vif->irq);
135 xen_netbk_deschedule_xenvif(vif);
136 xen_netbk_remove_xenvif(vif);
137}
138
139static int xenvif_open(struct net_device *dev)
140{
141 struct xenvif *vif = netdev_priv(dev);
142 if (netif_carrier_ok(dev))
143 xenvif_up(vif);
144 netif_start_queue(dev);
145 return 0;
146}
147
148static int xenvif_close(struct net_device *dev)
149{
150 struct xenvif *vif = netdev_priv(dev);
151 if (netif_carrier_ok(dev))
152 xenvif_down(vif);
153 netif_stop_queue(dev);
154 return 0;
155}
156
157static int xenvif_change_mtu(struct net_device *dev, int mtu)
158{
159 struct xenvif *vif = netdev_priv(dev);
160 int max = vif->can_sg ? 65535 - VLAN_ETH_HLEN : ETH_DATA_LEN;
161
162 if (mtu > max)
163 return -EINVAL;
164 dev->mtu = mtu;
165 return 0;
166}
167
168static void xenvif_set_features(struct xenvif *vif)
169{
170 struct net_device *dev = vif->dev;
171 u32 features = dev->features;
172
173 if (vif->can_sg)
174 features |= NETIF_F_SG;
175 if (vif->gso || vif->gso_prefix)
176 features |= NETIF_F_TSO;
177 if (vif->csum)
178 features |= NETIF_F_IP_CSUM;
179
180 features &= ~(vif->features_disabled);
181
182 if (!(features & NETIF_F_SG) && dev->mtu > ETH_DATA_LEN)
183 dev->mtu = ETH_DATA_LEN;
184
185 dev->features = features;
186}
187
188static int xenvif_set_tx_csum(struct net_device *dev, u32 data)
189{
190 struct xenvif *vif = netdev_priv(dev);
191 if (data) {
192 if (!vif->csum)
193 return -EOPNOTSUPP;
194 vif->features_disabled &= ~NETIF_F_IP_CSUM;
195 } else {
196 vif->features_disabled |= NETIF_F_IP_CSUM;
197 }
198
199 xenvif_set_features(vif);
200 return 0;
201}
202
203static int xenvif_set_sg(struct net_device *dev, u32 data)
204{
205 struct xenvif *vif = netdev_priv(dev);
206 if (data) {
207 if (!vif->can_sg)
208 return -EOPNOTSUPP;
209 vif->features_disabled &= ~NETIF_F_SG;
210 } else {
211 vif->features_disabled |= NETIF_F_SG;
212 }
213
214 xenvif_set_features(vif);
215 return 0;
216}
217
218static int xenvif_set_tso(struct net_device *dev, u32 data)
219{
220 struct xenvif *vif = netdev_priv(dev);
221 if (data) {
222 if (!vif->gso && !vif->gso_prefix)
223 return -EOPNOTSUPP;
224 vif->features_disabled &= ~NETIF_F_TSO;
225 } else {
226 vif->features_disabled |= NETIF_F_TSO;
227 }
228
229 xenvif_set_features(vif);
230 return 0;
231}
232
233static const struct xenvif_stat {
234 char name[ETH_GSTRING_LEN];
235 u16 offset;
236} xenvif_stats[] = {
237 {
238 "rx_gso_checksum_fixup",
239 offsetof(struct xenvif, rx_gso_checksum_fixup)
240 },
241};
242
243static int xenvif_get_sset_count(struct net_device *dev, int string_set)
244{
245 switch (string_set) {
246 case ETH_SS_STATS:
247 return ARRAY_SIZE(xenvif_stats);
248 default:
249 return -EINVAL;
250 }
251}
252
253static void xenvif_get_ethtool_stats(struct net_device *dev,
254 struct ethtool_stats *stats, u64 * data)
255{
256 void *vif = netdev_priv(dev);
257 int i;
258
259 for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
260 data[i] = *(unsigned long *)(vif + xenvif_stats[i].offset);
261}
262
263static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
264{
265 int i;
266
267 switch (stringset) {
268 case ETH_SS_STATS:
269 for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
270 memcpy(data + i * ETH_GSTRING_LEN,
271 xenvif_stats[i].name, ETH_GSTRING_LEN);
272 break;
273 }
274}
275
276static struct ethtool_ops xenvif_ethtool_ops = {
277 .get_tx_csum = ethtool_op_get_tx_csum,
278 .set_tx_csum = xenvif_set_tx_csum,
279 .get_sg = ethtool_op_get_sg,
280 .set_sg = xenvif_set_sg,
281 .get_tso = ethtool_op_get_tso,
282 .set_tso = xenvif_set_tso,
283 .get_link = ethtool_op_get_link,
284
285 .get_sset_count = xenvif_get_sset_count,
286 .get_ethtool_stats = xenvif_get_ethtool_stats,
287 .get_strings = xenvif_get_strings,
288};
289
290static struct net_device_ops xenvif_netdev_ops = {
291 .ndo_start_xmit = xenvif_start_xmit,
292 .ndo_get_stats = xenvif_get_stats,
293 .ndo_open = xenvif_open,
294 .ndo_stop = xenvif_close,
295 .ndo_change_mtu = xenvif_change_mtu,
296};
297
298struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
299 unsigned int handle)
300{
301 int err;
302 struct net_device *dev;
303 struct xenvif *vif;
304 char name[IFNAMSIZ] = {};
305
306 snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
307 dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup);
308 if (dev == NULL) {
309 pr_warn("Could not allocate netdev\n");
310 return ERR_PTR(-ENOMEM);
311 }
312
313 SET_NETDEV_DEV(dev, parent);
314
315 vif = netdev_priv(dev);
316 vif->domid = domid;
317 vif->handle = handle;
318 vif->netbk = NULL;
319 vif->can_sg = 1;
320 vif->csum = 1;
321 atomic_set(&vif->refcnt, 1);
322 init_waitqueue_head(&vif->waiting_to_free);
323 vif->dev = dev;
324 INIT_LIST_HEAD(&vif->schedule_list);
325 INIT_LIST_HEAD(&vif->notify_list);
326
327 vif->credit_bytes = vif->remaining_credit = ~0UL;
328 vif->credit_usec = 0UL;
329 init_timer(&vif->credit_timeout);
330 /* Initialize 'expires' now: it's used to track the credit window. */
331 vif->credit_timeout.expires = jiffies;
332
333 dev->netdev_ops = &xenvif_netdev_ops;
334 xenvif_set_features(vif);
335 SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops);
336
337 dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
338
339 /*
340 * Initialise a dummy MAC address. We choose the numerically
341 * largest non-broadcast address to prevent the address getting
342 * stolen by an Ethernet bridge for STP purposes.
343 * (FE:FF:FF:FF:FF:FF)
344 */
345 memset(dev->dev_addr, 0xFF, ETH_ALEN);
346 dev->dev_addr[0] &= ~0x01;
347
348 netif_carrier_off(dev);
349
350 err = register_netdev(dev);
351 if (err) {
352 netdev_warn(dev, "Could not register device: err=%d\n", err);
353 free_netdev(dev);
354 return ERR_PTR(err);
355 }
356
357 netdev_dbg(dev, "Successfully created xenvif\n");
358 return vif;
359}
360
361int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
362 unsigned long rx_ring_ref, unsigned int evtchn)
363{
364 int err = -ENOMEM;
365
366 /* Already connected through? */
367 if (vif->irq)
368 return 0;
369
370 xenvif_set_features(vif);
371
372 err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
373 if (err < 0)
374 goto err;
375
376 err = bind_interdomain_evtchn_to_irqhandler(
377 vif->domid, evtchn, xenvif_interrupt, 0,
378 vif->dev->name, vif);
379 if (err < 0)
380 goto err_unmap;
381 vif->irq = err;
382 disable_irq(vif->irq);
383
384 xenvif_get(vif);
385
386 rtnl_lock();
387 netif_carrier_on(vif->dev);
388 if (netif_running(vif->dev))
389 xenvif_up(vif);
390 rtnl_unlock();
391
392 return 0;
393err_unmap:
394 xen_netbk_unmap_frontend_rings(vif);
395err:
396 return err;
397}
398
399void xenvif_disconnect(struct xenvif *vif)
400{
401 struct net_device *dev = vif->dev;
402 if (netif_carrier_ok(dev)) {
403 rtnl_lock();
404 netif_carrier_off(dev); /* discard queued packets */
405 if (netif_running(dev))
406 xenvif_down(vif);
407 rtnl_unlock();
408 xenvif_put(vif);
409 }
410
411 atomic_dec(&vif->refcnt);
412 wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0);
413
414 del_timer_sync(&vif->credit_timeout);
415
416 if (vif->irq)
417 unbind_from_irqhandler(vif->irq, vif);
418
419 unregister_netdev(vif->dev);
420
421 xen_netbk_unmap_frontend_rings(vif);
422
423 free_netdev(vif->dev);
424}