Skip to contentSkip to conversation

hv_netvsc: minor fixes

PATCH In review Stephen Hemminger 3 participants 12 messages Mar 2, 2018 – Mar 5, 2018

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v2 cover hv_netvsc: minor fixes

These are improvements to netvsc driver. They aren't functionality changes so not targeting net-next; and they are not show stopper bugs that need to go to stable either.

v2
- drop the irq flags patch, defer it to net-next
- split the multicast filter flag patch out
- change propogate rx mode patch to handle startup of vf

Stephen Hemminger (9):
hv_netvsc: avoid retry on send during shutdown
hv_netvsc: only wake transmit queue if link is up
hv_netvsc: fix error unwind handling if vmbus_open fails
hv_netvsc: cancel subchannel setup before halting device
hv_netvsc: fix race in napi poll when rescheduling
hv_netvsc: use napi_schedule_irqoff
hv_netvsc: defer queue selection to VF
hv_netvsc: filter multicast/broadcast
hv_netvsc: propagate rx filters to VF

 drivers/net/hyperv/netvsc.c       | 33 ++++++++-------------
 drivers/net/hyperv/netvsc_drv.c   | 62 ++++++++++++++++++++++++++++++++-------
 drivers/net/hyperv/rndis_filter.c | 23 ++++++++++-----
 3 files changed, 79 insertions(+), 39 deletions(-)
Signature · 1 line
2.16.1
#1 lore raw

PATCH v2 1/9 hv_netvsc: avoid retry on send during shutdown

Change the initialization order so that the device is ready to transmit (ie connect vsp is completed) before setting the internal reference to the device with RCU.

This avoids any races on initialization and prevents retry issues on shutdown.

---
 drivers/net/hyperv/netvsc.c | 24 +++++++-----------------
 1 file changed, 7 insertions(+), 17 deletions(-)
drivers/net/hyperv/netvsc.c +7 -17
index 17e529af79dc..686900d61374 100644
@@ -852,13 +852,6 @@ int netvsc_send(struct net_device *ndev,
 	if (unlikely(!net_device || net_device->destroy))
 		return -ENODEV;
 
-	/* We may race with netvsc_connect_vsp()/netvsc_init_buf() and get
-	 * here before the negotiation with the host is finished and
-	 * send_section_map may not be allocated yet.
-	 */
-	if (unlikely(!net_device->send_section_map))
-		return -EAGAIN;
-
 	nvchan = &net_device->chan_table[packet->q_idx];
 	packet->send_buf_index = NETVSC_INVALID_INDEX;
 	packet->cp_partial = false;
@@ -866,10 +859,8 @@ int netvsc_send(struct net_device *ndev,
 	/* Send control message directly without accessing msd (Multi-Send
 	 * Data) field which may be changed during data packet processing.
 	 */
-	if (!skb) {
-		cur_send = packet;
-		goto send_now;
-	}
+	if (!skb)
+		return netvsc_send_pkt(device, packet, net_device, pb, skb);
 
 	/* batch packets in send buffer if possible */
 	msdp = &nvchan->msd;
@@ -953,7 +944,6 @@ int netvsc_send(struct net_device *ndev,
 		}
 	}
 
-send_now:
 	if (cur_send)
 		ret = netvsc_send_pkt(device, cur_send, net_device, pb, skb);
 
@@ -1306,11 +1296,6 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
 
 	napi_enable(&net_device->chan_table[0].napi);
 
-	/* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is
-	 * populated.
-	 */
-	rcu_assign_pointer(net_device_ctx->nvdev, net_device);
-
 	/* Connect with the NetVsp */
 	ret = netvsc_connect_vsp(device, net_device, device_info);
 	if (ret != 0) {
@@ -1319,6 +1304,11 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
 		goto close;
 	}
 
+	/* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is
+	 * populated.
+	 */
+	rcu_assign_pointer(net_device_ctx->nvdev, net_device);
+
 	return net_device;
 
 close:
Signature · 1 line
2.16.1
#2 lore raw

PATCH v2 2/9 hv_netvsc: only wake transmit queue if link is up

In reply to Stephen Hemminger

Don't wake transmit queues if link is not up yet.

---
 drivers/net/hyperv/netvsc_drv.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/net/hyperv/netvsc_drv.c +3 -4
index c5584c2d440e..fa6cf18e7719 100644
@@ -91,12 +91,11 @@ static int netvsc_open(struct net_device *net)
 		return ret;
 	}
 
-	netif_tx_wake_all_queues(net);
-
 	rdev = nvdev->extension;
-
-	if (!rdev->link_state)
+	if (!rdev->link_state) {
 		netif_carrier_on(net);
+		netif_tx_wake_all_queues(net);
+	}
 
 	if (vf_netdev) {
 		/* Setting synthetic device up transparently sets
Signature · 1 line
2.16.1
#3 lore raw

PATCH v2 3/9 hv_netvsc: fix error unwind handling if vmbus_open fails

In reply to Stephen Hemminger

Need to delete NAPI association if vmbus_open fails.

---
 drivers/net/hyperv/netvsc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/hyperv/netvsc.c +1 -1
index 686900d61374..ff97a85b2e9d 100644
@@ -1286,7 +1286,6 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
 			 netvsc_channel_cb, net_device->chan_table);
 
 	if (ret != 0) {
-		netif_napi_del(&net_device->chan_table[0].napi);
 		netdev_err(ndev, "unable to open channel: %d\n", ret);
 		goto cleanup;
 	}
@@ -1319,6 +1318,7 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
 	vmbus_close(device->channel);
 
 cleanup:
+	netif_napi_del(&net_device->chan_table[0].napi);
 	free_netvsc_device(&net_device->rcu);
 
 	return ERR_PTR(ret);
Signature · 1 line
2.16.1
#4 lore raw

PATCH v2 4/9 hv_netvsc: cancel subchannel setup before halting device

In reply to Stephen Hemminger

Block setup of multiple channels earlier in the teardown process. This avoids possible races between halt and subchannel initialization.

---
 drivers/net/hyperv/rndis_filter.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/hyperv/rndis_filter.c +3 -0
index c3ca191fea7f..1cba767c6453 100644
@@ -1340,6 +1340,9 @@ void rndis_filter_device_remove(struct hv_device *dev,
 {
 	struct rndis_device *rndis_dev = net_dev->extension;
 
+	/* Don't try and setup sub channels if about to halt */
+	cancel_work_sync(&net_dev->subchan_work);
+
 	/* Halt and release the rndis device */
 	rndis_filter_halt_device(rndis_dev);
 
Signature · 1 line
2.16.1
#5 lore raw

PATCH v2 5/9 hv_netvsc: fix race in napi poll when rescheduling

In reply to Stephen Hemminger

There is a race between napi_reschedule and re-enabling interrupts which could lead to missed host interrrupts. This occurs when interrupts are re-enabled (hv_end_read) and vmbus irq callback (netvsc_channel_cb) has already scheduled NAPI.

---
 drivers/net/hyperv/netvsc.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/hyperv/netvsc.c +3 -2
index ff97a85b2e9d..4237cedc4f08 100644
@@ -1207,9 +1207,10 @@ int netvsc_poll(struct napi_struct *napi, int budget)
 	if (send_recv_completions(ndev, net_device, nvchan) == 0 &&
 	    work_done < budget &&
 	    napi_complete_done(napi, work_done) &&
-	    hv_end_read(&channel->inbound)) {
+	    hv_end_read(&channel->inbound) &&
+	    napi_schedule_prep(napi)) {
 		hv_begin_read(&channel->inbound);
-		napi_reschedule(napi);
+		__napi_schedule(napi);
 	}
 
 	/* Driver may overshoot since multiple packets per descriptor */
Signature · 1 line
2.16.1
#6 lore raw

PATCH v2 6/9 hv_netvsc: use napi_schedule_irqoff

In reply to Stephen Hemminger

Since the netvsc_channel_cb is already called in interrupt context from vmbus, there is no need to do irqsave/restore.

---
 drivers/net/hyperv/netvsc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/hyperv/netvsc.c +1 -1
index 4237cedc4f08..0265d703eb03 100644
@@ -1233,7 +1233,7 @@ void netvsc_channel_cb(void *context)
 		/* disable interupts from host */
 		hv_begin_read(rbi);
 
-		__napi_schedule(&nvchan->napi);
+		__napi_schedule_irqoff(&nvchan->napi);
 	}
 }
 
Signature · 1 line
2.16.1
#7 lore raw

PATCH v2 7/9 hv_netvsc: defer queue selection to VF

In reply to Stephen Hemminger

When VF is used for accelerated networking it will likely have more queues (and different policy) than the synthetic NIC. This patch defers the queue policy to the VF so that all the queues can be used. This impacts workloads like local generate UDP.

---
 drivers/net/hyperv/netvsc_drv.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/net/hyperv/netvsc_drv.c +13 -2
index fa6cf18e7719..5299cfb16ce2 100644
@@ -298,8 +298,19 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
 	rcu_read_lock();
 	vf_netdev = rcu_dereference(ndc->vf_netdev);
 	if (vf_netdev) {
-		txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
-		qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
+		const struct net_device_ops *vf_ops = vf_netdev->netdev_ops;
+
+		if (vf_ops->ndo_select_queue)
+			txq = vf_ops->ndo_select_queue(vf_netdev, skb,
+						       accel_priv, fallback);
+		else
+			txq = fallback(vf_netdev, skb);
+
+		/* Record the queue selected by VF so that it can be
+		 * used for common case where VF has more queues than
+		 * the synthetic device.
+		 */
+		qdisc_skb_cb(skb)->slave_dev_queue_mapping = txq;
 	} else {
 		txq = netvsc_pick_tx(ndev, skb);
 	}
Signature · 1 line
2.16.1
#8 lore raw

PATCH v2 8/9 hv_netvsc: filter multicast/broadcast

In reply to Stephen Hemminger

The netvsc driver was always enabling all multicast and broadcast even if netdevice flag had not enabled it.

---
 drivers/net/hyperv/rndis_filter.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)
drivers/net/hyperv/rndis_filter.c +12 -8
index 1cba767c6453..8927c483c217 100644
@@ -854,15 +854,19 @@ static void rndis_set_multicast(struct work_struct *w)
 {
 	struct rndis_device *rdev
 		= container_of(w, struct rndis_device, mcast_work);
+	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
+	unsigned int flags = rdev->ndev->flags;
 
-	if (rdev->ndev->flags & IFF_PROMISC)
-		rndis_filter_set_packet_filter(rdev,
-					       NDIS_PACKET_TYPE_PROMISCUOUS);
-	else
-		rndis_filter_set_packet_filter(rdev,
-					       NDIS_PACKET_TYPE_BROADCAST |
-					       NDIS_PACKET_TYPE_ALL_MULTICAST |
-					       NDIS_PACKET_TYPE_DIRECTED);
+	if (flags & IFF_PROMISC) {
+		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
+	} else {
+		if (flags & IFF_ALLMULTI)
+			flags |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+		if (flags & IFF_BROADCAST)
+			flags |= NDIS_PACKET_TYPE_BROADCAST;
+	}
+
+	rndis_filter_set_packet_filter(rdev, filter);
 }
 
 void rndis_filter_update(struct netvsc_device *nvdev)
Signature · 1 line
2.16.1
#9 lore raw

PATCH v2 9/9 hv_netvsc: propagate rx filters to VF

In reply to Stephen Hemminger

The netvsc device should propagate filters to the SR-IOV VF device (if present). The flags also need to be propagated to the VF device as well. This only really matters on local Hyper-V since Azure does not support multiple addresses.

---
 drivers/net/hyperv/netvsc_drv.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)
drivers/net/hyperv/netvsc_drv.c +36 -4
index 5299cfb16ce2..cdb78eefab67 100644
@@ -66,10 +66,36 @@ static int debug = -1;
 module_param(debug, int, S_IRUGO);
 MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
 
-static void netvsc_set_multicast_list(struct net_device *net)
+static void netvsc_change_rx_flags(struct net_device *net, int change)
 {
-	struct net_device_context *net_device_ctx = netdev_priv(net);
-	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
+	struct net_device_context *ndev_ctx = netdev_priv(net);
+	struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
+	int inc;
+
+	if (!vf_netdev)
+		return;
+
+	if (change & IFF_PROMISC) {
+		inc = (net->flags & IFF_PROMISC) ? 1 : -1;
+		dev_set_promiscuity(vf_netdev, inc);
+	}
+
+	if (change & IFF_ALLMULTI) {
+		inc = (net->flags & IFF_ALLMULTI) ? 1 : -1;
+		dev_set_allmulti(vf_netdev, inc);
+	}
+}
+
+static void netvsc_set_rx_mode(struct net_device *net)
+{
+	struct net_device_context *ndev_ctx = netdev_priv(net);
+	struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
+	struct netvsc_device *nvdev = rtnl_dereference(ndev_ctx->nvdev);
+
+	if (vf_netdev) {
+		dev_uc_sync(vf_netdev, net);
+		dev_mc_sync(vf_netdev, net);
+	}
 
 	rndis_filter_update(nvdev);
 }
@@ -1586,7 +1612,8 @@ static const struct net_device_ops device_ops = {
 	.ndo_open =			netvsc_open,
 	.ndo_stop =			netvsc_close,
 	.ndo_start_xmit =		netvsc_start_xmit,
-	.ndo_set_rx_mode =		netvsc_set_multicast_list,
+	.ndo_change_rx_flags =		netvsc_change_rx_flags,
+	.ndo_set_rx_mode =		netvsc_set_rx_mode,
 	.ndo_change_mtu =		netvsc_change_mtu,
 	.ndo_validate_addr =		eth_validate_addr,
 	.ndo_set_mac_address =		netvsc_set_mac_addr,
@@ -1817,6 +1844,11 @@ static void __netvsc_vf_setup(struct net_device *ndev,
 		netdev_warn(vf_netdev,
 			    "unable to change mtu to %u\n", ndev->mtu);
 
+	/* set multicast etc flags on VF */
+	dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE);
+	dev_uc_sync(vf_netdev, ndev);
+	dev_mc_sync(vf_netdev, ndev);
+
 	if (netif_running(ndev)) {
 		ret = dev_open(vf_netdev);
 		if (ret)
Signature · 1 line
2.16.1
maintainer
#11 lore raw
In reply to Stephen Hemminger

From: Stephen Hemminger <[email protected]>
Date: Fri, 2 Mar 2018 13:49:00 -0800

Quoted 8 lines

These are improvements to netvsc driver. They aren't functionality changes so not targeting net-next; and they are not show stopper bugs that need to go to stable either.

v2
- drop the irq flags patch, defer it to net-next
- split the multicast filter flag patch out
- change propogate rx mode patch to handle startup of vf

Series applied, thanks Stephen.