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