Skip to content

Commit

Permalink
hv_netvsc: Add support for LRO/RSC in the vSwitch
Browse files Browse the repository at this point in the history
LRO/RSC in the vSwitch is a feature available in Windows Server 2019
hosts and later. It reduces the per packet processing overhead by
coalescing multiple TCP segments when possible. This patch adds netvsc
driver support for this feature.

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Haiyang Zhang authored and David S. Miller committed Sep 23, 2018
1 parent bd4d08d commit c8e4eff
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 38 deletions.
47 changes: 40 additions & 7 deletions drivers/net/hyperv/hyperv_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ struct rndis_device {
/* Interface */
struct rndis_message;
struct netvsc_device;
struct netvsc_channel;
struct net_device_context;

extern u32 netvsc_ring_bytes;
Expand All @@ -203,10 +204,7 @@ void netvsc_linkstatus_callback(struct net_device *net,
struct rndis_message *resp);
int netvsc_recv_callback(struct net_device *net,
struct netvsc_device *nvdev,
struct vmbus_channel *channel,
void *data, u32 len,
const struct ndis_tcp_ip_checksum_info *csum_info,
const struct ndis_pkt_8021q_info *vlan);
struct netvsc_channel *nvchan);
void netvsc_channel_cb(void *context);
int netvsc_poll(struct napi_struct *napi, int budget);

Expand All @@ -222,7 +220,7 @@ int rndis_filter_set_rss_param(struct rndis_device *rdev,
const u8 *key);
int rndis_filter_receive(struct net_device *ndev,
struct netvsc_device *net_dev,
struct vmbus_channel *channel,
struct netvsc_channel *nvchan,
void *data, u32 buflen);

int rndis_filter_set_device_mac(struct netvsc_device *ndev,
Expand Down Expand Up @@ -524,6 +522,8 @@ struct nvsp_2_vsc_capability {
u64 ieee8021q:1;
u64 correlation_id:1;
u64 teaming:1;
u64 vsubnetid:1;
u64 rsc:1;
};
};
} __packed;
Expand Down Expand Up @@ -826,7 +826,7 @@ struct nvsp_message {

#define NETVSC_SUPPORTED_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | \
NETIF_F_TSO | NETIF_F_IPV6_CSUM | \
NETIF_F_TSO6)
NETIF_F_TSO6 | NETIF_F_LRO)

#define VRSS_SEND_TAB_SIZE 16 /* must be power of 2 */
#define VRSS_CHANNEL_MAX 64
Expand All @@ -852,6 +852,18 @@ struct multi_recv_comp {
u32 next; /* next entry for writing */
};

#define NVSP_RSC_MAX 562 /* Max #RSC frags in a vmbus xfer page pkt */

struct nvsc_rsc {
const struct ndis_pkt_8021q_info *vlan;
const struct ndis_tcp_ip_checksum_info *csum_info;
u8 is_last; /* last RNDIS msg in a vmtransfer_page */
u32 cnt; /* #fragments in an RSC packet */
u32 pktlen; /* Full packet length */
void *data[NVSP_RSC_MAX];
u32 len[NVSP_RSC_MAX];
};

struct netvsc_stats {
u64 packets;
u64 bytes;
Expand Down Expand Up @@ -955,6 +967,7 @@ struct netvsc_channel {
struct multi_send_data msd;
struct multi_recv_comp mrc;
atomic_t queue_sends;
struct nvsc_rsc rsc;

struct netvsc_stats tx_stats;
struct netvsc_stats rx_stats;
Expand Down Expand Up @@ -1136,7 +1149,8 @@ struct rndis_oobd {
/* Packet extension field contents associated with a Data message. */
struct rndis_per_packet_info {
u32 size;
u32 type;
u32 type:31;
u32 internal:1;
u32 ppi_offset;
};

Expand All @@ -1157,6 +1171,25 @@ enum ndis_per_pkt_info_type {
MAX_PER_PKT_INFO
};

enum rndis_per_pkt_info_interal_type {
RNDIS_PKTINFO_ID = 1,
/* Add more memebers here */

RNDIS_PKTINFO_MAX
};

#define RNDIS_PKTINFO_SUBALLOC BIT(0)
#define RNDIS_PKTINFO_1ST_FRAG BIT(1)
#define RNDIS_PKTINFO_LAST_FRAG BIT(2)

#define RNDIS_PKTINFO_ID_V1 1

struct rndis_pktinfo_id {
u8 ver;
u8 flag;
u16 pkt_id;
};

struct ndis_pkt_8021q_info {
union {
struct {
Expand Down
18 changes: 13 additions & 5 deletions drivers/net/hyperv/netvsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ static int negotiate_nvsp_ver(struct hv_device *device,
init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1;
}

if (nvsp_ver >= NVSP_PROTOCOL_VERSION_61)
init_packet->msg.v2_msg.send_ndis_config.capability.rsc = 1;

trace_nvsp_send(ndev, init_packet);

ret = vmbus_sendpacket(device->channel, init_packet,
Expand Down Expand Up @@ -1111,11 +1114,12 @@ static void enq_receive_complete(struct net_device *ndev,

static int netvsc_receive(struct net_device *ndev,
struct netvsc_device *net_device,
struct vmbus_channel *channel,
struct netvsc_channel *nvchan,
const struct vmpacket_descriptor *desc,
const struct nvsp_message *nvsp)
{
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct vmbus_channel *channel = nvchan->channel;
const struct vmtransfer_page_packet_header *vmxferpage_packet
= container_of(desc, const struct vmtransfer_page_packet_header, d);
u16 q_idx = channel->offermsg.offer.sub_channel_index;
Expand Down Expand Up @@ -1150,6 +1154,7 @@ static int netvsc_receive(struct net_device *ndev,
int ret;

if (unlikely(offset + buflen > net_device->recv_buf_size)) {
nvchan->rsc.cnt = 0;
status = NVSP_STAT_FAIL;
netif_err(net_device_ctx, rx_err, ndev,
"Packet offset:%u + len:%u too big\n",
Expand All @@ -1160,11 +1165,13 @@ static int netvsc_receive(struct net_device *ndev,

data = recv_buf + offset;

nvchan->rsc.is_last = (i == count - 1);

trace_rndis_recv(ndev, q_idx, data);

/* Pass it to the upper layer */
ret = rndis_filter_receive(ndev, net_device,
channel, data, buflen);
nvchan, data, buflen);

if (unlikely(ret != NVSP_STAT_SUCCESS))
status = NVSP_STAT_FAIL;
Expand Down Expand Up @@ -1223,12 +1230,13 @@ static void netvsc_receive_inband(struct net_device *ndev,
}

static int netvsc_process_raw_pkt(struct hv_device *device,
struct vmbus_channel *channel,
struct netvsc_channel *nvchan,
struct netvsc_device *net_device,
struct net_device *ndev,
const struct vmpacket_descriptor *desc,
int budget)
{
struct vmbus_channel *channel = nvchan->channel;
const struct nvsp_message *nvmsg = hv_pkt_data(desc);

trace_nvsp_recv(ndev, channel, nvmsg);
Expand All @@ -1240,7 +1248,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device,
break;

case VM_PKT_DATA_USING_XFER_PAGES:
return netvsc_receive(ndev, net_device, channel,
return netvsc_receive(ndev, net_device, nvchan,
desc, nvmsg);
break;

Expand Down Expand Up @@ -1284,7 +1292,7 @@ int netvsc_poll(struct napi_struct *napi, int budget)
nvchan->desc = hv_pkt_iter_first(channel);

while (nvchan->desc && work_done < budget) {
work_done += netvsc_process_raw_pkt(device, channel, net_device,
work_done += netvsc_process_raw_pkt(device, nvchan, net_device,
ndev, nvchan->desc, budget);
nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc);
}
Expand Down
28 changes: 14 additions & 14 deletions drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,22 +744,25 @@ void netvsc_linkstatus_callback(struct net_device *net,
}

static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
struct napi_struct *napi,
const struct ndis_tcp_ip_checksum_info *csum_info,
const struct ndis_pkt_8021q_info *vlan,
void *data, u32 buflen)
struct netvsc_channel *nvchan)
{
struct napi_struct *napi = &nvchan->napi;
const struct ndis_pkt_8021q_info *vlan = nvchan->rsc.vlan;
const struct ndis_tcp_ip_checksum_info *csum_info =
nvchan->rsc.csum_info;
struct sk_buff *skb;
int i;

skb = napi_alloc_skb(napi, buflen);
skb = napi_alloc_skb(napi, nvchan->rsc.pktlen);
if (!skb)
return skb;

/*
* Copy to skb. This copy is needed here since the memory pointed by
* hv_netvsc_packet cannot be deallocated
*/
skb_put_data(skb, data, buflen);
for (i = 0; i < nvchan->rsc.cnt; i++)
skb_put_data(skb, nvchan->rsc.data[i], nvchan->rsc.len[i]);

skb->protocol = eth_type_trans(skb, net);

Expand Down Expand Up @@ -792,23 +795,20 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
*/
int netvsc_recv_callback(struct net_device *net,
struct netvsc_device *net_device,
struct vmbus_channel *channel,
void *data, u32 len,
const struct ndis_tcp_ip_checksum_info *csum_info,
const struct ndis_pkt_8021q_info *vlan)
struct netvsc_channel *nvchan)
{
struct net_device_context *net_device_ctx = netdev_priv(net);
struct vmbus_channel *channel = nvchan->channel;
u16 q_idx = channel->offermsg.offer.sub_channel_index;
struct netvsc_channel *nvchan = &net_device->chan_table[q_idx];
struct sk_buff *skb;
struct netvsc_stats *rx_stats;

if (net->reg_state != NETREG_REGISTERED)
return NVSP_STAT_FAIL;

/* Allocate a skb - TODO direct I/O to pages? */
skb = netvsc_alloc_recv_skb(net, &nvchan->napi,
csum_info, vlan, data, len);
skb = netvsc_alloc_recv_skb(net, nvchan);

if (unlikely(!skb)) {
++net_device_ctx->eth_stats.rx_no_memory;
rcu_read_unlock();
Expand All @@ -825,7 +825,7 @@ int netvsc_recv_callback(struct net_device *net,
rx_stats = &nvchan->rx_stats;
u64_stats_update_begin(&rx_stats->syncp);
rx_stats->packets++;
rx_stats->bytes += len;
rx_stats->bytes += nvchan->rsc.pktlen;

if (skb->pkt_type == PACKET_BROADCAST)
++rx_stats->broadcast;
Expand Down
Loading

0 comments on commit c8e4eff

Please sign in to comment.