aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Young <aaron.young@oracle.com>2016-03-15 14:35:39 -0400
committerDavid S. Miller <davem@davemloft.net>2016-03-18 19:33:00 -0400
commit5d01fa0c6bd84ddf1339a3fadfefecd2c28d472e (patch)
tree8e98abe74385c1d2993da5294d692bfa41675d16
parent67d0719f06ded9488311472b3d65ad37d992c332 (diff)
ldmvsw: Add ldmvsw.c driver code
Add ldmvsw.c driver Details: The ldmvsw driver very closely follows the sunvnet.c code and makes use of the sunvnet_common.c code for core functionality. A significant difference between sunvnet and ldmvsw driver is sunvnet creates a network interface for each vnet-port *parent* node in the MD while the ldmvsw driver creates a network interface for every vsw-port node in the Machine Description (MD). Therefore the netdev_priv() for sunvnet is a vnet structure while the netdev_priv() for ldmvsw is a vnet_port structure. Vnet_port structures allocated by ldmvsw have the vsw bit set. When finding the net_device associated with a port, the common code keys off this bit to use either the net_device found in the vnet_port or the net_device in the vnet structure (see the VNET_PORT_TO_NET_DEVICE() macro in sunvnet_common.h). This scheme allows the common code to work with both drivers with minimal changes. Similar to Xen, network interfaces created by the ldmvsw driver will always have a HW Addr (i.e. mac address) of FE:FF:FF:FF:FF:FF and each will be assigned the devname "vif<cfg_handle>.<port_id>" - where <cfg_handle> and <port_id> are a unique handle/port pair assigned to the associated vsw-port node in the MD. Signed-off-by: Aaron Young <aaron.young@oracle.com> Signed-off-by: Rashmi Narasimhan <rashmi.narasimhan@oracle.com> Reviewed-by: Sowmini Varadhan <sowmini.varadhan@oracle.com> Reviewed-by: Alexandre Chartre <Alexandre.Chartre@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/sparc/configs/sparc64_defconfig1
-rw-r--r--drivers/net/ethernet/sun/Kconfig11
-rw-r--r--drivers/net/ethernet/sun/Makefile1
-rw-r--r--drivers/net/ethernet/sun/ldmvsw.c468
4 files changed, 481 insertions, 0 deletions
diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig
index 6b68f12f29db..04920ab8e292 100644
--- a/arch/sparc/configs/sparc64_defconfig
+++ b/arch/sparc/configs/sparc64_defconfig
@@ -102,6 +102,7 @@ CONFIG_SUNLANCE=m
102CONFIG_HAPPYMEAL=m 102CONFIG_HAPPYMEAL=m
103CONFIG_SUNGEM=m 103CONFIG_SUNGEM=m
104CONFIG_SUNVNET=m 104CONFIG_SUNVNET=m
105CONFIG_LDMVSW=m
105CONFIG_NET_PCI=y 106CONFIG_NET_PCI=y
106CONFIG_E1000=m 107CONFIG_E1000=m
107CONFIG_E1000E=m 108CONFIG_E1000E=m
diff --git a/drivers/net/ethernet/sun/Kconfig b/drivers/net/ethernet/sun/Kconfig
index aa58c11ea6db..a4b40e3015e5 100644
--- a/drivers/net/ethernet/sun/Kconfig
+++ b/drivers/net/ethernet/sun/Kconfig
@@ -80,6 +80,17 @@ config SUNVNET
80 ---help--- 80 ---help---
81 Support for virtual network devices under Sun Logical Domains. 81 Support for virtual network devices under Sun Logical Domains.
82 82
83config LDMVSW
84 tristate "Sun4v LDoms Virtual Switch support"
85 depends on SUN_LDOMS
86 ---help---
87 Support for virtual switch devices under Sun4v Logical Domains.
88 This driver adds a network interface for every vsw-port node
89 found in the machine description of a service domain.
90 Linux bridge/switch software can use these interfaces for
91 guest domain network interconnectivity or guest domain
92 connection to a physical network on a service domain.
93
83config NIU 94config NIU
84 tristate "Sun Neptune 10Gbit Ethernet support" 95 tristate "Sun Neptune 10Gbit Ethernet support"
85 depends on PCI 96 depends on PCI
diff --git a/drivers/net/ethernet/sun/Makefile b/drivers/net/ethernet/sun/Makefile
index 7b622aa14c5b..37855438b3cb 100644
--- a/drivers/net/ethernet/sun/Makefile
+++ b/drivers/net/ethernet/sun/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_SUNGEM) += sungem.o
9obj-$(CONFIG_CASSINI) += cassini.o 9obj-$(CONFIG_CASSINI) += cassini.o
10obj-$(CONFIG_SUNVNET_COMMON) += sunvnet_common.o 10obj-$(CONFIG_SUNVNET_COMMON) += sunvnet_common.o
11obj-$(CONFIG_SUNVNET) += sunvnet.o 11obj-$(CONFIG_SUNVNET) += sunvnet.o
12obj-$(CONFIG_LDMVSW) += ldmvsw.o
12obj-$(CONFIG_NIU) += niu.o 13obj-$(CONFIG_NIU) += niu.o
diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c
new file mode 100644
index 000000000000..e15bf84fc6b2
--- /dev/null
+++ b/drivers/net/ethernet/sun/ldmvsw.c
@@ -0,0 +1,468 @@
1/* ldmvsw.c: Sun4v LDOM Virtual Switch Driver.
2 *
3 * Copyright (C) 2016 Oracle. All rights reserved.
4 */
5
6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7
8#include <linux/delay.h>
9#include <linux/etherdevice.h>
10#include <linux/ethtool.h>
11#include <linux/highmem.h>
12#include <linux/if_vlan.h>
13#include <linux/init.h>
14#include <linux/kconfig.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
18#include <linux/netdevice.h>
19#include <linux/slab.h>
20#include <linux/types.h>
21
22#if defined(CONFIG_IPV6)
23#include <linux/icmpv6.h>
24#endif
25
26#include <net/ip.h>
27#include <net/icmp.h>
28#include <net/route.h>
29
30#include <asm/vio.h>
31#include <asm/ldc.h>
32
33/* This driver makes use of the common code in sunvnet_common.c */
34#include "sunvnet_common.h"
35
36/* Length of time before we decide the hardware is hung,
37 * and dev->tx_timeout() should be called to fix the problem.
38 */
39#define VSW_TX_TIMEOUT (10 * HZ)
40
41/* Static HW Addr used for the network interfaces representing vsw ports */
42static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
43
44#define DRV_MODULE_NAME "ldmvsw"
45#define DRV_MODULE_VERSION "1.0"
46#define DRV_MODULE_RELDATE "Jan 15, 2016"
47
48static char version[] =
49 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
50MODULE_AUTHOR("Oracle");
51MODULE_DESCRIPTION("Sun4v LDOM Virtual Switch Driver");
52MODULE_LICENSE("GPL");
53MODULE_VERSION(DRV_MODULE_VERSION);
54
55/* Ordered from largest major to lowest */
56static struct vio_version vsw_versions[] = {
57 { .major = 1, .minor = 8 },
58 { .major = 1, .minor = 7 },
59 { .major = 1, .minor = 6 },
60 { .major = 1, .minor = 0 },
61};
62
63static void vsw_get_drvinfo(struct net_device *dev,
64 struct ethtool_drvinfo *info)
65{
66 strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
67 strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
68}
69
70static u32 vsw_get_msglevel(struct net_device *dev)
71{
72 struct vnet_port *port = netdev_priv(dev);
73
74 return port->vp->msg_enable;
75}
76
77static void vsw_set_msglevel(struct net_device *dev, u32 value)
78{
79 struct vnet_port *port = netdev_priv(dev);
80
81 port->vp->msg_enable = value;
82}
83
84static const struct ethtool_ops vsw_ethtool_ops = {
85 .get_drvinfo = vsw_get_drvinfo,
86 .get_msglevel = vsw_get_msglevel,
87 .set_msglevel = vsw_set_msglevel,
88 .get_link = ethtool_op_get_link,
89};
90
91static LIST_HEAD(vnet_list);
92static DEFINE_MUTEX(vnet_list_mutex);
93
94/* func arg to vnet_start_xmit_common() to get the proper tx port */
95static struct vnet_port *vsw_tx_port_find(struct sk_buff *skb,
96 struct net_device *dev)
97{
98 struct vnet_port *port = netdev_priv(dev);
99
100 return port;
101}
102
103static u16 vsw_select_queue(struct net_device *dev, struct sk_buff *skb,
104 void *accel_priv, select_queue_fallback_t fallback)
105{
106 struct vnet_port *port = netdev_priv(dev);
107
108 if (!port)
109 return 0;
110
111 return port->q_index;
112}
113
114/* Wrappers to common functions */
115static int vsw_start_xmit(struct sk_buff *skb, struct net_device *dev)
116{
117 return sunvnet_start_xmit_common(skb, dev, vsw_tx_port_find);
118}
119
120static void vsw_set_rx_mode(struct net_device *dev)
121{
122 struct vnet_port *port = netdev_priv(dev);
123
124 return sunvnet_set_rx_mode_common(dev, port->vp);
125}
126
127#ifdef CONFIG_NET_POLL_CONTROLLER
128static void vsw_poll_controller(struct net_device *dev)
129{
130 struct vnet_port *port = netdev_priv(dev);
131
132 return sunvnet_poll_controller_common(dev, port->vp);
133}
134#endif
135
136static const struct net_device_ops vsw_ops = {
137 .ndo_open = sunvnet_open_common,
138 .ndo_stop = sunvnet_close_common,
139 .ndo_set_rx_mode = vsw_set_rx_mode,
140 .ndo_set_mac_address = sunvnet_set_mac_addr_common,
141 .ndo_validate_addr = eth_validate_addr,
142 .ndo_tx_timeout = sunvnet_tx_timeout_common,
143 .ndo_change_mtu = sunvnet_change_mtu_common,
144 .ndo_start_xmit = vsw_start_xmit,
145 .ndo_select_queue = vsw_select_queue,
146#ifdef CONFIG_NET_POLL_CONTROLLER
147 .ndo_poll_controller = vsw_poll_controller,
148#endif
149};
150
151static const char *local_mac_prop = "local-mac-address";
152static const char *cfg_handle_prop = "cfg-handle";
153
154static struct vnet *vsw_get_vnet(struct mdesc_handle *hp,
155 u64 port_node,
156 u64 *handle)
157{
158 struct vnet *vp;
159 struct vnet *iter;
160 const u64 *local_mac = NULL;
161 const u64 *cfghandle = NULL;
162 u64 a;
163
164 /* Get the parent virtual-network-switch macaddr and cfghandle */
165 mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) {
166 u64 target = mdesc_arc_target(hp, a);
167 const char *name;
168
169 name = mdesc_get_property(hp, target, "name", NULL);
170 if (!name || strcmp(name, "virtual-network-switch"))
171 continue;
172
173 local_mac = mdesc_get_property(hp, target,
174 local_mac_prop, NULL);
175 cfghandle = mdesc_get_property(hp, target,
176 cfg_handle_prop, NULL);
177 break;
178 }
179 if (!local_mac || !cfghandle)
180 return ERR_PTR(-ENODEV);
181
182 /* find or create associated vnet */
183 vp = NULL;
184 mutex_lock(&vnet_list_mutex);
185 list_for_each_entry(iter, &vnet_list, list) {
186 if (iter->local_mac == *local_mac) {
187 vp = iter;
188 break;
189 }
190 }
191
192 if (!vp) {
193 vp = kzalloc(sizeof(*vp), GFP_KERNEL);
194 if (unlikely(!vp)) {
195 mutex_unlock(&vnet_list_mutex);
196 return ERR_PTR(-ENOMEM);
197 }
198
199 spin_lock_init(&vp->lock);
200 INIT_LIST_HEAD(&vp->port_list);
201 INIT_LIST_HEAD(&vp->list);
202 vp->local_mac = *local_mac;
203 list_add(&vp->list, &vnet_list);
204 }
205
206 mutex_unlock(&vnet_list_mutex);
207
208 *handle = (u64)*cfghandle;
209
210 return vp;
211}
212
213static struct net_device *vsw_alloc_netdev(u8 hwaddr[],
214 struct vio_dev *vdev,
215 u64 handle,
216 u64 port_id)
217{
218 struct net_device *dev;
219 struct vnet_port *port;
220 int i;
221
222 dev = alloc_etherdev_mqs(sizeof(*port), VNET_MAX_TXQS, 1);
223 if (!dev)
224 return ERR_PTR(-ENOMEM);
225 dev->needed_headroom = VNET_PACKET_SKIP + 8;
226 dev->needed_tailroom = 8;
227
228 for (i = 0; i < ETH_ALEN; i++) {
229 dev->dev_addr[i] = hwaddr[i];
230 dev->perm_addr[i] = dev->dev_addr[i];
231 }
232
233 sprintf(dev->name, "vif%d.%d", (int)handle, (int)port_id);
234
235 dev->netdev_ops = &vsw_ops;
236 dev->ethtool_ops = &vsw_ethtool_ops;
237 dev->watchdog_timeo = VSW_TX_TIMEOUT;
238
239 dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE |
240 NETIF_F_HW_CSUM | NETIF_F_SG;
241 dev->features = dev->hw_features;
242
243 SET_NETDEV_DEV(dev, &vdev->dev);
244
245 return dev;
246}
247
248static struct ldc_channel_config vsw_ldc_cfg = {
249 .event = sunvnet_event_common,
250 .mtu = 64,
251 .mode = LDC_MODE_UNRELIABLE,
252};
253
254static struct vio_driver_ops vsw_vio_ops = {
255 .send_attr = sunvnet_send_attr_common,
256 .handle_attr = sunvnet_handle_attr_common,
257 .handshake_complete = sunvnet_handshake_complete_common,
258};
259
260static void print_version(void)
261{
262 printk_once(KERN_INFO "%s", version);
263}
264
265static const char *remote_macaddr_prop = "remote-mac-address";
266static const char *id_prop = "id";
267
268static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
269{
270 struct mdesc_handle *hp;
271 struct vnet_port *port;
272 unsigned long flags;
273 struct vnet *vp;
274 struct net_device *dev;
275 const u64 *rmac;
276 int len, i, err;
277 const u64 *port_id;
278 u64 handle;
279
280 print_version();
281
282 hp = mdesc_grab();
283
284 rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len);
285 err = -ENODEV;
286 if (!rmac) {
287 pr_err("Port lacks %s property\n", remote_macaddr_prop);
288 mdesc_release(hp);
289 return err;
290 }
291
292 port_id = mdesc_get_property(hp, vdev->mp, id_prop, NULL);
293 err = -ENODEV;
294 if (!port_id) {
295 pr_err("Port lacks %s property\n", id_prop);
296 mdesc_release(hp);
297 return err;
298 }
299
300 /* Get (or create) the vnet associated with this port */
301 vp = vsw_get_vnet(hp, vdev->mp, &handle);
302 if (unlikely(IS_ERR(vp))) {
303 err = PTR_ERR(vp);
304 pr_err("Failed to get vnet for vsw-port\n");
305 mdesc_release(hp);
306 return err;
307 }
308
309 mdesc_release(hp);
310
311 dev = vsw_alloc_netdev(vsw_port_hwaddr, vdev, handle, *port_id);
312 if (IS_ERR(dev)) {
313 err = PTR_ERR(dev);
314 pr_err("Failed to alloc netdev for vsw-port\n");
315 return err;
316 }
317
318 port = netdev_priv(dev);
319
320 INIT_LIST_HEAD(&port->list);
321
322 for (i = 0; i < ETH_ALEN; i++)
323 port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff;
324
325 port->vp = vp;
326 port->dev = dev;
327 port->switch_port = 1;
328 port->tso = true;
329 port->tsolen = 0;
330
331 /* Mark the port as belonging to ldmvsw which directs the
332 * the common code to use the net_device in the vnet_port
333 * rather than the net_device in the vnet (which is used
334 * by sunvnet). This bit is used by the VNET_PORT_TO_NET_DEVICE
335 * macro.
336 */
337 port->vsw = 1;
338
339 err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK,
340 vsw_versions, ARRAY_SIZE(vsw_versions),
341 &vsw_vio_ops, dev->name);
342 if (err)
343 goto err_out_free_dev;
344
345 err = vio_ldc_alloc(&port->vio, &vsw_ldc_cfg, port);
346 if (err)
347 goto err_out_free_dev;
348
349 dev_set_drvdata(&vdev->dev, port);
350
351 netif_napi_add(dev, &port->napi, sunvnet_poll_common,
352 NAPI_POLL_WEIGHT);
353
354 spin_lock_irqsave(&vp->lock, flags);
355 list_add_rcu(&port->list, &vp->port_list);
356 spin_unlock_irqrestore(&vp->lock, flags);
357
358 setup_timer(&port->clean_timer, sunvnet_clean_timer_expire_common,
359 (unsigned long)port);
360
361 err = register_netdev(dev);
362 if (err) {
363 pr_err("Cannot register net device, aborting\n");
364 goto err_out_del_timer;
365 }
366
367 spin_lock_irqsave(&vp->lock, flags);
368 sunvnet_port_add_txq_common(port);
369 spin_unlock_irqrestore(&vp->lock, flags);
370
371 napi_enable(&port->napi);
372 vio_port_up(&port->vio);
373
374 netdev_info(dev, "LDOM vsw-port %pM\n", dev->dev_addr);
375
376 pr_info("%s: PORT ( remote-mac %pM%s )\n", dev->name,
377 port->raddr, " switch-port");
378
379 return 0;
380
381err_out_del_timer:
382 del_timer_sync(&port->clean_timer);
383 list_del_rcu(&port->list);
384 synchronize_rcu();
385 netif_napi_del(&port->napi);
386 dev_set_drvdata(&vdev->dev, NULL);
387 vio_ldc_free(&port->vio);
388
389err_out_free_dev:
390 free_netdev(dev);
391 return err;
392}
393
394static int vsw_port_remove(struct vio_dev *vdev)
395{
396 struct vnet_port *port = dev_get_drvdata(&vdev->dev);
397 unsigned long flags;
398
399 if (port) {
400 del_timer_sync(&port->vio.timer);
401
402 napi_disable(&port->napi);
403
404 list_del_rcu(&port->list);
405
406 synchronize_rcu();
407 del_timer_sync(&port->clean_timer);
408 spin_lock_irqsave(&port->vp->lock, flags);
409 sunvnet_port_rm_txq_common(port);
410 spin_unlock_irqrestore(&port->vp->lock, flags);
411 netif_napi_del(&port->napi);
412 sunvnet_port_free_tx_bufs_common(port);
413 vio_ldc_free(&port->vio);
414
415 dev_set_drvdata(&vdev->dev, NULL);
416
417 unregister_netdev(port->dev);
418 free_netdev(port->dev);
419 }
420
421 return 0;
422}
423
424static void vsw_cleanup(void)
425{
426 struct vnet *vp;
427
428 /* just need to free up the vnet list */
429 mutex_lock(&vnet_list_mutex);
430 while (!list_empty(&vnet_list)) {
431 vp = list_first_entry(&vnet_list, struct vnet, list);
432 list_del(&vp->list);
433 /* vio_unregister_driver() should have cleaned up port_list */
434 if (!list_empty(&vp->port_list))
435 pr_err("Ports not removed by VIO subsystem!\n");
436 kfree(vp);
437 }
438 mutex_unlock(&vnet_list_mutex);
439}
440
441static const struct vio_device_id vsw_port_match[] = {
442 {
443 .type = "vsw-port",
444 },
445 {},
446};
447MODULE_DEVICE_TABLE(vio, vsw_port_match);
448
449static struct vio_driver vsw_port_driver = {
450 .id_table = vsw_port_match,
451 .probe = vsw_port_probe,
452 .remove = vsw_port_remove,
453 .name = "vsw_port",
454};
455
456static int __init vsw_init(void)
457{
458 return vio_register_driver(&vsw_port_driver);
459}
460
461static void __exit vsw_exit(void)
462{
463 vio_unregister_driver(&vsw_port_driver);
464 vsw_cleanup();
465}
466
467module_init(vsw_init);
468module_exit(vsw_exit);