diff options
| author | Thomas Graf <tgraf@suug.ch> | 2005-06-23 23:59:51 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2005-06-23 23:59:51 -0400 |
| commit | 677e90eda3bd8cfde0b748daaa46476162a03950 (patch) | |
| tree | 4b40614cf9cd125883d0430467be9172997ca184 | |
| parent | 6408f79cce401e1bfecf923e7156f84f96e021e3 (diff) | |
[NET]: Zerocopy sequential reading of skb data
Implements sequential reading for both linear and non-linear
skb data at zerocopy cost. The data is returned in chunks of
arbitary length, therefore random access is not possible.
Usage:
from := 0
to := 128
state := undef
data := undef
len := undef
consumed := 0
skb_prepare_seq_read(skb, from, to, &state)
while (len = skb_seq_read(consumed, &data, &state)) != 0 do
/* do something with 'data' of length 'len' */
if abort then
/* abort read if we don't wait for
* skb_seq_read() to return 0 */
skb_abort_seq_read(&state)
return
endif
/* not necessary to consume all of 'len' */
consumed += len
done
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/linux/skbuff.h | 18 | ||||
| -rw-r--r-- | net/core/skbuff.c | 117 |
2 files changed, 135 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d7c839a21842..171a37dff83a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
| @@ -321,6 +321,24 @@ extern void skb_over_panic(struct sk_buff *skb, int len, | |||
| 321 | extern void skb_under_panic(struct sk_buff *skb, int len, | 321 | extern void skb_under_panic(struct sk_buff *skb, int len, |
| 322 | void *here); | 322 | void *here); |
| 323 | 323 | ||
| 324 | struct skb_seq_state | ||
| 325 | { | ||
| 326 | __u32 lower_offset; | ||
| 327 | __u32 upper_offset; | ||
| 328 | __u32 frag_idx; | ||
| 329 | __u32 stepped_offset; | ||
| 330 | struct sk_buff *root_skb; | ||
| 331 | struct sk_buff *cur_skb; | ||
| 332 | __u8 *frag_data; | ||
| 333 | }; | ||
| 334 | |||
| 335 | extern void skb_prepare_seq_read(struct sk_buff *skb, | ||
| 336 | unsigned int from, unsigned int to, | ||
| 337 | struct skb_seq_state *st); | ||
| 338 | extern unsigned int skb_seq_read(unsigned int consumed, const u8 **data, | ||
| 339 | struct skb_seq_state *st); | ||
| 340 | extern void skb_abort_seq_read(struct skb_seq_state *st); | ||
| 341 | |||
| 324 | /* Internal */ | 342 | /* Internal */ |
| 325 | #define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) | 343 | #define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) |
| 326 | 344 | ||
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6d68c03bc051..d285f2f7e812 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
| @@ -1500,6 +1500,120 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) | |||
| 1500 | skb_split_no_header(skb, skb1, len, pos); | 1500 | skb_split_no_header(skb, skb1, len, pos); |
| 1501 | } | 1501 | } |
| 1502 | 1502 | ||
| 1503 | /** | ||
| 1504 | * skb_prepare_seq_read - Prepare a sequential read of skb data | ||
| 1505 | * @skb: the buffer to read | ||
| 1506 | * @from: lower offset of data to be read | ||
| 1507 | * @to: upper offset of data to be read | ||
| 1508 | * @st: state variable | ||
| 1509 | * | ||
| 1510 | * Initializes the specified state variable. Must be called before | ||
| 1511 | * invoking skb_seq_read() for the first time. | ||
| 1512 | */ | ||
| 1513 | void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, | ||
| 1514 | unsigned int to, struct skb_seq_state *st) | ||
| 1515 | { | ||
| 1516 | st->lower_offset = from; | ||
| 1517 | st->upper_offset = to; | ||
| 1518 | st->root_skb = st->cur_skb = skb; | ||
| 1519 | st->frag_idx = st->stepped_offset = 0; | ||
| 1520 | st->frag_data = NULL; | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | /** | ||
| 1524 | * skb_seq_read - Sequentially read skb data | ||
| 1525 | * @consumed: number of bytes consumed by the caller so far | ||
| 1526 | * @data: destination pointer for data to be returned | ||
| 1527 | * @st: state variable | ||
| 1528 | * | ||
| 1529 | * Reads a block of skb data at &consumed relative to the | ||
| 1530 | * lower offset specified to skb_prepare_seq_read(). Assigns | ||
| 1531 | * the head of the data block to &data and returns the length | ||
| 1532 | * of the block or 0 if the end of the skb data or the upper | ||
| 1533 | * offset has been reached. | ||
| 1534 | * | ||
| 1535 | * The caller is not required to consume all of the data | ||
| 1536 | * returned, i.e. &consumed is typically set to the number | ||
| 1537 | * of bytes already consumed and the next call to | ||
| 1538 | * skb_seq_read() will return the remaining part of the block. | ||
| 1539 | * | ||
| 1540 | * Note: The size of each block of data returned can be arbitary, | ||
| 1541 | * this limitation is the cost for zerocopy seqeuental | ||
| 1542 | * reads of potentially non linear data. | ||
| 1543 | * | ||
| 1544 | * Note: Fragment lists within fragments are not implemented | ||
| 1545 | * at the moment, state->root_skb could be replaced with | ||
| 1546 | * a stack for this purpose. | ||
| 1547 | */ | ||
| 1548 | unsigned int skb_seq_read(unsigned int consumed, const u8 **data, | ||
| 1549 | struct skb_seq_state *st) | ||
| 1550 | { | ||
| 1551 | unsigned int block_limit, abs_offset = consumed + st->lower_offset; | ||
| 1552 | skb_frag_t *frag; | ||
| 1553 | |||
| 1554 | if (unlikely(abs_offset >= st->upper_offset)) | ||
| 1555 | return 0; | ||
| 1556 | |||
| 1557 | next_skb: | ||
| 1558 | block_limit = skb_headlen(st->cur_skb); | ||
| 1559 | |||
| 1560 | if (abs_offset < block_limit) { | ||
| 1561 | *data = st->cur_skb->data + abs_offset; | ||
| 1562 | return block_limit - abs_offset; | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | if (st->frag_idx == 0 && !st->frag_data) | ||
| 1566 | st->stepped_offset += skb_headlen(st->cur_skb); | ||
| 1567 | |||
| 1568 | while (st->frag_idx < skb_shinfo(st->cur_skb)->nr_frags) { | ||
| 1569 | frag = &skb_shinfo(st->cur_skb)->frags[st->frag_idx]; | ||
| 1570 | block_limit = frag->size + st->stepped_offset; | ||
| 1571 | |||
| 1572 | if (abs_offset < block_limit) { | ||
| 1573 | if (!st->frag_data) | ||
| 1574 | st->frag_data = kmap_skb_frag(frag); | ||
| 1575 | |||
| 1576 | *data = (u8 *) st->frag_data + frag->page_offset + | ||
| 1577 | (abs_offset - st->stepped_offset); | ||
| 1578 | |||
| 1579 | return block_limit - abs_offset; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | if (st->frag_data) { | ||
| 1583 | kunmap_skb_frag(st->frag_data); | ||
| 1584 | st->frag_data = NULL; | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | st->frag_idx++; | ||
| 1588 | st->stepped_offset += frag->size; | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | if (st->cur_skb->next) { | ||
| 1592 | st->cur_skb = st->cur_skb->next; | ||
| 1593 | st->frag_idx = 0; | ||
| 1594 | goto next_skb; | ||
| 1595 | } else if (st->root_skb == st->cur_skb && | ||
| 1596 | skb_shinfo(st->root_skb)->frag_list) { | ||
| 1597 | st->cur_skb = skb_shinfo(st->root_skb)->frag_list; | ||
| 1598 | goto next_skb; | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | return 0; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | /** | ||
| 1605 | * skb_abort_seq_read - Abort a sequential read of skb data | ||
| 1606 | * @st: state variable | ||
| 1607 | * | ||
| 1608 | * Must be called if skb_seq_read() was not called until it | ||
| 1609 | * returned 0. | ||
| 1610 | */ | ||
| 1611 | void skb_abort_seq_read(struct skb_seq_state *st) | ||
| 1612 | { | ||
| 1613 | if (st->frag_data) | ||
| 1614 | kunmap_skb_frag(st->frag_data); | ||
| 1615 | } | ||
| 1616 | |||
| 1503 | void __init skb_init(void) | 1617 | void __init skb_init(void) |
| 1504 | { | 1618 | { |
| 1505 | skbuff_head_cache = kmem_cache_create("skbuff_head_cache", | 1619 | skbuff_head_cache = kmem_cache_create("skbuff_head_cache", |
| @@ -1538,3 +1652,6 @@ EXPORT_SYMBOL(skb_queue_tail); | |||
| 1538 | EXPORT_SYMBOL(skb_unlink); | 1652 | EXPORT_SYMBOL(skb_unlink); |
| 1539 | EXPORT_SYMBOL(skb_append); | 1653 | EXPORT_SYMBOL(skb_append); |
| 1540 | EXPORT_SYMBOL(skb_split); | 1654 | EXPORT_SYMBOL(skb_split); |
| 1655 | EXPORT_SYMBOL(skb_prepare_seq_read); | ||
| 1656 | EXPORT_SYMBOL(skb_seq_read); | ||
| 1657 | EXPORT_SYMBOL(skb_abort_seq_read); | ||
