1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
// SPDX-License-Identifier: GPL-2.0
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <net/gro_cells.h>
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) || netif_elide_gro(dev)) {
res = netif_rx(skb);
goto unlock;
}
cell = this_cpu_ptr(gcells->cells);
if (skb_queue_len(&cell->napi_skbs) > READ_ONCE(netdev_max_backlog)) {
drop:
dev_core_stats_rx_dropped_inc(dev);
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_enable(&cell->napi);
}
return 0;
}
EXPORT_SYMBOL(gro_cells_init);
struct percpu_free_defer {
struct rcu_head rcu;
void __percpu *ptr;
};
static void percpu_free_defer_callback(struct rcu_head *head)
{
struct percpu_free_defer *defer;
defer = container_of(head, struct percpu_free_defer, rcu);
free_percpu(defer->ptr);
kfree(defer);
}
void gro_cells_destroy(struct gro_cells *gcells)
{
struct percpu_free_defer *defer;
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);
}
/* We need to observe an rcu grace period before freeing ->cells,
* because netpoll could access dev->napi_list under rcu protection.
* Try hard using call_rcu() instead of synchronize_rcu(),
* because we might be called from cleanup_net(), and we
* definitely do not want to block this critical task.
*/
defer = kmalloc(sizeof(*defer), GFP_KERNEL | __GFP_NOWARN);
if (likely(defer)) {
defer->ptr = gcells->cells;
call_rcu(&defer->rcu, percpu_free_defer_callback);
} else {
/* We do not hold RTNL at this point, synchronize_net()
* would not be able to expedite this sync.
*/
synchronize_rcu_expedited();
free_percpu(gcells->cells);
}
gcells->cells = NULL;
}
EXPORT_SYMBOL(gro_cells_destroy);
|