From 2493b842f258e14938f278e44ecc26970dfabbf0 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:56:57 -0800 Subject: [PATCH 1/8] sunvnet: make sunvnet common code dynamically loadable When the sunvnet_common code was split out for use by both sunvnet and the newer ldmvsw, it was made into a static kernel library, which limits the usefulness of sunvnet and ldmvsw as loadables, since most of the real work is being done in the shared code. Also, this is simply dead code in kernels that aren't running the LDoms. This patch makes the sunvnet_common into a dynamically loadable module and makes sunvnet and ldmvsw dependent on sunvnet_common. Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/Kconfig | 8 ++++++-- drivers/net/ethernet/sun/sunvnet_common.c | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sun/Kconfig b/drivers/net/ethernet/sun/Kconfig index a4b40e3015e5..b2caf5132bd2 100644 --- a/drivers/net/ethernet/sun/Kconfig +++ b/drivers/net/ethernet/sun/Kconfig @@ -70,19 +70,23 @@ config CASSINI . config SUNVNET_COMMON - bool + tristate "Common routines to support Sun Virtual Networking" depends on SUN_LDOMS - default y if SUN_LDOMS + default m config SUNVNET tristate "Sun Virtual Network support" + default m depends on SUN_LDOMS + depends on SUNVNET_COMMON ---help--- Support for virtual network devices under Sun Logical Domains. config LDMVSW tristate "Sun4v LDoms Virtual Switch support" + default m depends on SUN_LDOMS + depends on SUNVNET_COMMON ---help--- Support for virtual switch devices under Sun4v Logical Domains. This driver adds a network interface for every vsw-port node diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 191c8ade6155..c71f0007ad5b 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -37,6 +37,11 @@ */ #define VNET_MAX_RETRIES 10 +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Sun LDOM virtual network support library"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.1"); + static int __vnet_tx_trigger(struct vnet_port *port, u32 start); static void vnet_port_reset(struct vnet_port *port); From d4aa89cc2bbe021722c946eb11b21ebb0f13c825 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Mon, 13 Feb 2017 10:56:58 -0800 Subject: [PATCH 2/8] sunvnet: remove unused variable in maybe_tx_wakeup The vio_dring_state *dr variable is unused in maybe_tx_wakeup(). As the comments indicate, we call maybe_tx_wakeup() whenever we get a STOPPED LDC message on the port. If the queue is stopped, we want to wake it up so that we will send another START message at the next TX and trigger the consumer to drain the dring. Signed-off-by: Sowmini Varadhan Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index c71f0007ad5b..0f940f0e488d 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -719,12 +719,8 @@ static void maybe_tx_wakeup(struct vnet_port *port) txq = netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), port->q_index); __netif_tx_lock(txq, smp_processor_id()); - if (likely(netif_tx_queue_stopped(txq))) { - struct vio_dring_state *dr; - - dr = &port->vio.drings[VIO_DRIVER_TX_RING]; + if (likely(netif_tx_queue_stopped(txq))) netif_tx_wake_queue(txq); - } __netif_tx_unlock(txq); } From f2f3e210bffe5c8f8b30d0b0c7b0f733ff5db334 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:56:59 -0800 Subject: [PATCH 3/8] sunvnet: update version and version printing There have been several changes since the first version of this code, so we bump the version number. While we're at it, we can simplify the version printing a bit and drop a couple lines of code. Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 5356a7074796..4cc2571f71c6 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -38,11 +38,11 @@ #define VNET_TX_TIMEOUT (5 * HZ) #define DRV_MODULE_NAME "sunvnet" -#define DRV_MODULE_VERSION "1.0" -#define DRV_MODULE_RELDATE "June 25, 2007" +#define DRV_MODULE_VERSION "2.0" +#define DRV_MODULE_RELDATE "February 3, 2017" static char version[] = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Sun LDOM virtual network driver"); MODULE_LICENSE("GPL"); @@ -303,11 +303,6 @@ static struct vio_driver_ops vnet_vio_ops = { .handshake_complete = sunvnet_handshake_complete_common, }; -static void print_version(void) -{ - printk_once(KERN_INFO "%s", version); -} - const char *remote_macaddr_prop = "remote-mac-address"; static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) @@ -319,8 +314,6 @@ static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) const u64 *rmac; int len, i, err, switch_port; - print_version(); - hp = mdesc_grab(); vp = vnet_find_parent(hp, vdev->mp, vdev); @@ -446,6 +439,7 @@ static struct vio_driver vnet_port_driver = { static int __init vnet_init(void) { + pr_info("%s\n", version); return vio_register_driver(&vnet_port_driver); } From fd263fb6e718c5bdf35cbc1de4f781c71794d2a4 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:57:00 -0800 Subject: [PATCH 4/8] sunvnet: add memory barrier before check for tx enable In order to allow the underlying LDC and outstanding memory operations to potentially catch up with the driver's Tx requests, add a memory barrier before checking again for available tx descriptors. Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 0f940f0e488d..623363bf08df 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1427,6 +1427,7 @@ ldc_start_done: dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); if (unlikely(vnet_tx_dring_avail(dr) < 1)) { netif_tx_stop_queue(txq); + smp_rmb(); if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) netif_tx_wake_queue(txq); } From bf091f3f362b3c562a18bbf7a2d3e2f3a36eba1d Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:57:01 -0800 Subject: [PATCH 5/8] sunvnet: straighten up message event handling logic The use of gotos for handling the incoming events made this code harder to read and support than it should be. This patch straightens out and clears up the logic. Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 88 +++++++++++------------ 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 623363bf08df..d124bdebf636 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -738,41 +738,37 @@ static int vnet_event_napi(struct vnet_port *port, int budget) struct vio_driver_state *vio = &port->vio; int tx_wakeup, err; int npkts = 0; - int event = (port->rx_event & LDC_EVENT_RESET); -ldc_ctrl: - if (unlikely(event == LDC_EVENT_RESET || - event == LDC_EVENT_UP)) { - vio_link_state_change(vio, event); + /* we don't expect any other bits */ + BUG_ON(port->rx_event & ~(LDC_EVENT_DATA_READY | + LDC_EVENT_RESET | + LDC_EVENT_UP)); - if (event == LDC_EVENT_RESET) { - vnet_port_reset(port); - vio_port_up(vio); + /* RESET takes precedent over any other event */ + if (port->rx_event & LDC_EVENT_RESET) { + vio_link_state_change(vio, LDC_EVENT_RESET); + vnet_port_reset(port); + vio_port_up(vio); + + /* If the device is running but its tx queue was + * stopped (due to flow control), restart it. + * This is necessary since vnet_port_reset() + * clears the tx drings and thus we may never get + * back a VIO_TYPE_DATA ACK packet - which is + * the normal mechanism to restart the tx queue. + */ + if (netif_running(dev)) + maybe_tx_wakeup(port); - /* If the device is running but its tx queue was - * stopped (due to flow control), restart it. - * This is necessary since vnet_port_reset() - * clears the tx drings and thus we may never get - * back a VIO_TYPE_DATA ACK packet - which is - * the normal mechanism to restart the tx queue. - */ - if (netif_running(dev)) - maybe_tx_wakeup(port); - } port->rx_event = 0; return 0; } - /* We may have multiple LDC events in rx_event. Unroll send_events() */ - event = (port->rx_event & LDC_EVENT_UP); - port->rx_event &= ~(LDC_EVENT_RESET | LDC_EVENT_UP); - if (event == LDC_EVENT_UP) - goto ldc_ctrl; - event = port->rx_event; - if (!(event & LDC_EVENT_DATA_READY)) - return 0; - /* we dont expect any other bits than RESET, UP, DATA_READY */ - BUG_ON(event != LDC_EVENT_DATA_READY); + if (port->rx_event & LDC_EVENT_UP) { + vio_link_state_change(vio, LDC_EVENT_UP); + port->rx_event = 0; + return 0; + } err = 0; tx_wakeup = 0; @@ -795,25 +791,25 @@ ldc_ctrl: pkt->start_idx = vio_dring_next(dr, port->napi_stop_idx); pkt->end_idx = -1; - goto napi_resume; + } else { + err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); + if (unlikely(err < 0)) { + if (err == -ECONNRESET) + vio_conn_reset(vio); + break; + } + if (err == 0) + break; + viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", + msgbuf.tag.type, + msgbuf.tag.stype, + msgbuf.tag.stype_env, + msgbuf.tag.sid); + err = vio_validate_sid(vio, &msgbuf.tag); + if (err < 0) + break; } - err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); - if (unlikely(err < 0)) { - if (err == -ECONNRESET) - vio_conn_reset(vio); - break; - } - if (err == 0) - break; - viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", - msgbuf.tag.type, - msgbuf.tag.stype, - msgbuf.tag.stype_env, - msgbuf.tag.sid); - err = vio_validate_sid(vio, &msgbuf.tag); - if (err < 0) - break; -napi_resume: + if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) { if (!sunvnet_port_is_up_common(port)) { From daa86e50f649fccadafc53994ddc4254d75a008b Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:57:02 -0800 Subject: [PATCH 6/8] sunvnet: remove extra rcu_read_unlocks The RCU read lock is grabbed first thing in sunvnet_start_xmit_common() so it always needs to be released. This removes the conditional release in the dropped packet error path and removes a couple of superfluous calls in the middle of the code. Reported-by: Bijan Mottahedeh Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index d124bdebf636..65f7038e82fe 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1253,10 +1253,8 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, rcu_read_lock(); port = vnet_tx_port(skb, dev); - if (unlikely(!port)) { - rcu_read_unlock(); + if (unlikely(!port)) goto out_dropped; - } if (skb_is_gso(skb) && skb->len > port->tsolen) { err = vnet_handle_offloads(port, skb, vnet_tx_port); @@ -1281,7 +1279,6 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, fl4.saddr = ip_hdr(skb)->saddr; rt = ip_route_output_key(dev_net(dev), &fl4); - rcu_read_unlock(); if (!IS_ERR(rt)) { skb_dst_set(skb, &rt->dst); icmp_send(skb, ICMP_DEST_UNREACH, @@ -1441,8 +1438,7 @@ out_dropped: jiffies + VNET_CLEAN_TIMEOUT); else if (port) del_timer(&port->clean_timer); - if (port) - rcu_read_unlock(); + rcu_read_unlock(); if (skb) dev_kfree_skb(skb); vnet_free_skbs(freeskbs); From 7602011f59cc32ebc3a5f9058d6ba11b096c8c50 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:57:03 -0800 Subject: [PATCH 7/8] ldmvsw: update and simplify version string New version and simplify the print code. Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/ldmvsw.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 335b87660638..f0fe6cf57cda 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -41,11 +41,11 @@ static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #define DRV_MODULE_NAME "ldmvsw" -#define DRV_MODULE_VERSION "1.0" -#define DRV_MODULE_RELDATE "Jan 15, 2016" +#define DRV_MODULE_VERSION "1.1" +#define DRV_MODULE_RELDATE "February 3, 2017" static char version[] = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; MODULE_AUTHOR("Oracle"); MODULE_DESCRIPTION("Sun4v LDOM Virtual Switch Driver"); MODULE_LICENSE("GPL"); @@ -259,11 +259,6 @@ static struct vio_driver_ops vsw_vio_ops = { .handshake_complete = sunvnet_handshake_complete_common, }; -static void print_version(void) -{ - printk_once(KERN_INFO "%s", version); -} - static const char *remote_macaddr_prop = "remote-mac-address"; static const char *id_prop = "id"; @@ -279,8 +274,6 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) const u64 *port_id; u64 handle; - print_version(); - hp = mdesc_grab(); rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); @@ -457,6 +450,7 @@ static struct vio_driver vsw_port_driver = { static int __init vsw_init(void) { + pr_info("%s\n", version); return vio_register_driver(&vsw_port_driver); } From bc221a34ac473b444a7cfdd0c152b4c71f79326b Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Mon, 13 Feb 2017 10:57:04 -0800 Subject: [PATCH 8/8] ldmvsw: disable tso and gso for bridge operations The ldmvsw driver is specifically for supporting the ldom virtual networking by running in the primary ldom and using the LDC to connect the remaining ldoms to the outside world via a bridge. With TSO and GSO supported while connected the bridge, things tend to misbehave as seen in our case by delayed packets, enough to begin triggering retransmits and affecting overall throughput. By turning off advertised support for TSO and GSO we restore stable traffic flow through the bridge. Orabug: 23293104 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/ldmvsw.c | 5 ++--- drivers/net/ethernet/sun/sunvnet_common.c | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index f0fe6cf57cda..89952deae47f 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -234,8 +234,7 @@ static struct net_device *vsw_alloc_netdev(u8 hwaddr[], dev->ethtool_ops = &vsw_ethtool_ops; dev->watchdog_timeo = VSW_TX_TIMEOUT; - dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | - NETIF_F_HW_CSUM | NETIF_F_SG; + dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; /* MTU range: 68 - 65535 */ @@ -320,7 +319,7 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) port->vp = vp; port->dev = dev; port->switch_port = 1; - port->tso = true; + port->tso = false; /* no tso in vsw, misbehaves in bridge */ port->tsolen = 0; /* Mark the port as belonging to ldmvsw which directs the diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 65f7038e82fe..fa2d11ca9b81 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -186,6 +186,7 @@ static int handle_attr_info(struct vio_driver_state *vio, } else { pkt->cflags &= ~VNET_LSO_IPV4_CAPAB; pkt->ipv4_lso_maxlen = 0; + port->tsolen = 0; } /* for version >= 1.6, ACK packet mode we support */ @@ -1635,7 +1636,7 @@ static void vnet_port_reset(struct vnet_port *port) del_timer(&port->clean_timer); sunvnet_port_free_tx_bufs_common(port); port->rmtu = 0; - port->tso = true; + port->tso = (port->vsw == 0); /* no tso in vsw, misbehaves in bridge */ port->tsolen = 0; }