diff options
Diffstat (limited to 'drivers/net/xen-netback/netback.c')
-rw-r--r-- | drivers/net/xen-netback/netback.c | 94 |
1 files changed, 64 insertions, 30 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 8c20935d72c9..0071f211a08a 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c | |||
@@ -354,6 +354,49 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head) | |||
354 | return false; | 354 | return false; |
355 | } | 355 | } |
356 | 356 | ||
357 | struct xenvif_count_slot_state { | ||
358 | unsigned long copy_off; | ||
359 | bool head; | ||
360 | }; | ||
361 | |||
362 | unsigned int xenvif_count_frag_slots(struct xenvif *vif, | ||
363 | unsigned long offset, unsigned long size, | ||
364 | struct xenvif_count_slot_state *state) | ||
365 | { | ||
366 | unsigned count = 0; | ||
367 | |||
368 | offset &= ~PAGE_MASK; | ||
369 | |||
370 | while (size > 0) { | ||
371 | unsigned long bytes; | ||
372 | |||
373 | bytes = PAGE_SIZE - offset; | ||
374 | |||
375 | if (bytes > size) | ||
376 | bytes = size; | ||
377 | |||
378 | if (start_new_rx_buffer(state->copy_off, bytes, state->head)) { | ||
379 | count++; | ||
380 | state->copy_off = 0; | ||
381 | } | ||
382 | |||
383 | if (state->copy_off + bytes > MAX_BUFFER_OFFSET) | ||
384 | bytes = MAX_BUFFER_OFFSET - state->copy_off; | ||
385 | |||
386 | state->copy_off += bytes; | ||
387 | |||
388 | offset += bytes; | ||
389 | size -= bytes; | ||
390 | |||
391 | if (offset == PAGE_SIZE) | ||
392 | offset = 0; | ||
393 | |||
394 | state->head = false; | ||
395 | } | ||
396 | |||
397 | return count; | ||
398 | } | ||
399 | |||
357 | /* | 400 | /* |
358 | * Figure out how many ring slots we're going to need to send @skb to | 401 | * Figure out how many ring slots we're going to need to send @skb to |
359 | * the guest. This function is essentially a dry run of | 402 | * the guest. This function is essentially a dry run of |
@@ -361,48 +404,39 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head) | |||
361 | */ | 404 | */ |
362 | unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) | 405 | unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) |
363 | { | 406 | { |
407 | struct xenvif_count_slot_state state; | ||
364 | unsigned int count; | 408 | unsigned int count; |
365 | int i, copy_off; | 409 | unsigned char *data; |
410 | unsigned i; | ||
366 | 411 | ||
367 | count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE); | 412 | state.head = true; |
413 | state.copy_off = 0; | ||
368 | 414 | ||
369 | copy_off = skb_headlen(skb) % PAGE_SIZE; | 415 | /* Slot for the first (partial) page of data. */ |
416 | count = 1; | ||
370 | 417 | ||
418 | /* Need a slot for the GSO prefix for GSO extra data? */ | ||
371 | if (skb_shinfo(skb)->gso_size) | 419 | if (skb_shinfo(skb)->gso_size) |
372 | count++; | 420 | count++; |
373 | 421 | ||
374 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | 422 | data = skb->data; |
375 | unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); | 423 | while (data < skb_tail_pointer(skb)) { |
376 | unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; | 424 | unsigned long offset = offset_in_page(data); |
377 | unsigned long bytes; | 425 | unsigned long size = PAGE_SIZE - offset; |
378 | |||
379 | offset &= ~PAGE_MASK; | ||
380 | |||
381 | while (size > 0) { | ||
382 | BUG_ON(offset >= PAGE_SIZE); | ||
383 | BUG_ON(copy_off > MAX_BUFFER_OFFSET); | ||
384 | |||
385 | bytes = PAGE_SIZE - offset; | ||
386 | |||
387 | if (bytes > size) | ||
388 | bytes = size; | ||
389 | 426 | ||
390 | if (start_new_rx_buffer(copy_off, bytes, 0)) { | 427 | if (data + size > skb_tail_pointer(skb)) |
391 | count++; | 428 | size = skb_tail_pointer(skb) - data; |
392 | copy_off = 0; | ||
393 | } | ||
394 | 429 | ||
395 | if (copy_off + bytes > MAX_BUFFER_OFFSET) | 430 | count += xenvif_count_frag_slots(vif, offset, size, &state); |
396 | bytes = MAX_BUFFER_OFFSET - copy_off; | ||
397 | 431 | ||
398 | copy_off += bytes; | 432 | data += size; |
433 | } | ||
399 | 434 | ||
400 | offset += bytes; | 435 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { |
401 | size -= bytes; | 436 | unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); |
437 | unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; | ||
402 | 438 | ||
403 | if (offset == PAGE_SIZE) | 439 | count += xenvif_count_frag_slots(vif, offset, size, &state); |
404 | offset = 0; | ||
405 | } | ||
406 | } | 440 | } |
407 | return count; | 441 | return count; |
408 | } | 442 | } |