aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netback/netback.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/xen-netback/netback.c')
-rw-r--r--drivers/net/xen-netback/netback.c94
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
357struct xenvif_count_slot_state {
358 unsigned long copy_off;
359 bool head;
360};
361
362unsigned 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 */
362unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) 405unsigned 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}