162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/skbuff.h>
362306a36Sopenharmony_ci#include <linux/slab.h>
462306a36Sopenharmony_ci#include <linux/netdevice.h>
562306a36Sopenharmony_ci#include <net/gro_cells.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistruct gro_cell {
862306a36Sopenharmony_ci	struct sk_buff_head	napi_skbs;
962306a36Sopenharmony_ci	struct napi_struct	napi;
1062306a36Sopenharmony_ci};
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciint gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	struct net_device *dev = skb->dev;
1562306a36Sopenharmony_ci	struct gro_cell *cell;
1662306a36Sopenharmony_ci	int res;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	rcu_read_lock();
1962306a36Sopenharmony_ci	if (unlikely(!(dev->flags & IFF_UP)))
2062306a36Sopenharmony_ci		goto drop;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
2362306a36Sopenharmony_ci		res = netif_rx(skb);
2462306a36Sopenharmony_ci		goto unlock;
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	cell = this_cpu_ptr(gcells->cells);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (skb_queue_len(&cell->napi_skbs) > READ_ONCE(netdev_max_backlog)) {
3062306a36Sopenharmony_cidrop:
3162306a36Sopenharmony_ci		dev_core_stats_rx_dropped_inc(dev);
3262306a36Sopenharmony_ci		kfree_skb(skb);
3362306a36Sopenharmony_ci		res = NET_RX_DROP;
3462306a36Sopenharmony_ci		goto unlock;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	__skb_queue_tail(&cell->napi_skbs, skb);
3862306a36Sopenharmony_ci	if (skb_queue_len(&cell->napi_skbs) == 1)
3962306a36Sopenharmony_ci		napi_schedule(&cell->napi);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	res = NET_RX_SUCCESS;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciunlock:
4462306a36Sopenharmony_ci	rcu_read_unlock();
4562306a36Sopenharmony_ci	return res;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ciEXPORT_SYMBOL(gro_cells_receive);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* called under BH context */
5062306a36Sopenharmony_cistatic int gro_cell_poll(struct napi_struct *napi, int budget)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct gro_cell *cell = container_of(napi, struct gro_cell, napi);
5362306a36Sopenharmony_ci	struct sk_buff *skb;
5462306a36Sopenharmony_ci	int work_done = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	while (work_done < budget) {
5762306a36Sopenharmony_ci		skb = __skb_dequeue(&cell->napi_skbs);
5862306a36Sopenharmony_ci		if (!skb)
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
6162306a36Sopenharmony_ci		work_done++;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (work_done < budget)
6562306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
6662306a36Sopenharmony_ci	return work_done;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciint gro_cells_init(struct gro_cells *gcells, struct net_device *dev)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int i;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	gcells->cells = alloc_percpu(struct gro_cell);
7462306a36Sopenharmony_ci	if (!gcells->cells)
7562306a36Sopenharmony_ci		return -ENOMEM;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	for_each_possible_cpu(i) {
7862306a36Sopenharmony_ci		struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		__skb_queue_head_init(&cell->napi_skbs);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		set_bit(NAPI_STATE_NO_BUSY_POLL, &cell->napi.state);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		netif_napi_add(dev, &cell->napi, gro_cell_poll);
8562306a36Sopenharmony_ci		napi_enable(&cell->napi);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ciEXPORT_SYMBOL(gro_cells_init);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistruct percpu_free_defer {
9262306a36Sopenharmony_ci	struct rcu_head rcu;
9362306a36Sopenharmony_ci	void __percpu	*ptr;
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void percpu_free_defer_callback(struct rcu_head *head)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct percpu_free_defer *defer;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	defer = container_of(head, struct percpu_free_defer, rcu);
10162306a36Sopenharmony_ci	free_percpu(defer->ptr);
10262306a36Sopenharmony_ci	kfree(defer);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid gro_cells_destroy(struct gro_cells *gcells)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct percpu_free_defer *defer;
10862306a36Sopenharmony_ci	int i;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!gcells->cells)
11162306a36Sopenharmony_ci		return;
11262306a36Sopenharmony_ci	for_each_possible_cpu(i) {
11362306a36Sopenharmony_ci		struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		napi_disable(&cell->napi);
11662306a36Sopenharmony_ci		__netif_napi_del(&cell->napi);
11762306a36Sopenharmony_ci		__skb_queue_purge(&cell->napi_skbs);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	/* We need to observe an rcu grace period before freeing ->cells,
12062306a36Sopenharmony_ci	 * because netpoll could access dev->napi_list under rcu protection.
12162306a36Sopenharmony_ci	 * Try hard using call_rcu() instead of synchronize_rcu(),
12262306a36Sopenharmony_ci	 * because we might be called from cleanup_net(), and we
12362306a36Sopenharmony_ci	 * definitely do not want to block this critical task.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	defer = kmalloc(sizeof(*defer), GFP_KERNEL | __GFP_NOWARN);
12662306a36Sopenharmony_ci	if (likely(defer)) {
12762306a36Sopenharmony_ci		defer->ptr = gcells->cells;
12862306a36Sopenharmony_ci		call_rcu(&defer->rcu, percpu_free_defer_callback);
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		/* We do not hold RTNL at this point, synchronize_net()
13162306a36Sopenharmony_ci		 * would not be able to expedite this sync.
13262306a36Sopenharmony_ci		 */
13362306a36Sopenharmony_ci		synchronize_rcu_expedited();
13462306a36Sopenharmony_ci		free_percpu(gcells->cells);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	gcells->cells = NULL;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ciEXPORT_SYMBOL(gro_cells_destroy);
139