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