diff options
author | Stephen Hemminger <shemminger@vyatta.com> | 2010-05-04 12:58:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-05-11 14:36:10 -0400 |
commit | b220f5f925b8938747bfe4a61e362d132bdd9544 (patch) | |
tree | a2611046dbe3b5de730e843a1fb2694d38aaf7a5 | |
parent | 6048718d719f460abba8eaff1c0122247b2c3d91 (diff) |
Staging: hv: add transmit flow control
Keep track of the number of pages sent over transmit and stop
before going over.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Acked-by: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-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 4124979835a..a6ca01025e4 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c | |||
@@ -43,6 +43,7 @@ | |||
43 | struct net_device_context { | 43 | struct net_device_context { |
44 | /* point back to our device context */ | 44 | /* point back to our device context */ |
45 | struct vm_device *device_ctx; | 45 | struct vm_device *device_ctx; |
46 | unsigned long avail; | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | struct netvsc_driver_context { | 49 | struct netvsc_driver_context { |
@@ -52,8 +53,10 @@ struct netvsc_driver_context { | |||
52 | struct netvsc_driver drv_obj; | 53 | struct netvsc_driver drv_obj; |
53 | }; | 54 | }; |
54 | 55 | ||
55 | /* Need at least MAX_SKB_FRAGS (18) + 1 | 56 | #define PACKET_PAGES_LOWATER 8 |
56 | to handle worst case fragmented packet */ | 57 | /* Need this many pages to handle worst case fragmented packet */ |
58 | #define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2) | ||
59 | |||
57 | static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1); | 60 | static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1); |
58 | module_param(ring_size, int, S_IRUGO); | 61 | module_param(ring_size, int, S_IRUGO); |
59 | MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); | 62 | MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); |
@@ -122,14 +125,13 @@ static void netvsc_xmit_completion(void *context) | |||
122 | 125 | ||
123 | if (skb) { | 126 | if (skb) { |
124 | struct net_device *net = skb->dev; | 127 | struct net_device *net = skb->dev; |
125 | dev_kfree_skb_any(skb); | 128 | struct net_device_context *net_device_ctx = netdev_priv(net); |
129 | unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2; | ||
126 | 130 | ||
127 | if (netif_queue_stopped(net)) { | 131 | dev_kfree_skb_any(skb); |
128 | DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...", | ||
129 | net); | ||
130 | 132 | ||
131 | netif_wake_queue(net); | 133 | if ((net_device_ctx->avail += num_pages) >= PACKET_PAGES_HIWATER) |
132 | } | 134 | netif_wake_queue(net); |
133 | } | 135 | } |
134 | 136 | ||
135 | DPRINT_EXIT(NETVSC_DRV); | 137 | DPRINT_EXIT(NETVSC_DRV); |
@@ -146,7 +148,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
146 | struct hv_netvsc_packet *packet; | 148 | struct hv_netvsc_packet *packet; |
147 | int ret; | 149 | int ret; |
148 | unsigned int i, num_pages; | 150 | unsigned int i, num_pages; |
149 | int retries = 0; | ||
150 | 151 | ||
151 | DPRINT_ENTER(NETVSC_DRV); | 152 | DPRINT_ENTER(NETVSC_DRV); |
152 | 153 | ||
@@ -155,14 +156,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
155 | 156 | ||
156 | /* Add 1 for skb->data and additional one for RNDIS */ | 157 | /* Add 1 for skb->data and additional one for RNDIS */ |
157 | num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; | 158 | num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; |
159 | if (num_pages > net_device_ctx->avail) | ||
160 | return NETDEV_TX_BUSY; | ||
158 | 161 | ||
159 | /* Allocate a netvsc packet based on # of frags. */ | 162 | /* Allocate a netvsc packet based on # of frags. */ |
160 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | 163 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + |
161 | (num_pages * sizeof(struct hv_page_buffer)) + | 164 | (num_pages * sizeof(struct hv_page_buffer)) + |
162 | net_drv_obj->RequestExtSize, GFP_ATOMIC); | 165 | net_drv_obj->RequestExtSize, GFP_ATOMIC); |
163 | if (!packet) { | 166 | if (!packet) { |
167 | /* out of memory, silently drop packet */ | ||
164 | DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); | 168 | DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); |
165 | return -1; | 169 | |
170 | dev_kfree_skb(skb); | ||
171 | net->stats.tx_dropped++; | ||
172 | return NETDEV_TX_OK; | ||
166 | } | 173 | } |
167 | 174 | ||
168 | packet->Extension = (void *)(unsigned long)packet + | 175 | packet->Extension = (void *)(unsigned long)packet + |
@@ -198,52 +205,26 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
198 | packet->Completion.Send.SendCompletionContext = packet; | 205 | packet->Completion.Send.SendCompletionContext = packet; |
199 | packet->Completion.Send.SendCompletionTid = (unsigned long)skb; | 206 | packet->Completion.Send.SendCompletionTid = (unsigned long)skb; |
200 | 207 | ||
201 | retry_send: | ||
202 | ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, | 208 | ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, |
203 | packet); | 209 | packet); |
204 | |||
205 | if (ret == 0) { | 210 | if (ret == 0) { |
206 | ret = NETDEV_TX_OK; | ||
207 | net->stats.tx_bytes += skb->len; | 211 | net->stats.tx_bytes += skb->len; |
208 | net->stats.tx_packets++; | 212 | net->stats.tx_packets++; |
209 | } else { | ||
210 | retries++; | ||
211 | if (retries < 4) { | ||
212 | DPRINT_ERR(NETVSC_DRV, "unable to send..." | ||
213 | "retrying %d...", retries); | ||
214 | udelay(100); | ||
215 | goto retry_send; | ||
216 | } | ||
217 | 213 | ||
218 | /* no more room or we are shutting down */ | 214 | DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", |
219 | DPRINT_ERR(NETVSC_DRV, "unable to send (%d)..." | 215 | net->stats.tx_packets, |
220 | "marking net device (%p) busy", ret, net); | 216 | net->stats.tx_bytes); |
221 | DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net); | ||
222 | 217 | ||
223 | ret = NETDEV_TX_BUSY; | 218 | if ((net_device_ctx->avail -= num_pages) < PACKET_PAGES_LOWATER) |
219 | netif_stop_queue(net); | ||
220 | } else { | ||
221 | /* we are shutting down or bus overloaded, just drop packet */ | ||
224 | net->stats.tx_dropped++; | 222 | net->stats.tx_dropped++; |
225 | 223 | netvsc_xmit_completion(packet); | |
226 | netif_stop_queue(net); | ||
227 | |||
228 | /* | ||
229 | * Null it since the caller will free it instead of the | ||
230 | * completion routine | ||
231 | */ | ||
232 | packet->Completion.Send.SendCompletionTid = 0; | ||
233 | |||
234 | /* | ||
235 | * Release the resources since we will not get any send | ||
236 | * completion | ||
237 | */ | ||
238 | netvsc_xmit_completion((void *)packet); | ||
239 | } | 224 | } |
240 | 225 | ||
241 | DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", | ||
242 | net->stats.tx_packets, | ||
243 | net->stats.tx_bytes); | ||
244 | |||
245 | DPRINT_EXIT(NETVSC_DRV); | 226 | DPRINT_EXIT(NETVSC_DRV); |
246 | return ret; | 227 | return NETDEV_TX_OK; |
247 | } | 228 | } |
248 | 229 | ||
249 | /* | 230 | /* |
@@ -382,6 +363,7 @@ static int netvsc_probe(struct device *device) | |||
382 | 363 | ||
383 | net_device_ctx = netdev_priv(net); | 364 | net_device_ctx = netdev_priv(net); |
384 | net_device_ctx->device_ctx = device_ctx; | 365 | net_device_ctx->device_ctx = device_ctx; |
366 | net_device_ctx->avail = ring_size; | ||
385 | dev_set_drvdata(device, net); | 367 | dev_set_drvdata(device, net); |
386 | 368 | ||
387 | /* Notify the netvsc driver of the new device */ | 369 | /* Notify the netvsc driver of the new device */ |