Commit b08cc791 authored by KY Srinivasan's avatar KY Srinivasan Committed by David S. Miller

hv_netvsc: Eliminate memory allocation in the packet send path

The network protocol used to communicate with the host is the remote ndis (rndis)
protocol. We need to decorate each outgoing packet with a rndis header and
additional rndis state (rndis per-packet state). To manage this state, we
currently allocate memory in the transmit path. Eliminate this allocation by
requesting additional head room in the skb.
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cbacec76
...@@ -128,6 +128,7 @@ struct ndis_tcp_ip_checksum_info; ...@@ -128,6 +128,7 @@ struct ndis_tcp_ip_checksum_info;
struct hv_netvsc_packet { struct hv_netvsc_packet {
/* Bookkeeping stuff */ /* Bookkeeping stuff */
u32 status; u32 status;
bool part_of_skb;
struct hv_device *device; struct hv_device *device;
bool is_data_pkt; bool is_data_pkt;
...@@ -150,7 +151,7 @@ struct hv_netvsc_packet { ...@@ -150,7 +151,7 @@ struct hv_netvsc_packet {
/* Points to the send/receive buffer where the ethernet frame is */ /* Points to the send/receive buffer where the ethernet frame is */
void *data; void *data;
u32 page_buf_cnt; u32 page_buf_cnt;
struct hv_page_buffer page_buf[0]; struct hv_page_buffer *page_buf;
}; };
struct netvsc_device_info { struct netvsc_device_info {
......
...@@ -866,11 +866,15 @@ int netvsc_send(struct hv_device *device, ...@@ -866,11 +866,15 @@ int netvsc_send(struct hv_device *device,
netvsc_copy_to_send_buf(net_device, netvsc_copy_to_send_buf(net_device,
section_index, msd_len, section_index, msd_len,
packet); packet);
if (!packet->part_of_skb) {
skb = (struct sk_buff *) skb = (struct sk_buff *)
(unsigned long)packet->send_completion_tid; (unsigned long)
packet->send_completion_tid;
packet->page_buf_cnt = 0;
packet->send_completion_tid = 0; packet->send_completion_tid = 0;
}
packet->page_buf_cnt = 0;
packet->send_buf_index = section_index; packet->send_buf_index = section_index;
packet->total_data_buflen += msd_len; packet->total_data_buflen += msd_len;
......
...@@ -235,6 +235,7 @@ static void netvsc_xmit_completion(void *context) ...@@ -235,6 +235,7 @@ static void netvsc_xmit_completion(void *context)
struct sk_buff *skb = (struct sk_buff *) struct sk_buff *skb = (struct sk_buff *)
(unsigned long)packet->send_completion_tid; (unsigned long)packet->send_completion_tid;
if (!packet->part_of_skb)
kfree(packet); kfree(packet);
if (skb) if (skb)
...@@ -383,6 +384,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -383,6 +384,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
u32 net_trans_info; u32 net_trans_info;
u32 hash; u32 hash;
u32 skb_length = skb->len; u32 skb_length = skb->len;
u32 head_room = skb_headroom(skb);
u32 pkt_sz;
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
/* We will atmost need two pages to describe the rndis /* We will atmost need two pages to describe the rndis
...@@ -397,24 +401,32 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -397,24 +401,32 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
/* Allocate a netvsc packet based on # of frags. */ pkt_sz = sizeof(struct hv_netvsc_packet) +
packet = kzalloc(sizeof(struct hv_netvsc_packet) +
(num_data_pgs * sizeof(struct hv_page_buffer)) +
sizeof(struct rndis_message) + sizeof(struct rndis_message) +
NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE, GFP_ATOMIC); NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
if (head_room < pkt_sz) {
packet = kmalloc(pkt_sz, GFP_ATOMIC);
if (!packet) { if (!packet) {
/* out of memory, drop packet */ /* out of memory, drop packet */
netdev_err(net, "unable to allocate hv_netvsc_packet\n"); netdev_err(net, "unable to alloc hv_netvsc_packet\n");
dev_kfree_skb(skb); dev_kfree_skb(skb);
net->stats.tx_dropped++; net->stats.tx_dropped++;
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
packet->part_of_skb = false;
} else {
/* Use the headroom for building up the packet */
packet = (struct hv_netvsc_packet *)skb->head;
packet->part_of_skb = true;
}
packet->status = 0;
packet->xmit_more = skb->xmit_more; packet->xmit_more = skb->xmit_more;
packet->vlan_tci = skb->vlan_tci; packet->vlan_tci = skb->vlan_tci;
packet->page_buf = page_buf;
packet->q_idx = skb_get_queue_mapping(skb); packet->q_idx = skb_get_queue_mapping(skb);
...@@ -422,8 +434,13 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -422,8 +434,13 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
packet->total_data_buflen = skb->len; packet->total_data_buflen = skb->len;
packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
sizeof(struct hv_netvsc_packet) + sizeof(struct hv_netvsc_packet));
(num_data_pgs * sizeof(struct hv_page_buffer)));
memset(packet->rndis_msg, 0, sizeof(struct rndis_message) +
NDIS_VLAN_PPI_SIZE +
NDIS_CSUM_PPI_SIZE +
NDIS_LSO_PPI_SIZE +
NDIS_HASH_PPI_SIZE);
/* Set the completion routine */ /* Set the completion routine */
packet->send_completion = netvsc_xmit_completion; packet->send_completion = netvsc_xmit_completion;
...@@ -555,7 +572,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -555,7 +572,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
rndis_msg->msg_len += rndis_msg_size; rndis_msg->msg_len += rndis_msg_size;
packet->total_data_buflen = rndis_msg->msg_len; packet->total_data_buflen = rndis_msg->msg_len;
packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
skb, &packet->page_buf[0]); skb, &page_buf[0]);
ret = netvsc_send(net_device_ctx->device_ctx, packet); ret = netvsc_send(net_device_ctx->device_ctx, packet);
...@@ -564,6 +581,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -564,6 +581,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
net->stats.tx_bytes += skb_length; net->stats.tx_bytes += skb_length;
net->stats.tx_packets++; net->stats.tx_packets++;
} else { } else {
if (!packet->part_of_skb)
kfree(packet); kfree(packet);
if (ret != -EAGAIN) { if (ret != -EAGAIN) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -846,12 +864,18 @@ static int netvsc_probe(struct hv_device *dev, ...@@ -846,12 +864,18 @@ static int netvsc_probe(struct hv_device *dev,
struct netvsc_device_info device_info; struct netvsc_device_info device_info;
struct netvsc_device *nvdev; struct netvsc_device *nvdev;
int ret; int ret;
u32 max_needed_headroom;
net = alloc_etherdev_mq(sizeof(struct net_device_context), net = alloc_etherdev_mq(sizeof(struct net_device_context),
num_online_cpus()); num_online_cpus());
if (!net) if (!net)
return -ENOMEM; return -ENOMEM;
max_needed_headroom = sizeof(struct hv_netvsc_packet) +
sizeof(struct rndis_message) +
NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
netif_carrier_off(net); netif_carrier_off(net);
net_device_ctx = netdev_priv(net); net_device_ctx = netdev_priv(net);
...@@ -870,6 +894,13 @@ static int netvsc_probe(struct hv_device *dev, ...@@ -870,6 +894,13 @@ static int netvsc_probe(struct hv_device *dev,
net->ethtool_ops = &ethtool_ops; net->ethtool_ops = &ethtool_ops;
SET_NETDEV_DEV(net, &dev->device); SET_NETDEV_DEV(net, &dev->device);
/*
* Request additional head room in the skb.
* We will use this space to build the rndis
* heaser and other state we need to maintain.
*/
net->needed_headroom = max_needed_headroom;
/* Notify the netvsc driver of the new device */ /* Notify the netvsc driver of the new device */
device_info.ring_size = ring_size; device_info.ring_size = ring_size;
ret = rndis_filter_device_add(dev, &device_info); ret = rndis_filter_device_add(dev, &device_info);
......
...@@ -210,6 +210,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, ...@@ -210,6 +210,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
{ {
int ret; int ret;
struct hv_netvsc_packet *packet; struct hv_netvsc_packet *packet;
struct hv_page_buffer page_buf[2];
/* Setup the packet to send it */ /* Setup the packet to send it */
packet = &req->pkt; packet = &req->pkt;
...@@ -217,6 +218,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, ...@@ -217,6 +218,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
packet->is_data_pkt = false; packet->is_data_pkt = false;
packet->total_data_buflen = req->request_msg.msg_len; packet->total_data_buflen = req->request_msg.msg_len;
packet->page_buf_cnt = 1; packet->page_buf_cnt = 1;
packet->page_buf = page_buf;
packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
PAGE_SHIFT; PAGE_SHIFT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment