xref: /linux/net/core/gro_cells.c (revision 28a57fb2c5df4deb42a06e52fd36c14b37aa0034)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/skbuff.h>
3 #include <linux/slab.h>
4 #include <linux/netdevice.h>
5 #include <net/gro_cells.h>
6 #include <net/hotdata.h>
7 
8 struct gro_cell {
9 	struct sk_buff_head	napi_skbs;
10 	struct napi_struct	napi;
11 	local_lock_t		bh_lock;
12 };
13 
14 int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
15 {
16 	struct net_device *dev = skb->dev;
17 	bool have_bh_lock = false;
18 	struct gro_cell *cell;
19 	int res;
20 
21 	rcu_read_lock();
22 	if (unlikely(!(dev->flags & IFF_UP)))
23 		goto drop;
24 
25 	skb_unset_transport_header(skb);
26 
27 	if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
28 		res = netif_rx(skb);
29 		goto unlock;
30 	}
31 
32 	local_lock_nested_bh(&gcells->cells->bh_lock);
33 	have_bh_lock = true;
34 	cell = this_cpu_ptr(gcells->cells);
35 
36 	if (skb_queue_len(&cell->napi_skbs) > READ_ONCE(net_hotdata.max_backlog)) {
37 drop:
38 		dev_core_stats_rx_dropped_inc(dev);
39 		kfree_skb(skb);
40 		res = NET_RX_DROP;
41 		goto unlock;
42 	}
43 
44 	__skb_queue_tail(&cell->napi_skbs, skb);
45 	if (skb_queue_len(&cell->napi_skbs) == 1)
46 		napi_schedule(&cell->napi);
47 
48 	res = NET_RX_SUCCESS;
49 
50 unlock:
51 	if (have_bh_lock)
52 		local_unlock_nested_bh(&gcells->cells->bh_lock);
53 	rcu_read_unlock();
54 	return res;
55 }
56 EXPORT_SYMBOL(gro_cells_receive);
57 
58 /* called under BH context */
59 static int gro_cell_poll(struct napi_struct *napi, int budget)
60 {
61 	struct gro_cell *cell = container_of(napi, struct gro_cell, napi);
62 	struct sk_buff *skb;
63 	int work_done = 0;
64 
65 	while (work_done < budget) {
66 		__local_lock_nested_bh(&cell->bh_lock);
67 		skb = __skb_dequeue(&cell->napi_skbs);
68 		__local_unlock_nested_bh(&cell->bh_lock);
69 		if (!skb)
70 			break;
71 		napi_gro_receive(napi, skb);
72 		work_done++;
73 	}
74 
75 	if (work_done < budget)
76 		napi_complete_done(napi, work_done);
77 	return work_done;
78 }
79 
80 int gro_cells_init(struct gro_cells *gcells, struct net_device *dev)
81 {
82 	int i;
83 
84 	gcells->cells = alloc_percpu(struct gro_cell);
85 	if (!gcells->cells)
86 		return -ENOMEM;
87 
88 	for_each_possible_cpu(i) {
89 		struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
90 
91 		__skb_queue_head_init(&cell->napi_skbs);
92 		local_lock_init(&cell->bh_lock);
93 
94 		set_bit(NAPI_STATE_NO_BUSY_POLL, &cell->napi.state);
95 
96 		netif_napi_add(dev, &cell->napi, gro_cell_poll);
97 		napi_enable(&cell->napi);
98 	}
99 	return 0;
100 }
101 EXPORT_SYMBOL(gro_cells_init);
102 
103 struct percpu_free_defer {
104 	struct rcu_head rcu;
105 	void __percpu	*ptr;
106 };
107 
108 static void percpu_free_defer_callback(struct rcu_head *head)
109 {
110 	struct percpu_free_defer *defer;
111 
112 	defer = container_of(head, struct percpu_free_defer, rcu);
113 	free_percpu(defer->ptr);
114 	kfree(defer);
115 }
116 
117 void gro_cells_destroy(struct gro_cells *gcells)
118 {
119 	struct percpu_free_defer *defer;
120 	int i;
121 
122 	if (!gcells->cells)
123 		return;
124 	for_each_possible_cpu(i) {
125 		struct gro_cell *cell = per_cpu_ptr(gcells->cells, i);
126 
127 		napi_disable(&cell->napi);
128 		__netif_napi_del(&cell->napi);
129 		__skb_queue_purge(&cell->napi_skbs);
130 	}
131 	/* We need to observe an rcu grace period before freeing ->cells,
132 	 * because netpoll could access dev->napi_list under rcu protection.
133 	 * Try hard using call_rcu() instead of synchronize_rcu(),
134 	 * because we might be called from cleanup_net(), and we
135 	 * definitely do not want to block this critical task.
136 	 */
137 	defer = kmalloc_obj(*defer, GFP_KERNEL | __GFP_NOWARN);
138 	if (likely(defer)) {
139 		defer->ptr = gcells->cells;
140 		call_rcu(&defer->rcu, percpu_free_defer_callback);
141 	} else {
142 		/* We do not hold RTNL at this point, synchronize_net()
143 		 * would not be able to expedite this sync.
144 		 */
145 		synchronize_rcu_expedited();
146 		free_percpu(gcells->cells);
147 	}
148 	gcells->cells = NULL;
149 }
150 EXPORT_SYMBOL(gro_cells_destroy);
151