diff options
| author | Eric Dumazet <edumazet@google.com> | 2012-09-27 15:29:05 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2012-10-01 17:01:46 -0400 |
| commit | c9e6bc644e557338221e75c242ab12c275a67d1b (patch) | |
| tree | dea44a1edd85dccafc2d0973280d3a608d69eb02 /include/net | |
| parent | 861b650101eb0c627d171eb18de81dddb93d395e (diff) | |
net: add gro_cells infrastructure
This adds a new include file (include/net/gro_cells.h), to bring GRO
(Generic Receive Offload) capability to tunnels, in a modular way.
Because tunnels receive path is lockless, and GRO adds a serialization
using a napi_struct, I chose to add an array of up to
DEFAULT_MAX_NUM_RSS_QUEUES cells, so that multi queue devices wont be
slowed down because of GRO layer.
skb_get_rx_queue() is used as selector.
In the future, we might add optional fanout capabilities, using rxhash
for example.
With help from Ben Hutchings who reminded me
netif_get_num_default_rss_queues() function.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include/net')
| -rw-r--r-- | include/net/gro_cells.h | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/include/net/gro_cells.h b/include/net/gro_cells.h new file mode 100644 index 000000000000..4fd8a4b4b7ee --- /dev/null +++ b/include/net/gro_cells.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | #ifndef _NET_GRO_CELLS_H | ||
| 2 | #define _NET_GRO_CELLS_H | ||
| 3 | |||
| 4 | #include <linux/skbuff.h> | ||
| 5 | #include <linux/slab.h> | ||
| 6 | #include <linux/netdevice.h> | ||
| 7 | |||
| 8 | struct gro_cell { | ||
| 9 | struct sk_buff_head napi_skbs; | ||
| 10 | struct napi_struct napi; | ||
| 11 | } ____cacheline_aligned_in_smp; | ||
| 12 | |||
| 13 | struct gro_cells { | ||
| 14 | unsigned int gro_cells_mask; | ||
| 15 | struct gro_cell *cells; | ||
| 16 | }; | ||
| 17 | |||
| 18 | static inline void gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) | ||
| 19 | { | ||
| 20 | unsigned long flags; | ||
| 21 | struct gro_cell *cell = gcells->cells; | ||
| 22 | struct net_device *dev = skb->dev; | ||
| 23 | |||
| 24 | if (!cell || skb_cloned(skb) || !(dev->features & NETIF_F_GRO)) { | ||
| 25 | netif_rx(skb); | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (skb_rx_queue_recorded(skb)) | ||
| 30 | cell += skb_get_rx_queue(skb) & gcells->gro_cells_mask; | ||
| 31 | |||
| 32 | if (skb_queue_len(&cell->napi_skbs) > netdev_max_backlog) { | ||
| 33 | atomic_long_inc(&dev->rx_dropped); | ||
| 34 | kfree_skb(skb); | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | |||
| 38 | spin_lock_irqsave(&cell->napi_skbs.lock, flags); | ||
| 39 | |||
| 40 | __skb_queue_tail(&cell->napi_skbs, skb); | ||
| 41 | if (skb_queue_len(&cell->napi_skbs) == 1) | ||
| 42 | napi_schedule(&cell->napi); | ||
| 43 | |||
| 44 | spin_unlock_irqrestore(&cell->napi_skbs.lock, flags); | ||
| 45 | } | ||
| 46 | |||
| 47 | static inline int gro_cell_poll(struct napi_struct *napi, int budget) | ||
| 48 | { | ||
| 49 | struct gro_cell *cell = container_of(napi, struct gro_cell, napi); | ||
| 50 | struct sk_buff *skb; | ||
| 51 | int work_done = 0; | ||
| 52 | |||
| 53 | while (work_done < budget) { | ||
| 54 | skb = skb_dequeue(&cell->napi_skbs); | ||
| 55 | if (!skb) | ||
| 56 | break; | ||
| 57 | |||
| 58 | napi_gro_receive(napi, skb); | ||
| 59 | work_done++; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (work_done < budget) | ||
| 63 | napi_complete(napi); | ||
| 64 | return work_done; | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline int gro_cells_init(struct gro_cells *gcells, struct net_device *dev) | ||
| 68 | { | ||
| 69 | int i; | ||
| 70 | |||
| 71 | gcells->gro_cells_mask = roundup_pow_of_two(netif_get_num_default_rss_queues()) - 1; | ||
| 72 | gcells->cells = kcalloc(sizeof(struct gro_cell), | ||
| 73 | gcells->gro_cells_mask + 1, | ||
| 74 | GFP_KERNEL); | ||
| 75 | if (!gcells->cells) | ||
| 76 | return -ENOMEM; | ||
| 77 | |||
| 78 | for (i = 0; i <= gcells->gro_cells_mask; i++) { | ||
| 79 | struct gro_cell *cell = gcells->cells + i; | ||
| 80 | |||
| 81 | skb_queue_head_init(&cell->napi_skbs); | ||
| 82 | netif_napi_add(dev, &cell->napi, gro_cell_poll, 64); | ||
| 83 | napi_enable(&cell->napi); | ||
| 84 | } | ||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | static inline void gro_cells_destroy(struct gro_cells *gcells) | ||
| 89 | { | ||
| 90 | struct gro_cell *cell = gcells->cells; | ||
| 91 | int i; | ||
| 92 | |||
| 93 | if (!cell) | ||
| 94 | return; | ||
| 95 | for (i = 0; i <= gcells->gro_cells_mask; i++,cell++) { | ||
| 96 | netif_napi_del(&cell->napi); | ||
| 97 | skb_queue_purge(&cell->napi_skbs); | ||
| 98 | } | ||
| 99 | kfree(gcells->cells); | ||
| 100 | gcells->cells = NULL; | ||
| 101 | } | ||
| 102 | |||
| 103 | #endif | ||
