diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/hv/netvsc_drv.c | 72 |
1 files changed, 27 insertions, 45 deletions
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c index 4124979..a6ca010 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -43,6 +43,7 @@ struct net_device_context { /* point back to our device context */ struct vm_device *device_ctx; + unsigned long avail; }; struct netvsc_driver_context { @@ -52,8 +53,10 @@ struct netvsc_driver_context { struct netvsc_driver drv_obj; }; -/* Need at least MAX_SKB_FRAGS (18) + 1 - to handle worst case fragmented packet */ +#define PACKET_PAGES_LOWATER 8 +/* 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); module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); @@ -122,14 +125,13 @@ static void netvsc_xmit_completion(void *context) if (skb) { 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)) { - DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...", - net); + dev_kfree_skb_any(skb); - netif_wake_queue(net); - } + if ((net_device_ctx->avail += num_pages) >= PACKET_PAGES_HIWATER) + netif_wake_queue(net); } DPRINT_EXIT(NETVSC_DRV); @@ -146,7 +148,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) struct hv_netvsc_packet *packet; int ret; unsigned int i, num_pages; - int retries = 0; DPRINT_ENTER(NETVSC_DRV); @@ -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 */ 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. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)) + net_drv_obj->RequestExtSize, GFP_ATOMIC); if (!packet) { + /* out of memory, silently drop 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 + @@ -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.SendCompletionTid = (unsigned long)skb; -retry_send: ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, packet); - if (ret == 0) { - ret = NETDEV_TX_OK; net->stats.tx_bytes += skb->len; 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_ERR(NETVSC_DRV, "unable to send (%d)..." - "marking net device (%p) busy", ret, net); - DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net); + DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", + net->stats.tx_packets, + net->stats.tx_bytes); - 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++; - - 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); + netvsc_xmit_completion(packet); } - DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", - net->stats.tx_packets, - net->stats.tx_bytes); - DPRINT_EXIT(NETVSC_DRV); - return ret; + return NETDEV_TX_OK; } /* @@ -382,6 +363,7 @@ static int netvsc_probe(struct device *device) net_device_ctx = netdev_priv(net); net_device_ctx->device_ctx = device_ctx; + net_device_ctx->avail = ring_size; dev_set_drvdata(device, net); /* Notify the netvsc driver of the new device */ |