diff options
Diffstat (limited to 'drivers/net/hyperv/netvsc.c')
| -rw-r--r-- | drivers/net/hyperv/netvsc.c | 55 | 
1 files changed, 51 insertions, 4 deletions
| diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index c64cc7639c39..9d07c9ce4be2 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -31,12 +31,13 @@   * Switch the data path from the synthetic interface to the VF   * interface.   */ -void netvsc_switch_datapath(struct net_device *ndev, bool vf) +int netvsc_switch_datapath(struct net_device *ndev, bool vf)  {  	struct net_device_context *net_device_ctx = netdev_priv(ndev);  	struct hv_device *dev = net_device_ctx->device_ctx;  	struct netvsc_device *nv_dev = rtnl_dereference(net_device_ctx->nvdev);  	struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt; +	int ret, retry = 0;  	/* Block sending traffic to VF if it's about to be gone */  	if (!vf) @@ -51,15 +52,41 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)  		init_pkt->msg.v4_msg.active_dp.active_datapath =  			NVSP_DATAPATH_SYNTHETIC; +again:  	trace_nvsp_send(ndev, init_pkt); -	vmbus_sendpacket(dev->channel, init_pkt, +	ret = vmbus_sendpacket(dev->channel, init_pkt,  			       sizeof(struct nvsp_message), -			       (unsigned long)init_pkt, -			       VM_PKT_DATA_INBAND, +			       (unsigned long)init_pkt, VM_PKT_DATA_INBAND,  			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + +	/* If failed to switch to/from VF, let data_path_is_vf stay false, +	 * so we use synthetic path to send data. +	 */ +	if (ret) { +		if (ret != -EAGAIN) { +			netdev_err(ndev, +				   "Unable to send sw datapath msg, err: %d\n", +				   ret); +			return ret; +		} + +		if (retry++ < RETRY_MAX) { +			usleep_range(RETRY_US_LO, RETRY_US_HI); +			goto again; +		} else { +			netdev_err( +				ndev, +				"Retry failed to send sw datapath msg, err: %d\n", +				ret); +			return ret; +		} +	} +  	wait_for_completion(&nv_dev->channel_init_wait);  	net_device_ctx->data_path_is_vf = vf; + +	return 0;  }  /* Worker to setup sub channels on initial setup @@ -1017,6 +1044,26 @@ static inline void move_pkt_msd(struct hv_netvsc_packet **msd_send,  }  /* RCU already held by caller */ +/* Batching/bouncing logic is designed to attempt to optimize + * performance. + * + * For small, non-LSO packets we copy the packet to a send buffer + * which is pre-registered with the Hyper-V side. This enables the + * hypervisor to avoid remapping the aperture to access the packet + * descriptor and data. + * + * If we already started using a buffer and the netdev is transmitting + * a burst of packets, keep on copying into the buffer until it is + * full or we are done collecting a burst. If there is an existing + * buffer with space for the RNDIS descriptor but not the packet, copy + * the RNDIS descriptor to the buffer, keeping the packet in place. + * + * If we do batching and send more than one packet using a single + * NetVSC message, free the SKBs of the packets copied, except for the + * last packet. This is done to streamline the handling of the case + * where the last packet only had the RNDIS descriptor copied to the + * send buffer, with the data pointers included in the NetVSC message. + */  int netvsc_send(struct net_device *ndev,  		struct hv_netvsc_packet *packet,  		struct rndis_message *rndis_msg, | 
