1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * net/core/dst_cache.c - dst entry cache
5 * Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com>
8 #include <linux/kernel.h>
9 #include <linux/percpu.h>
10 #include <net/dst_cache.h>
11 #include <net/route.h>
12 #if IS_ENABLED(CONFIG_IPV6)
13 #include <net/ip6_fib.h>
15 #include <uapi/linux/in.h>
17 struct dst_cache_pcpu
{
18 unsigned long refresh_ts
;
19 struct dst_entry
*dst
;
22 struct in_addr in_saddr
;
23 struct in6_addr in6_saddr
;
27 static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu
*dst_cache
,
28 struct dst_entry
*dst
, u32 cookie
)
30 DEBUG_NET_WARN_ON_ONCE(!in_softirq());
31 dst_release(dst_cache
->dst
);
35 dst_cache
->cookie
= cookie
;
39 static struct dst_entry
*dst_cache_per_cpu_get(struct dst_cache
*dst_cache
,
40 struct dst_cache_pcpu
*idst
)
42 struct dst_entry
*dst
;
44 DEBUG_NET_WARN_ON_ONCE(!in_softirq());
49 /* the cache already hold a dst reference; it can't go away */
52 if (unlikely(!time_after(idst
->refresh_ts
,
53 READ_ONCE(dst_cache
->reset_ts
)) ||
54 (dst
->obsolete
&& !dst
->ops
->check(dst
, idst
->cookie
)))) {
55 dst_cache_per_cpu_dst_set(idst
, NULL
, 0);
62 idst
->refresh_ts
= jiffies
;
66 struct dst_entry
*dst_cache_get(struct dst_cache
*dst_cache
)
68 if (!dst_cache
->cache
)
71 return dst_cache_per_cpu_get(dst_cache
, this_cpu_ptr(dst_cache
->cache
));
73 EXPORT_SYMBOL_GPL(dst_cache_get
);
75 struct rtable
*dst_cache_get_ip4(struct dst_cache
*dst_cache
, __be32
*saddr
)
77 struct dst_cache_pcpu
*idst
;
78 struct dst_entry
*dst
;
80 if (!dst_cache
->cache
)
83 idst
= this_cpu_ptr(dst_cache
->cache
);
84 dst
= dst_cache_per_cpu_get(dst_cache
, idst
);
88 *saddr
= idst
->in_saddr
.s_addr
;
89 return dst_rtable(dst
);
91 EXPORT_SYMBOL_GPL(dst_cache_get_ip4
);
93 void dst_cache_set_ip4(struct dst_cache
*dst_cache
, struct dst_entry
*dst
,
96 struct dst_cache_pcpu
*idst
;
98 if (!dst_cache
->cache
)
101 idst
= this_cpu_ptr(dst_cache
->cache
);
102 dst_cache_per_cpu_dst_set(idst
, dst
, 0);
103 idst
->in_saddr
.s_addr
= saddr
;
105 EXPORT_SYMBOL_GPL(dst_cache_set_ip4
);
107 #if IS_ENABLED(CONFIG_IPV6)
108 void dst_cache_set_ip6(struct dst_cache
*dst_cache
, struct dst_entry
*dst
,
109 const struct in6_addr
*saddr
)
111 struct dst_cache_pcpu
*idst
;
113 if (!dst_cache
->cache
)
116 idst
= this_cpu_ptr(dst_cache
->cache
);
117 dst_cache_per_cpu_dst_set(idst
, dst
,
118 rt6_get_cookie(dst_rt6_info(dst
)));
119 idst
->in6_saddr
= *saddr
;
121 EXPORT_SYMBOL_GPL(dst_cache_set_ip6
);
123 struct dst_entry
*dst_cache_get_ip6(struct dst_cache
*dst_cache
,
124 struct in6_addr
*saddr
)
126 struct dst_cache_pcpu
*idst
;
127 struct dst_entry
*dst
;
129 if (!dst_cache
->cache
)
132 idst
= this_cpu_ptr(dst_cache
->cache
);
133 dst
= dst_cache_per_cpu_get(dst_cache
, idst
);
137 *saddr
= idst
->in6_saddr
;
140 EXPORT_SYMBOL_GPL(dst_cache_get_ip6
);
143 int dst_cache_init(struct dst_cache
*dst_cache
, gfp_t gfp
)
145 dst_cache
->cache
= alloc_percpu_gfp(struct dst_cache_pcpu
,
147 if (!dst_cache
->cache
)
150 dst_cache_reset(dst_cache
);
153 EXPORT_SYMBOL_GPL(dst_cache_init
);
155 void dst_cache_destroy(struct dst_cache
*dst_cache
)
159 if (!dst_cache
->cache
)
162 for_each_possible_cpu(i
)
163 dst_release(per_cpu_ptr(dst_cache
->cache
, i
)->dst
);
165 free_percpu(dst_cache
->cache
);
167 EXPORT_SYMBOL_GPL(dst_cache_destroy
);
169 void dst_cache_reset_now(struct dst_cache
*dst_cache
)
173 if (!dst_cache
->cache
)
176 dst_cache_reset(dst_cache
);
177 for_each_possible_cpu(i
) {
178 struct dst_cache_pcpu
*idst
= per_cpu_ptr(dst_cache
->cache
, i
);
179 struct dst_entry
*dst
= idst
->dst
;
186 EXPORT_SYMBOL_GPL(dst_cache_reset_now
);