Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * net/core/dst.c Protocol independent destination cache.
4 : : *
5 : : * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 : : *
7 : : */
8 : :
9 : : #include <linux/bitops.h>
10 : : #include <linux/errno.h>
11 : : #include <linux/init.h>
12 : : #include <linux/kernel.h>
13 : : #include <linux/workqueue.h>
14 : : #include <linux/mm.h>
15 : : #include <linux/module.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/netdevice.h>
18 : : #include <linux/skbuff.h>
19 : : #include <linux/string.h>
20 : : #include <linux/types.h>
21 : : #include <net/net_namespace.h>
22 : : #include <linux/sched.h>
23 : : #include <linux/prefetch.h>
24 : : #include <net/lwtunnel.h>
25 : : #include <net/xfrm.h>
26 : :
27 : : #include <net/dst.h>
28 : : #include <net/dst_metadata.h>
29 : :
30 : 0 : int dst_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
31 : : {
32 : 0 : kfree_skb(skb);
33 : 0 : return 0;
34 : : }
35 : : EXPORT_SYMBOL(dst_discard_out);
36 : :
37 : : const struct dst_metrics dst_default_metrics = {
38 : : /* This initializer is needed to force linker to place this variable
39 : : * into const section. Otherwise it might end into bss section.
40 : : * We really want to avoid false sharing on this variable, and catch
41 : : * any writes on it.
42 : : */
43 : : .refcnt = REFCOUNT_INIT(1),
44 : : };
45 : : EXPORT_SYMBOL(dst_default_metrics);
46 : :
47 : 8848 : void dst_init(struct dst_entry *dst, struct dst_ops *ops,
48 : : struct net_device *dev, int initial_ref, int initial_obsolete,
49 : : unsigned short flags)
50 : : {
51 : 8848 : dst->dev = dev;
52 [ + - ]: 8848 : if (dev)
53 : 8848 : dev_hold(dev);
54 : 8848 : dst->ops = ops;
55 : : dst_init_metrics(dst, dst_default_metrics.metrics, true);
56 : 8848 : dst->expires = 0UL;
57 : : #ifdef CONFIG_XFRM
58 : 8848 : dst->xfrm = NULL;
59 : : #endif
60 : 8848 : dst->input = dst_discard;
61 : 8848 : dst->output = dst_discard_out;
62 : 8848 : dst->error = 0;
63 : 8848 : dst->obsolete = initial_obsolete;
64 : 8848 : dst->header_len = 0;
65 : 8848 : dst->trailer_len = 0;
66 : : #ifdef CONFIG_IP_ROUTE_CLASSID
67 : 8848 : dst->tclassid = 0;
68 : : #endif
69 : 8848 : dst->lwtstate = NULL;
70 : : atomic_set(&dst->__refcnt, initial_ref);
71 : 8848 : dst->__use = 0;
72 : 8848 : dst->lastuse = jiffies;
73 : 8848 : dst->flags = flags;
74 [ + - ]: 8848 : if (!(flags & DST_NOCOUNT))
75 : : dst_entries_add(ops, 1);
76 : 8848 : }
77 : : EXPORT_SYMBOL(dst_init);
78 : :
79 : 8848 : void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
80 : : int initial_ref, int initial_obsolete, unsigned short flags)
81 : : {
82 : : struct dst_entry *dst;
83 : :
84 [ + + - + ]: 11359 : if (ops->gc && dst_entries_get_fast(ops) > ops->gc_thresh) {
85 [ # # ]: 0 : if (ops->gc(ops)) {
86 [ # # ]: 0 : printk_ratelimited(KERN_NOTICE "Route cache is full: "
87 : : "consider increasing sysctl "
88 : : "net.ipv[4|6].route.max_size.\n");
89 : : return NULL;
90 : : }
91 : : }
92 : :
93 : 8848 : dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC);
94 [ + - ]: 8848 : if (!dst)
95 : : return NULL;
96 : :
97 : 8848 : dst_init(dst, ops, dev, initial_ref, initial_obsolete, flags);
98 : :
99 : 8848 : return dst;
100 : : }
101 : : EXPORT_SYMBOL(dst_alloc);
102 : :
103 : 7024 : struct dst_entry *dst_destroy(struct dst_entry * dst)
104 : : {
105 : : struct dst_entry *child = NULL;
106 : :
107 : 7024 : smp_rmb();
108 : :
109 : : #ifdef CONFIG_XFRM
110 [ - + ]: 7024 : if (dst->xfrm) {
111 : : struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
112 : :
113 : 0 : child = xdst->child;
114 : : }
115 : : #endif
116 [ + - ]: 7024 : if (!(dst->flags & DST_NOCOUNT))
117 : 7024 : dst_entries_add(dst->ops, -1);
118 : :
119 [ + - ]: 7024 : if (dst->ops->destroy)
120 : 7024 : dst->ops->destroy(dst);
121 [ + - ]: 7024 : if (dst->dev)
122 : 7024 : dev_put(dst->dev);
123 : :
124 : : lwtstate_put(dst->lwtstate);
125 : :
126 [ - + ]: 7024 : if (dst->flags & DST_METADATA)
127 : 0 : metadata_dst_free((struct metadata_dst *)dst);
128 : : else
129 : 7024 : kmem_cache_free(dst->ops->kmem_cachep, dst);
130 : :
131 : : dst = child;
132 [ - + ]: 7024 : if (dst)
133 : 0 : dst_release_immediate(dst);
134 : 7024 : return NULL;
135 : : }
136 : : EXPORT_SYMBOL(dst_destroy);
137 : :
138 : 7024 : static void dst_destroy_rcu(struct rcu_head *head)
139 : : {
140 : 7024 : struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head);
141 : :
142 : 7024 : dst = dst_destroy(dst);
143 : 7024 : }
144 : :
145 : : /* Operations to mark dst as DEAD and clean up the net device referenced
146 : : * by dst:
147 : : * 1. put the dst under loopback interface and discard all tx/rx packets
148 : : * on this route.
149 : : * 2. release the net_device
150 : : * This function should be called when removing routes from the fib tree
151 : : * in preparation for a NETDEV_DOWN/NETDEV_UNREGISTER event and also to
152 : : * make the next dst_ops->check() fail.
153 : : */
154 : 0 : void dst_dev_put(struct dst_entry *dst)
155 : : {
156 : 0 : struct net_device *dev = dst->dev;
157 : :
158 : 0 : dst->obsolete = DST_OBSOLETE_DEAD;
159 [ # # ]: 0 : if (dst->ops->ifdown)
160 : 0 : dst->ops->ifdown(dst, dev, true);
161 : 0 : dst->input = dst_discard;
162 : 0 : dst->output = dst_discard_out;
163 : 0 : dst->dev = blackhole_netdev;
164 : 0 : dev_hold(dst->dev);
165 : 0 : dev_put(dev);
166 : 0 : }
167 : : EXPORT_SYMBOL(dst_dev_put);
168 : :
169 : 49576 : void dst_release(struct dst_entry *dst)
170 : : {
171 [ + + ]: 49576 : if (dst) {
172 : : int newrefcnt;
173 : :
174 : 19374 : newrefcnt = atomic_dec_return(&dst->__refcnt);
175 [ - + # # : 19375 : if (WARN_ONCE(newrefcnt < 0, "dst_release underflow"))
- + ]
176 [ # # ]: 0 : net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
177 : : __func__, dst, newrefcnt);
178 [ + + ]: 19375 : if (!newrefcnt)
179 : 7024 : call_rcu(&dst->rcu_head, dst_destroy_rcu);
180 : : }
181 : 49577 : }
182 : : EXPORT_SYMBOL(dst_release);
183 : :
184 : 0 : void dst_release_immediate(struct dst_entry *dst)
185 : : {
186 [ # # ]: 0 : if (dst) {
187 : : int newrefcnt;
188 : :
189 : 0 : newrefcnt = atomic_dec_return(&dst->__refcnt);
190 [ # # # # : 0 : if (WARN_ONCE(newrefcnt < 0, "dst_release_immediate underflow"))
# # ]
191 [ # # ]: 0 : net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
192 : : __func__, dst, newrefcnt);
193 [ # # ]: 0 : if (!newrefcnt)
194 : 0 : dst_destroy(dst);
195 : : }
196 : 0 : }
197 : : EXPORT_SYMBOL(dst_release_immediate);
198 : :
199 : 2001 : u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old)
200 : : {
201 : : struct dst_metrics *p = kmalloc(sizeof(*p), GFP_ATOMIC);
202 : :
203 [ + - ]: 2001 : if (p) {
204 : 2001 : struct dst_metrics *old_p = (struct dst_metrics *)__DST_METRICS_PTR(old);
205 : : unsigned long prev, new;
206 : :
207 : : refcount_set(&p->refcnt, 1);
208 : 2001 : memcpy(p->metrics, old_p->metrics, sizeof(p->metrics));
209 : :
210 : 2001 : new = (unsigned long) p;
211 : 2001 : prev = cmpxchg(&dst->_metrics, old, new);
212 : :
213 [ - + ]: 2001 : if (prev != old) {
214 : 0 : kfree(p);
215 : 0 : p = (struct dst_metrics *)__DST_METRICS_PTR(prev);
216 [ # # ]: 0 : if (prev & DST_METRICS_READ_ONLY)
217 : : p = NULL;
218 [ - + ]: 2001 : } else if (prev & DST_METRICS_REFCOUNTED) {
219 [ # # ]: 0 : if (refcount_dec_and_test(&old_p->refcnt))
220 : 0 : kfree(old_p);
221 : : }
222 : : }
223 : : BUILD_BUG_ON(offsetof(struct dst_metrics, metrics) != 0);
224 : 2001 : return (u32 *)p;
225 : : }
226 : : EXPORT_SYMBOL(dst_cow_metrics_generic);
227 : :
228 : : /* Caller asserts that dst_metrics_read_only(dst) is false. */
229 : 0 : void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old)
230 : : {
231 : : unsigned long prev, new;
232 : :
233 : 0 : new = ((unsigned long) &dst_default_metrics) | DST_METRICS_READ_ONLY;
234 : 0 : prev = cmpxchg(&dst->_metrics, old, new);
235 [ # # ]: 0 : if (prev == old)
236 : 0 : kfree(__DST_METRICS_PTR(old));
237 : 0 : }
238 : : EXPORT_SYMBOL(__dst_destroy_metrics_generic);
239 : :
240 : : static struct dst_ops md_dst_ops = {
241 : : .family = AF_UNSPEC,
242 : : };
243 : :
244 : 0 : static int dst_md_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
245 : : {
246 [ # # ]: 0 : WARN_ONCE(1, "Attempting to call output on metadata dst\n");
247 : 0 : kfree_skb(skb);
248 : 0 : return 0;
249 : : }
250 : :
251 : 0 : static int dst_md_discard(struct sk_buff *skb)
252 : : {
253 [ # # ]: 0 : WARN_ONCE(1, "Attempting to call input on metadata dst\n");
254 : 0 : kfree_skb(skb);
255 : 0 : return 0;
256 : : }
257 : :
258 : 0 : static void __metadata_dst_init(struct metadata_dst *md_dst,
259 : : enum metadata_type type, u8 optslen)
260 : :
261 : : {
262 : : struct dst_entry *dst;
263 : :
264 : 0 : dst = &md_dst->dst;
265 : 0 : dst_init(dst, &md_dst_ops, NULL, 1, DST_OBSOLETE_NONE,
266 : : DST_METADATA | DST_NOCOUNT);
267 : :
268 : 0 : dst->input = dst_md_discard;
269 : 0 : dst->output = dst_md_discard_out;
270 : :
271 : 0 : memset(dst + 1, 0, sizeof(*md_dst) + optslen - sizeof(*dst));
272 : 0 : md_dst->type = type;
273 : 0 : }
274 : :
275 : 0 : struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type,
276 : : gfp_t flags)
277 : : {
278 : : struct metadata_dst *md_dst;
279 : :
280 : 0 : md_dst = kmalloc(sizeof(*md_dst) + optslen, flags);
281 [ # # ]: 0 : if (!md_dst)
282 : : return NULL;
283 : :
284 : 0 : __metadata_dst_init(md_dst, type, optslen);
285 : :
286 : 0 : return md_dst;
287 : : }
288 : : EXPORT_SYMBOL_GPL(metadata_dst_alloc);
289 : :
290 : 0 : void metadata_dst_free(struct metadata_dst *md_dst)
291 : : {
292 : : #ifdef CONFIG_DST_CACHE
293 [ # # ]: 0 : if (md_dst->type == METADATA_IP_TUNNEL)
294 : 0 : dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
295 : : #endif
296 : 0 : kfree(md_dst);
297 : 0 : }
298 : : EXPORT_SYMBOL_GPL(metadata_dst_free);
299 : :
300 : : struct metadata_dst __percpu *
301 : 0 : metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags)
302 : : {
303 : : int cpu;
304 : : struct metadata_dst __percpu *md_dst;
305 : :
306 : 0 : md_dst = __alloc_percpu_gfp(sizeof(struct metadata_dst) + optslen,
307 : : __alignof__(struct metadata_dst), flags);
308 [ # # ]: 0 : if (!md_dst)
309 : : return NULL;
310 : :
311 [ # # ]: 0 : for_each_possible_cpu(cpu)
312 : 0 : __metadata_dst_init(per_cpu_ptr(md_dst, cpu), type, optslen);
313 : :
314 : : return md_dst;
315 : : }
316 : : EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu);
317 : :
318 : 0 : void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
319 : : {
320 : : #ifdef CONFIG_DST_CACHE
321 : : int cpu;
322 : :
323 [ # # ]: 0 : for_each_possible_cpu(cpu) {
324 : 0 : struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu);
325 : :
326 [ # # ]: 0 : if (one_md_dst->type == METADATA_IP_TUNNEL)
327 : 0 : dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
328 : : }
329 : : #endif
330 : 0 : free_percpu(md_dst);
331 : 0 : }
332 : : EXPORT_SYMBOL_GPL(metadata_dst_free_percpu);
|