From 266ea3a2aa81fdd82559a05e42bc4bc16025edcf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 7 Feb 2017 15:37:15 -0800 Subject: [PATCH] BACKPORT: gro_cells: move to net/core/gro_cells.c We have many gro cells users, so lets move the code to avoid duplication. This creates a CONFIG_GRO_CELLS option. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sorayukii --- drivers/net/Kconfig | 3 ++ include/net/gro_cells.h | 101 ++----------------------------------- net/Kconfig | 4 ++ net/core/Makefile | 1 + net/core/gro_cells.c | 107 ++++++++++++++++++++++++++++++++++++++++ net/ipv4/Kconfig | 1 + net/ipv6/Kconfig | 1 + net/xfrm/Kconfig | 1 + 8 files changed, 122 insertions(+), 97 deletions(-) create mode 100644 net/core/gro_cells.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b7f9c5c1fe88..40603a9e301e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -205,6 +205,7 @@ config VXLAN tristate "Virtual eXtensible Local Area Network (VXLAN)" depends on INET select NET_UDP_TUNNEL + select GRO_CELLS ---help--- This allows one to create vxlan virtual interfaces that provide Layer 2 Networks over Layer 3 Networks. VXLAN is often used @@ -219,6 +220,7 @@ config GENEVE tristate "Generic Network Virtualization Encapsulation" depends on INET && NET_UDP_TUNNEL select NET_IP_TUNNEL + select GRO_CELLS ---help--- This allows one to create geneve virtual interfaces that provide Layer 2 Networks over Layer 3 Networks. GENEVE is often used @@ -251,6 +253,7 @@ config MACSEC select CRYPTO select CRYPTO_AES select CRYPTO_GCM + select GRO_CELLS ---help--- MACsec is an encryption standard for Ethernet. diff --git a/include/net/gro_cells.h b/include/net/gro_cells.h index 6db0e8534127..fcaf8f479130 100644 --- a/include/net/gro_cells.h +++ b/include/net/gro_cells.h @@ -5,107 +5,14 @@ #include #include -struct gro_cell { - struct sk_buff_head napi_skbs; - struct napi_struct napi; -}; +struct gro_cell; struct gro_cells { struct gro_cell __percpu *cells; }; -static inline int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) -{ - struct gro_cell *cell; - struct net_device *dev = skb->dev; - int res; - - rcu_read_lock(); - if (unlikely(!(dev->flags & IFF_UP))) - goto drop; - - if (!gcells->cells || skb_cloned(skb) || !(dev->features & NETIF_F_GRO)) { - res = netif_rx(skb); - goto unlock; - } - - cell = this_cpu_ptr(gcells->cells); - - if (skb_queue_len(&cell->napi_skbs) > netdev_max_backlog) { -drop: - atomic_long_inc(&dev->rx_dropped); - kfree_skb(skb); - res = NET_RX_DROP; - goto unlock; - } - - __skb_queue_tail(&cell->napi_skbs, skb); - if (skb_queue_len(&cell->napi_skbs) == 1) - napi_schedule(&cell->napi); - - res = NET_RX_SUCCESS; - -unlock: - rcu_read_unlock(); - return res; -} - -/* called under BH context */ -static inline int gro_cell_poll(struct napi_struct *napi, int budget) -{ - struct gro_cell *cell = container_of(napi, struct gro_cell, napi); - struct sk_buff *skb; - int work_done = 0; - - while (work_done < budget) { - skb = __skb_dequeue(&cell->napi_skbs); - if (!skb) - break; - napi_gro_receive(napi, skb); - work_done++; - } - - if (work_done < budget) - napi_complete_done(napi, work_done); - return work_done; -} - -static inline int gro_cells_init(struct gro_cells *gcells, struct net_device *dev) -{ - int i; - - gcells->cells = alloc_percpu(struct gro_cell); - if (!gcells->cells) - return -ENOMEM; - - for_each_possible_cpu(i) { - struct gro_cell *cell = per_cpu_ptr(gcells->cells, i); - - __skb_queue_head_init(&cell->napi_skbs); - - set_bit(NAPI_STATE_NO_BUSY_POLL, &cell->napi.state); - - netif_napi_add(dev, &cell->napi, gro_cell_poll, 64); - napi_enable(&cell->napi); - } - return 0; -} - -static inline void gro_cells_destroy(struct gro_cells *gcells) -{ - int i; - - if (!gcells->cells) - return; - for_each_possible_cpu(i) { - struct gro_cell *cell = per_cpu_ptr(gcells->cells, i); - - napi_disable(&cell->napi); - netif_napi_del(&cell->napi); - __skb_queue_purge(&cell->napi_skbs); - } - free_percpu(gcells->cells); - gcells->cells = NULL; -} +int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb); +int gro_cells_init(struct gro_cells *gcells, struct net_device *dev); +void gro_cells_destroy(struct gro_cells *gcells); #endif diff --git a/net/Kconfig b/net/Kconfig index 8facd9d25fe3..ab8615b50ab8 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -428,6 +428,10 @@ config DST_CACHE bool default n +config GRO_CELLS + bool + default n + config NET_DEVLINK tristate "Network physical/parent device Netlink interface" help diff --git a/net/core/Makefile b/net/core/Makefile index 4b0a2b37bc4f..bdb59f41e23b 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_SOCKEV_NLMCAST) += sockev_nlmcast.o obj-$(CONFIG_DST_CACHE) += dst_cache.o obj-$(CONFIG_HWBM) += hwbm.o obj-$(CONFIG_NET_DEVLINK) += devlink.o +obj-$(CONFIG_GRO_CELLS) += gro_cells.o diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c new file mode 100644 index 000000000000..dbc47f48e805 --- /dev/null +++ b/net/core/gro_cells.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +struct gro_cell { + struct sk_buff_head napi_skbs; + struct napi_struct napi; +}; + +int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct gro_cell *cell; + int res; + + rcu_read_lock(); + if (unlikely(!(dev->flags & IFF_UP))) + goto drop; + + if (!gcells->cells || skb_cloned(skb) || !(dev->features & NETIF_F_GRO)) { + res = netif_rx(skb); + goto unlock; + } + + cell = this_cpu_ptr(gcells->cells); + + if (skb_queue_len(&cell->napi_skbs) > netdev_max_backlog) { +drop: + atomic_long_inc(&dev->rx_dropped); + kfree_skb(skb); + res = NET_RX_DROP; + goto unlock; + } + + __skb_queue_tail(&cell->napi_skbs, skb); + if (skb_queue_len(&cell->napi_skbs) == 1) + napi_schedule(&cell->napi); + + res = NET_RX_SUCCESS; + +unlock: + rcu_read_unlock(); + return res; +} +EXPORT_SYMBOL(gro_cells_receive); + +/* called under BH context */ +static int gro_cell_poll(struct napi_struct *napi, int budget) +{ + struct gro_cell *cell = container_of(napi, struct gro_cell, napi); + struct sk_buff *skb; + int work_done = 0; + + while (work_done < budget) { + skb = __skb_dequeue(&cell->napi_skbs); + if (!skb) + break; + napi_gro_receive(napi, skb); + work_done++; + } + + if (work_done < budget) + napi_complete_done(napi, work_done); + return work_done; +} + +int gro_cells_init(struct gro_cells *gcells, struct net_device *dev) +{ + int i; + + gcells->cells = alloc_percpu(struct gro_cell); + if (!gcells->cells) + return -ENOMEM; + + for_each_possible_cpu(i) { + struct gro_cell *cell = per_cpu_ptr(gcells->cells, i); + + __skb_queue_head_init(&cell->napi_skbs); + + set_bit(NAPI_STATE_NO_BUSY_POLL, &cell->napi.state); + + netif_napi_add(dev, &cell->napi, gro_cell_poll, + NAPI_POLL_WEIGHT); + napi_enable(&cell->napi); + } + return 0; +} +EXPORT_SYMBOL(gro_cells_init); + +void gro_cells_destroy(struct gro_cells *gcells) +{ + int i; + + if (!gcells->cells) + return; + for_each_possible_cpu(i) { + struct gro_cell *cell = per_cpu_ptr(gcells->cells, i); + + napi_disable(&cell->napi); + netif_napi_del(&cell->napi); + __skb_queue_purge(&cell->napi_skbs); + } + free_percpu(gcells->cells); + gcells->cells = NULL; +} +EXPORT_SYMBOL(gro_cells_destroy); \ No newline at end of file diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 29be5bcbe7ac..59926ba7591a 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -187,6 +187,7 @@ config NET_IPGRE_DEMUX config NET_IP_TUNNEL tristate select DST_CACHE + select GRO_CELLS default n config NET_IPGRE diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index ec1267e2bd1f..3c7c76b2a7ba 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -208,6 +208,7 @@ config IPV6_TUNNEL tristate "IPv6: IP-in-IPv6 tunnel (RFC2473)" select INET6_TUNNEL select DST_CACHE + select GRO_CELLS ---help--- Support for IPv6-in-IPv6 and IPv4-in-IPv6 tunnels described in RFC 2473. diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig index 668fdfa4d7f2..57f9df7945ab 100644 --- a/net/xfrm/Kconfig +++ b/net/xfrm/Kconfig @@ -4,6 +4,7 @@ config XFRM bool depends on NET + select GRO_CELLS config XFRM_ALGO tristate