12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2911362c7SPaolo Abeni /*
3911362c7SPaolo Abeni * net/core/dst_cache.c - dst entry cache
4911362c7SPaolo Abeni *
5911362c7SPaolo Abeni * Copyright (c) 2016 Paolo Abeni <[email protected]>
6911362c7SPaolo Abeni */
7911362c7SPaolo Abeni
8911362c7SPaolo Abeni #include <linux/kernel.h>
9911362c7SPaolo Abeni #include <linux/percpu.h>
10911362c7SPaolo Abeni #include <net/dst_cache.h>
11911362c7SPaolo Abeni #include <net/route.h>
12911362c7SPaolo Abeni #if IS_ENABLED(CONFIG_IPV6)
13911362c7SPaolo Abeni #include <net/ip6_fib.h>
14911362c7SPaolo Abeni #endif
15911362c7SPaolo Abeni #include <uapi/linux/in.h>
16911362c7SPaolo Abeni
17911362c7SPaolo Abeni struct dst_cache_pcpu {
18911362c7SPaolo Abeni unsigned long refresh_ts;
19911362c7SPaolo Abeni struct dst_entry *dst;
20911362c7SPaolo Abeni u32 cookie;
21911362c7SPaolo Abeni union {
22911362c7SPaolo Abeni struct in_addr in_saddr;
23911362c7SPaolo Abeni struct in6_addr in6_saddr;
24911362c7SPaolo Abeni };
25911362c7SPaolo Abeni };
26911362c7SPaolo Abeni
dst_cache_per_cpu_dst_set(struct dst_cache_pcpu * dst_cache,struct dst_entry * dst,u32 cookie)27b73f96fcSWu Fengguang static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
28911362c7SPaolo Abeni struct dst_entry *dst, u32 cookie)
29911362c7SPaolo Abeni {
30*2fe6fb36SEric Dumazet DEBUG_NET_WARN_ON_ONCE(!in_softirq());
31911362c7SPaolo Abeni dst_release(dst_cache->dst);
32911362c7SPaolo Abeni if (dst)
33911362c7SPaolo Abeni dst_hold(dst);
34911362c7SPaolo Abeni
35911362c7SPaolo Abeni dst_cache->cookie = cookie;
36911362c7SPaolo Abeni dst_cache->dst = dst;
37911362c7SPaolo Abeni }
38911362c7SPaolo Abeni
dst_cache_per_cpu_get(struct dst_cache * dst_cache,struct dst_cache_pcpu * idst)39b73f96fcSWu Fengguang static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache,
40911362c7SPaolo Abeni struct dst_cache_pcpu *idst)
41911362c7SPaolo Abeni {
42911362c7SPaolo Abeni struct dst_entry *dst;
43911362c7SPaolo Abeni
44*2fe6fb36SEric Dumazet DEBUG_NET_WARN_ON_ONCE(!in_softirq());
45911362c7SPaolo Abeni dst = idst->dst;
46911362c7SPaolo Abeni if (!dst)
47911362c7SPaolo Abeni goto fail;
48911362c7SPaolo Abeni
49911362c7SPaolo Abeni /* the cache already hold a dst reference; it can't go away */
50911362c7SPaolo Abeni dst_hold(dst);
51911362c7SPaolo Abeni
523b09b2bdSEric Dumazet if (unlikely(!time_after(idst->refresh_ts,
533b09b2bdSEric Dumazet READ_ONCE(dst_cache->reset_ts)) ||
54911362c7SPaolo Abeni (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) {
55911362c7SPaolo Abeni dst_cache_per_cpu_dst_set(idst, NULL, 0);
56911362c7SPaolo Abeni dst_release(dst);
57911362c7SPaolo Abeni goto fail;
58911362c7SPaolo Abeni }
59911362c7SPaolo Abeni return dst;
60911362c7SPaolo Abeni
61911362c7SPaolo Abeni fail:
62911362c7SPaolo Abeni idst->refresh_ts = jiffies;
63911362c7SPaolo Abeni return NULL;
64911362c7SPaolo Abeni }
65911362c7SPaolo Abeni
dst_cache_get(struct dst_cache * dst_cache)66911362c7SPaolo Abeni struct dst_entry *dst_cache_get(struct dst_cache *dst_cache)
67911362c7SPaolo Abeni {
68911362c7SPaolo Abeni if (!dst_cache->cache)
69911362c7SPaolo Abeni return NULL;
70911362c7SPaolo Abeni
71911362c7SPaolo Abeni return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
72911362c7SPaolo Abeni }
73911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_get);
74911362c7SPaolo Abeni
dst_cache_get_ip4(struct dst_cache * dst_cache,__be32 * saddr)75911362c7SPaolo Abeni struct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
76911362c7SPaolo Abeni {
77911362c7SPaolo Abeni struct dst_cache_pcpu *idst;
78911362c7SPaolo Abeni struct dst_entry *dst;
79911362c7SPaolo Abeni
80911362c7SPaolo Abeni if (!dst_cache->cache)
81911362c7SPaolo Abeni return NULL;
82911362c7SPaolo Abeni
83911362c7SPaolo Abeni idst = this_cpu_ptr(dst_cache->cache);
84911362c7SPaolo Abeni dst = dst_cache_per_cpu_get(dst_cache, idst);
85911362c7SPaolo Abeni if (!dst)
86911362c7SPaolo Abeni return NULL;
87911362c7SPaolo Abeni
88911362c7SPaolo Abeni *saddr = idst->in_saddr.s_addr;
8905d6d492SEric Dumazet return dst_rtable(dst);
90911362c7SPaolo Abeni }
91911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_get_ip4);
92911362c7SPaolo Abeni
dst_cache_set_ip4(struct dst_cache * dst_cache,struct dst_entry * dst,__be32 saddr)93911362c7SPaolo Abeni void dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
94911362c7SPaolo Abeni __be32 saddr)
95911362c7SPaolo Abeni {
96911362c7SPaolo Abeni struct dst_cache_pcpu *idst;
97911362c7SPaolo Abeni
98911362c7SPaolo Abeni if (!dst_cache->cache)
99911362c7SPaolo Abeni return;
100911362c7SPaolo Abeni
101911362c7SPaolo Abeni idst = this_cpu_ptr(dst_cache->cache);
102911362c7SPaolo Abeni dst_cache_per_cpu_dst_set(idst, dst, 0);
103911362c7SPaolo Abeni idst->in_saddr.s_addr = saddr;
104911362c7SPaolo Abeni }
105911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_set_ip4);
106911362c7SPaolo Abeni
107911362c7SPaolo Abeni #if IS_ENABLED(CONFIG_IPV6)
dst_cache_set_ip6(struct dst_cache * dst_cache,struct dst_entry * dst,const struct in6_addr * saddr)108911362c7SPaolo Abeni void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
1094c1342d9SJonathan Neuschäfer const struct in6_addr *saddr)
110911362c7SPaolo Abeni {
111911362c7SPaolo Abeni struct dst_cache_pcpu *idst;
112911362c7SPaolo Abeni
113911362c7SPaolo Abeni if (!dst_cache->cache)
114911362c7SPaolo Abeni return;
115911362c7SPaolo Abeni
116911362c7SPaolo Abeni idst = this_cpu_ptr(dst_cache->cache);
117e2d09e5aSEric Dumazet dst_cache_per_cpu_dst_set(idst, dst,
118e8dfd42cSEric Dumazet rt6_get_cookie(dst_rt6_info(dst)));
1194c1342d9SJonathan Neuschäfer idst->in6_saddr = *saddr;
120911362c7SPaolo Abeni }
121911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_set_ip6);
122911362c7SPaolo Abeni
dst_cache_get_ip6(struct dst_cache * dst_cache,struct in6_addr * saddr)123911362c7SPaolo Abeni struct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache,
124911362c7SPaolo Abeni struct in6_addr *saddr)
125911362c7SPaolo Abeni {
126911362c7SPaolo Abeni struct dst_cache_pcpu *idst;
127911362c7SPaolo Abeni struct dst_entry *dst;
128911362c7SPaolo Abeni
129911362c7SPaolo Abeni if (!dst_cache->cache)
130911362c7SPaolo Abeni return NULL;
131911362c7SPaolo Abeni
132911362c7SPaolo Abeni idst = this_cpu_ptr(dst_cache->cache);
133911362c7SPaolo Abeni dst = dst_cache_per_cpu_get(dst_cache, idst);
134911362c7SPaolo Abeni if (!dst)
135911362c7SPaolo Abeni return NULL;
136911362c7SPaolo Abeni
137911362c7SPaolo Abeni *saddr = idst->in6_saddr;
138911362c7SPaolo Abeni return dst;
139911362c7SPaolo Abeni }
140911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_get_ip6);
141911362c7SPaolo Abeni #endif
142911362c7SPaolo Abeni
dst_cache_init(struct dst_cache * dst_cache,gfp_t gfp)143911362c7SPaolo Abeni int dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
144911362c7SPaolo Abeni {
145911362c7SPaolo Abeni dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
146911362c7SPaolo Abeni gfp | __GFP_ZERO);
147911362c7SPaolo Abeni if (!dst_cache->cache)
148911362c7SPaolo Abeni return -ENOMEM;
149911362c7SPaolo Abeni
150911362c7SPaolo Abeni dst_cache_reset(dst_cache);
151911362c7SPaolo Abeni return 0;
152911362c7SPaolo Abeni }
153911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_init);
154911362c7SPaolo Abeni
dst_cache_destroy(struct dst_cache * dst_cache)155911362c7SPaolo Abeni void dst_cache_destroy(struct dst_cache *dst_cache)
156911362c7SPaolo Abeni {
157911362c7SPaolo Abeni int i;
158911362c7SPaolo Abeni
159911362c7SPaolo Abeni if (!dst_cache->cache)
160911362c7SPaolo Abeni return;
161911362c7SPaolo Abeni
162911362c7SPaolo Abeni for_each_possible_cpu(i)
163911362c7SPaolo Abeni dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
164911362c7SPaolo Abeni
165911362c7SPaolo Abeni free_percpu(dst_cache->cache);
166911362c7SPaolo Abeni }
167911362c7SPaolo Abeni EXPORT_SYMBOL_GPL(dst_cache_destroy);
16820ae1d6aSJason A. Donenfeld
dst_cache_reset_now(struct dst_cache * dst_cache)16920ae1d6aSJason A. Donenfeld void dst_cache_reset_now(struct dst_cache *dst_cache)
17020ae1d6aSJason A. Donenfeld {
17120ae1d6aSJason A. Donenfeld int i;
17220ae1d6aSJason A. Donenfeld
17320ae1d6aSJason A. Donenfeld if (!dst_cache->cache)
17420ae1d6aSJason A. Donenfeld return;
17520ae1d6aSJason A. Donenfeld
1763b09b2bdSEric Dumazet dst_cache_reset(dst_cache);
17720ae1d6aSJason A. Donenfeld for_each_possible_cpu(i) {
17820ae1d6aSJason A. Donenfeld struct dst_cache_pcpu *idst = per_cpu_ptr(dst_cache->cache, i);
17920ae1d6aSJason A. Donenfeld struct dst_entry *dst = idst->dst;
18020ae1d6aSJason A. Donenfeld
18120ae1d6aSJason A. Donenfeld idst->cookie = 0;
18220ae1d6aSJason A. Donenfeld idst->dst = NULL;
18320ae1d6aSJason A. Donenfeld dst_release(dst);
18420ae1d6aSJason A. Donenfeld }
18520ae1d6aSJason A. Donenfeld }
18620ae1d6aSJason A. Donenfeld EXPORT_SYMBOL_GPL(dst_cache_reset_now);
187