diff options
author | Jon Mason <jon.mason@intel.com> | 2012-11-16 21:27:13 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-17 22:11:14 -0500 |
commit | 548c237c0a9972df5d1afaca38aa733ee577128d (patch) | |
tree | 4e528e54395066efeec4ddcfd83c308ef77af90f /drivers/net/ntb_netdev.c | |
parent | fce8a7bb5b4bfb8a27324703fd5b002ee9247e90 (diff) |
net: Add support for NTB virtual ethernet device
A virtual ethernet device that uses the NTB transport API to
send/receive data.
Signed-off-by: Jon Mason <jon.mason@intel.com>
Reviewed-by: Nicholas Bellinger <nab@linux-iscsi.org>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/net/ntb_netdev.c')
-rw-r--r-- | drivers/net/ntb_netdev.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c new file mode 100644 index 000000000000..af48a69cab02 --- /dev/null +++ b/drivers/net/ntb_netdev.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
3 | * redistributing this file, you may do so under either license. | ||
4 | * | ||
5 | * GPL LICENSE SUMMARY | ||
6 | * | ||
7 | * Copyright(c) 2012 Intel Corporation. All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of version 2 of the GNU General Public License as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * BSD LICENSE | ||
14 | * | ||
15 | * Copyright(c) 2012 Intel Corporation. All rights reserved. | ||
16 | * | ||
17 | * Redistribution and use in source and binary forms, with or without | ||
18 | * modification, are permitted provided that the following conditions | ||
19 | * are met: | ||
20 | * | ||
21 | * * Redistributions of source code must retain the above copyright | ||
22 | * notice, this list of conditions and the following disclaimer. | ||
23 | * * Redistributions in binary form must reproduce the above copy | ||
24 | * notice, this list of conditions and the following disclaimer in | ||
25 | * the documentation and/or other materials provided with the | ||
26 | * distribution. | ||
27 | * * Neither the name of Intel Corporation nor the names of its | ||
28 | * contributors may be used to endorse or promote products derived | ||
29 | * from this software without specific prior written permission. | ||
30 | * | ||
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
35 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
36 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
37 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
38 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
39 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
40 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
41 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
42 | * | ||
43 | * Intel PCIe NTB Network Linux driver | ||
44 | * | ||
45 | * Contact Information: | ||
46 | * Jon Mason <jon.mason@intel.com> | ||
47 | */ | ||
48 | #include <linux/etherdevice.h> | ||
49 | #include <linux/ethtool.h> | ||
50 | #include <linux/module.h> | ||
51 | #include <linux/pci.h> | ||
52 | #include <linux/ntb.h> | ||
53 | |||
54 | #define NTB_NETDEV_VER "0.6" | ||
55 | |||
56 | MODULE_DESCRIPTION(KBUILD_MODNAME); | ||
57 | MODULE_VERSION(NTB_NETDEV_VER); | ||
58 | MODULE_LICENSE("Dual BSD/GPL"); | ||
59 | MODULE_AUTHOR("Intel Corporation"); | ||
60 | |||
61 | struct ntb_netdev { | ||
62 | struct list_head list; | ||
63 | struct pci_dev *pdev; | ||
64 | struct net_device *ndev; | ||
65 | struct ntb_transport_qp *qp; | ||
66 | }; | ||
67 | |||
68 | #define NTB_TX_TIMEOUT_MS 1000 | ||
69 | #define NTB_RXQ_SIZE 100 | ||
70 | |||
71 | static LIST_HEAD(dev_list); | ||
72 | |||
73 | static void ntb_netdev_event_handler(void *data, int status) | ||
74 | { | ||
75 | struct net_device *ndev = data; | ||
76 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
77 | |||
78 | netdev_dbg(ndev, "Event %x, Link %x\n", status, | ||
79 | ntb_transport_link_query(dev->qp)); | ||
80 | |||
81 | /* Currently, only link status event is supported */ | ||
82 | if (status) | ||
83 | netif_carrier_on(ndev); | ||
84 | else | ||
85 | netif_carrier_off(ndev); | ||
86 | } | ||
87 | |||
88 | static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, | ||
89 | void *data, int len) | ||
90 | { | ||
91 | struct net_device *ndev = qp_data; | ||
92 | struct sk_buff *skb; | ||
93 | int rc; | ||
94 | |||
95 | skb = data; | ||
96 | if (!skb) | ||
97 | return; | ||
98 | |||
99 | netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len); | ||
100 | |||
101 | skb_put(skb, len); | ||
102 | skb->protocol = eth_type_trans(skb, ndev); | ||
103 | skb->ip_summed = CHECKSUM_NONE; | ||
104 | |||
105 | if (netif_rx(skb) == NET_RX_DROP) { | ||
106 | ndev->stats.rx_errors++; | ||
107 | ndev->stats.rx_dropped++; | ||
108 | } else { | ||
109 | ndev->stats.rx_packets++; | ||
110 | ndev->stats.rx_bytes += len; | ||
111 | } | ||
112 | |||
113 | skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); | ||
114 | if (!skb) { | ||
115 | ndev->stats.rx_errors++; | ||
116 | ndev->stats.rx_frame_errors++; | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); | ||
121 | if (rc) { | ||
122 | ndev->stats.rx_errors++; | ||
123 | ndev->stats.rx_fifo_errors++; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, | ||
128 | void *data, int len) | ||
129 | { | ||
130 | struct net_device *ndev = qp_data; | ||
131 | struct sk_buff *skb; | ||
132 | |||
133 | skb = data; | ||
134 | if (!skb || !ndev) | ||
135 | return; | ||
136 | |||
137 | if (len > 0) { | ||
138 | ndev->stats.tx_packets++; | ||
139 | ndev->stats.tx_bytes += skb->len; | ||
140 | } else { | ||
141 | ndev->stats.tx_errors++; | ||
142 | ndev->stats.tx_aborted_errors++; | ||
143 | } | ||
144 | |||
145 | dev_kfree_skb(skb); | ||
146 | |||
147 | if (netif_queue_stopped(ndev)) | ||
148 | netif_wake_queue(ndev); | ||
149 | } | ||
150 | |||
151 | static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, | ||
152 | struct net_device *ndev) | ||
153 | { | ||
154 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
155 | int rc; | ||
156 | |||
157 | netdev_dbg(ndev, "ntb_transport_tx_enqueue\n"); | ||
158 | |||
159 | rc = ntb_transport_tx_enqueue(dev->qp, skb, skb->data, skb->len); | ||
160 | if (rc) | ||
161 | goto err; | ||
162 | |||
163 | return NETDEV_TX_OK; | ||
164 | |||
165 | err: | ||
166 | ndev->stats.tx_dropped++; | ||
167 | ndev->stats.tx_errors++; | ||
168 | netif_stop_queue(ndev); | ||
169 | return NETDEV_TX_BUSY; | ||
170 | } | ||
171 | |||
172 | static int ntb_netdev_open(struct net_device *ndev) | ||
173 | { | ||
174 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
175 | struct sk_buff *skb; | ||
176 | int rc, i, len; | ||
177 | |||
178 | /* Add some empty rx bufs */ | ||
179 | for (i = 0; i < NTB_RXQ_SIZE; i++) { | ||
180 | skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); | ||
181 | if (!skb) { | ||
182 | rc = -ENOMEM; | ||
183 | goto err; | ||
184 | } | ||
185 | |||
186 | rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, | ||
187 | ndev->mtu + ETH_HLEN); | ||
188 | if (rc == -EINVAL) | ||
189 | goto err; | ||
190 | } | ||
191 | |||
192 | netif_carrier_off(ndev); | ||
193 | ntb_transport_link_up(dev->qp); | ||
194 | |||
195 | return 0; | ||
196 | |||
197 | err: | ||
198 | while ((skb = ntb_transport_rx_remove(dev->qp, &len))) | ||
199 | dev_kfree_skb(skb); | ||
200 | return rc; | ||
201 | } | ||
202 | |||
203 | static int ntb_netdev_close(struct net_device *ndev) | ||
204 | { | ||
205 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
206 | struct sk_buff *skb; | ||
207 | int len; | ||
208 | |||
209 | ntb_transport_link_down(dev->qp); | ||
210 | |||
211 | while ((skb = ntb_transport_rx_remove(dev->qp, &len))) | ||
212 | dev_kfree_skb(skb); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int ntb_netdev_change_mtu(struct net_device *ndev, int new_mtu) | ||
218 | { | ||
219 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
220 | struct sk_buff *skb; | ||
221 | int len, rc; | ||
222 | |||
223 | if (new_mtu > ntb_transport_max_size(dev->qp) - ETH_HLEN) | ||
224 | return -EINVAL; | ||
225 | |||
226 | if (!netif_running(ndev)) { | ||
227 | ndev->mtu = new_mtu; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* Bring down the link and dispose of posted rx entries */ | ||
232 | ntb_transport_link_down(dev->qp); | ||
233 | |||
234 | if (ndev->mtu < new_mtu) { | ||
235 | int i; | ||
236 | |||
237 | for (i = 0; (skb = ntb_transport_rx_remove(dev->qp, &len)); i++) | ||
238 | dev_kfree_skb(skb); | ||
239 | |||
240 | for (; i; i--) { | ||
241 | skb = netdev_alloc_skb(ndev, new_mtu + ETH_HLEN); | ||
242 | if (!skb) { | ||
243 | rc = -ENOMEM; | ||
244 | goto err; | ||
245 | } | ||
246 | |||
247 | rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, | ||
248 | new_mtu + ETH_HLEN); | ||
249 | if (rc) { | ||
250 | dev_kfree_skb(skb); | ||
251 | goto err; | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | ndev->mtu = new_mtu; | ||
257 | |||
258 | ntb_transport_link_up(dev->qp); | ||
259 | |||
260 | return 0; | ||
261 | |||
262 | err: | ||
263 | ntb_transport_link_down(dev->qp); | ||
264 | |||
265 | while ((skb = ntb_transport_rx_remove(dev->qp, &len))) | ||
266 | dev_kfree_skb(skb); | ||
267 | |||
268 | netdev_err(ndev, "Error changing MTU, device inoperable\n"); | ||
269 | return rc; | ||
270 | } | ||
271 | |||
272 | static void ntb_netdev_tx_timeout(struct net_device *ndev) | ||
273 | { | ||
274 | if (netif_running(ndev)) | ||
275 | netif_wake_queue(ndev); | ||
276 | } | ||
277 | |||
278 | static const struct net_device_ops ntb_netdev_ops = { | ||
279 | .ndo_open = ntb_netdev_open, | ||
280 | .ndo_stop = ntb_netdev_close, | ||
281 | .ndo_start_xmit = ntb_netdev_start_xmit, | ||
282 | .ndo_change_mtu = ntb_netdev_change_mtu, | ||
283 | .ndo_tx_timeout = ntb_netdev_tx_timeout, | ||
284 | .ndo_set_mac_address = eth_mac_addr, | ||
285 | }; | ||
286 | |||
287 | static void ntb_get_drvinfo(struct net_device *ndev, | ||
288 | struct ethtool_drvinfo *info) | ||
289 | { | ||
290 | struct ntb_netdev *dev = netdev_priv(ndev); | ||
291 | |||
292 | strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); | ||
293 | strlcpy(info->version, NTB_NETDEV_VER, sizeof(info->version)); | ||
294 | strlcpy(info->bus_info, pci_name(dev->pdev), sizeof(info->bus_info)); | ||
295 | } | ||
296 | |||
297 | static int ntb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||
298 | { | ||
299 | cmd->supported = SUPPORTED_Backplane; | ||
300 | cmd->advertising = ADVERTISED_Backplane; | ||
301 | cmd->speed = SPEED_UNKNOWN; | ||
302 | ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); | ||
303 | cmd->duplex = DUPLEX_FULL; | ||
304 | cmd->port = PORT_OTHER; | ||
305 | cmd->phy_address = 0; | ||
306 | cmd->transceiver = XCVR_DUMMY1; | ||
307 | cmd->autoneg = AUTONEG_ENABLE; | ||
308 | cmd->maxtxpkt = 0; | ||
309 | cmd->maxrxpkt = 0; | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static const struct ethtool_ops ntb_ethtool_ops = { | ||
315 | .get_drvinfo = ntb_get_drvinfo, | ||
316 | .get_link = ethtool_op_get_link, | ||
317 | .get_settings = ntb_get_settings, | ||
318 | }; | ||
319 | |||
320 | static const struct ntb_queue_handlers ntb_netdev_handlers = { | ||
321 | .tx_handler = ntb_netdev_tx_handler, | ||
322 | .rx_handler = ntb_netdev_rx_handler, | ||
323 | .event_handler = ntb_netdev_event_handler, | ||
324 | }; | ||
325 | |||
326 | static int __devinit ntb_netdev_probe(struct pci_dev *pdev) | ||
327 | { | ||
328 | struct net_device *ndev; | ||
329 | struct ntb_netdev *dev; | ||
330 | int rc; | ||
331 | |||
332 | ndev = alloc_etherdev(sizeof(struct ntb_netdev)); | ||
333 | if (!ndev) | ||
334 | return -ENOMEM; | ||
335 | |||
336 | dev = netdev_priv(ndev); | ||
337 | dev->ndev = ndev; | ||
338 | dev->pdev = pdev; | ||
339 | BUG_ON(!dev->pdev); | ||
340 | ndev->features = NETIF_F_HIGHDMA; | ||
341 | |||
342 | ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; | ||
343 | |||
344 | ndev->hw_features = ndev->features; | ||
345 | ndev->watchdog_timeo = msecs_to_jiffies(NTB_TX_TIMEOUT_MS); | ||
346 | |||
347 | random_ether_addr(ndev->perm_addr); | ||
348 | memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len); | ||
349 | |||
350 | ndev->netdev_ops = &ntb_netdev_ops; | ||
351 | SET_ETHTOOL_OPS(ndev, &ntb_ethtool_ops); | ||
352 | |||
353 | dev->qp = ntb_transport_create_queue(ndev, pdev, &ntb_netdev_handlers); | ||
354 | if (!dev->qp) { | ||
355 | rc = -EIO; | ||
356 | goto err; | ||
357 | } | ||
358 | |||
359 | ndev->mtu = ntb_transport_max_size(dev->qp) - ETH_HLEN; | ||
360 | |||
361 | rc = register_netdev(ndev); | ||
362 | if (rc) | ||
363 | goto err1; | ||
364 | |||
365 | list_add(&dev->list, &dev_list); | ||
366 | pr_info("%s: %s created\n", KBUILD_MODNAME, ndev->name); | ||
367 | return 0; | ||
368 | |||
369 | err1: | ||
370 | ntb_transport_free_queue(dev->qp); | ||
371 | err: | ||
372 | free_netdev(ndev); | ||
373 | return rc; | ||
374 | } | ||
375 | |||
376 | static void __exit ntb_netdev_remove(struct pci_dev *pdev) | ||
377 | { | ||
378 | struct net_device *ndev; | ||
379 | struct ntb_netdev *dev; | ||
380 | |||
381 | list_for_each_entry(dev, &dev_list, list) { | ||
382 | if (dev->pdev == pdev) | ||
383 | break; | ||
384 | } | ||
385 | if (dev == NULL) | ||
386 | return; | ||
387 | |||
388 | ndev = dev->ndev; | ||
389 | |||
390 | unregister_netdev(ndev); | ||
391 | ntb_transport_free_queue(dev->qp); | ||
392 | free_netdev(ndev); | ||
393 | } | ||
394 | |||
395 | static struct ntb_client ntb_netdev_client = { | ||
396 | .driver.name = KBUILD_MODNAME, | ||
397 | .driver.owner = THIS_MODULE, | ||
398 | .probe = ntb_netdev_probe, | ||
399 | .remove = ntb_netdev_remove, | ||
400 | }; | ||
401 | |||
402 | static int __init ntb_netdev_init_module(void) | ||
403 | { | ||
404 | int rc; | ||
405 | |||
406 | rc = ntb_register_client_dev(KBUILD_MODNAME); | ||
407 | if (rc) | ||
408 | return rc; | ||
409 | return ntb_register_client(&ntb_netdev_client); | ||
410 | } | ||
411 | module_init(ntb_netdev_init_module); | ||
412 | |||
413 | static void __exit ntb_netdev_exit_module(void) | ||
414 | { | ||
415 | ntb_unregister_client(&ntb_netdev_client); | ||
416 | ntb_unregister_client_dev(KBUILD_MODNAME); | ||
417 | pr_info("%s: Driver removed\n", KBUILD_MODNAME); | ||
418 | } | ||
419 | module_exit(ntb_netdev_exit_module); | ||