Commit b220f5f9 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Greg Kroah-Hartman

Staging: hv: add transmit flow control

Keep track of the number of pages sent over transmit and stop
before going over.
Signed-off-by: default avatarStephen Hemminger <shemminger@vyatta.com>
Acked-by: default avatarHank Janssen <hjanssen@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6048718d
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
struct net_device_context { struct net_device_context {
/* point back to our device context */ /* point back to our device context */
struct vm_device *device_ctx; struct vm_device *device_ctx;
unsigned long avail;
}; };
struct netvsc_driver_context { struct netvsc_driver_context {
...@@ -52,8 +53,10 @@ struct netvsc_driver_context { ...@@ -52,8 +53,10 @@ struct netvsc_driver_context {
struct netvsc_driver drv_obj; struct netvsc_driver drv_obj;
}; };
/* Need at least MAX_SKB_FRAGS (18) + 1 #define PACKET_PAGES_LOWATER 8
to handle worst case fragmented packet */ /* Need this many pages to handle worst case fragmented packet */
#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2)
static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1); static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1);
module_param(ring_size, int, S_IRUGO); module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
...@@ -122,14 +125,13 @@ static void netvsc_xmit_completion(void *context) ...@@ -122,14 +125,13 @@ static void netvsc_xmit_completion(void *context)
if (skb) { if (skb) {
struct net_device *net = skb->dev; struct net_device *net = skb->dev;
dev_kfree_skb_any(skb); struct net_device_context *net_device_ctx = netdev_priv(net);
unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2;
if (netif_queue_stopped(net)) { dev_kfree_skb_any(skb);
DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...",
net);
netif_wake_queue(net); if ((net_device_ctx->avail += num_pages) >= PACKET_PAGES_HIWATER)
} netif_wake_queue(net);
} }
DPRINT_EXIT(NETVSC_DRV); DPRINT_EXIT(NETVSC_DRV);
...@@ -146,7 +148,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -146,7 +148,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
struct hv_netvsc_packet *packet; struct hv_netvsc_packet *packet;
int ret; int ret;
unsigned int i, num_pages; unsigned int i, num_pages;
int retries = 0;
DPRINT_ENTER(NETVSC_DRV); DPRINT_ENTER(NETVSC_DRV);
...@@ -155,14 +156,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -155,14 +156,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
/* Add 1 for skb->data and additional one for RNDIS */ /* Add 1 for skb->data and additional one for RNDIS */
num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
if (num_pages > net_device_ctx->avail)
return NETDEV_TX_BUSY;
/* Allocate a netvsc packet based on # of frags. */ /* Allocate a netvsc packet based on # of frags. */
packet = kzalloc(sizeof(struct hv_netvsc_packet) + packet = kzalloc(sizeof(struct hv_netvsc_packet) +
(num_pages * sizeof(struct hv_page_buffer)) + (num_pages * sizeof(struct hv_page_buffer)) +
net_drv_obj->RequestExtSize, GFP_ATOMIC); net_drv_obj->RequestExtSize, GFP_ATOMIC);
if (!packet) { if (!packet) {
/* out of memory, silently drop packet */
DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet");
return -1;
dev_kfree_skb(skb);
net->stats.tx_dropped++;
return NETDEV_TX_OK;
} }
packet->Extension = (void *)(unsigned long)packet + packet->Extension = (void *)(unsigned long)packet +
...@@ -198,52 +205,26 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -198,52 +205,26 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
packet->Completion.Send.SendCompletionContext = packet; packet->Completion.Send.SendCompletionContext = packet;
packet->Completion.Send.SendCompletionTid = (unsigned long)skb; packet->Completion.Send.SendCompletionTid = (unsigned long)skb;
retry_send:
ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj,
packet); packet);
if (ret == 0) { if (ret == 0) {
ret = NETDEV_TX_OK;
net->stats.tx_bytes += skb->len; net->stats.tx_bytes += skb->len;
net->stats.tx_packets++; net->stats.tx_packets++;
} else {
retries++;
if (retries < 4) {
DPRINT_ERR(NETVSC_DRV, "unable to send..."
"retrying %d...", retries);
udelay(100);
goto retry_send;
}
/* no more room or we are shutting down */ DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
DPRINT_ERR(NETVSC_DRV, "unable to send (%d)..." net->stats.tx_packets,
"marking net device (%p) busy", ret, net); net->stats.tx_bytes);
DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net);
ret = NETDEV_TX_BUSY; if ((net_device_ctx->avail -= num_pages) < PACKET_PAGES_LOWATER)
netif_stop_queue(net);
} else {
/* we are shutting down or bus overloaded, just drop packet */
net->stats.tx_dropped++; net->stats.tx_dropped++;
netvsc_xmit_completion(packet);
netif_stop_queue(net);
/*
* Null it since the caller will free it instead of the
* completion routine
*/
packet->Completion.Send.SendCompletionTid = 0;
/*
* Release the resources since we will not get any send
* completion
*/
netvsc_xmit_completion((void *)packet);
} }
DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
net->stats.tx_packets,
net->stats.tx_bytes);
DPRINT_EXIT(NETVSC_DRV); DPRINT_EXIT(NETVSC_DRV);
return ret; return NETDEV_TX_OK;
} }
/* /*
...@@ -382,6 +363,7 @@ static int netvsc_probe(struct device *device) ...@@ -382,6 +363,7 @@ static int netvsc_probe(struct device *device)
net_device_ctx = netdev_priv(net); net_device_ctx = netdev_priv(net);
net_device_ctx->device_ctx = device_ctx; net_device_ctx->device_ctx = device_ctx;
net_device_ctx->avail = ring_size;
dev_set_drvdata(device, net); dev_set_drvdata(device, net);
/* Notify the netvsc driver of the new device */ /* Notify the netvsc driver of the new device */
......
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