18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/core/dst_cache.c - dst entry cache
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/percpu.h>
108c2ecf20Sopenharmony_ci#include <net/dst_cache.h>
118c2ecf20Sopenharmony_ci#include <net/route.h>
128c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
138c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
148c2ecf20Sopenharmony_ci#endif
158c2ecf20Sopenharmony_ci#include <uapi/linux/in.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct dst_cache_pcpu {
188c2ecf20Sopenharmony_ci	unsigned long refresh_ts;
198c2ecf20Sopenharmony_ci	struct dst_entry *dst;
208c2ecf20Sopenharmony_ci	u32 cookie;
218c2ecf20Sopenharmony_ci	union {
228c2ecf20Sopenharmony_ci		struct in_addr in_saddr;
238c2ecf20Sopenharmony_ci		struct in6_addr in6_saddr;
248c2ecf20Sopenharmony_ci	};
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
288c2ecf20Sopenharmony_ci				      struct dst_entry *dst, u32 cookie)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	dst_release(dst_cache->dst);
318c2ecf20Sopenharmony_ci	if (dst)
328c2ecf20Sopenharmony_ci		dst_hold(dst);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	dst_cache->cookie = cookie;
358c2ecf20Sopenharmony_ci	dst_cache->dst = dst;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache,
398c2ecf20Sopenharmony_ci					       struct dst_cache_pcpu *idst)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct dst_entry *dst;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	dst = idst->dst;
448c2ecf20Sopenharmony_ci	if (!dst)
458c2ecf20Sopenharmony_ci		goto fail;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* the cache already hold a dst reference; it can't go away */
488c2ecf20Sopenharmony_ci	dst_hold(dst);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) ||
518c2ecf20Sopenharmony_ci		     (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) {
528c2ecf20Sopenharmony_ci		dst_cache_per_cpu_dst_set(idst, NULL, 0);
538c2ecf20Sopenharmony_ci		dst_release(dst);
548c2ecf20Sopenharmony_ci		goto fail;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	return dst;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cifail:
598c2ecf20Sopenharmony_ci	idst->refresh_ts = jiffies;
608c2ecf20Sopenharmony_ci	return NULL;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct dst_entry *dst_cache_get(struct dst_cache *dst_cache)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
668c2ecf20Sopenharmony_ci		return NULL;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct dst_cache_pcpu *idst;
758c2ecf20Sopenharmony_ci	struct dst_entry *dst;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
788c2ecf20Sopenharmony_ci		return NULL;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	idst = this_cpu_ptr(dst_cache->cache);
818c2ecf20Sopenharmony_ci	dst = dst_cache_per_cpu_get(dst_cache, idst);
828c2ecf20Sopenharmony_ci	if (!dst)
838c2ecf20Sopenharmony_ci		return NULL;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	*saddr = idst->in_saddr.s_addr;
868c2ecf20Sopenharmony_ci	return container_of(dst, struct rtable, dst);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get_ip4);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_civoid dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
918c2ecf20Sopenharmony_ci		       __be32 saddr)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct dst_cache_pcpu *idst;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
968c2ecf20Sopenharmony_ci		return;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	idst = this_cpu_ptr(dst_cache->cache);
998c2ecf20Sopenharmony_ci	dst_cache_per_cpu_dst_set(idst, dst, 0);
1008c2ecf20Sopenharmony_ci	idst->in_saddr.s_addr = saddr;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_set_ip4);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1058c2ecf20Sopenharmony_civoid dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
1068c2ecf20Sopenharmony_ci		       const struct in6_addr *saddr)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct dst_cache_pcpu *idst;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
1118c2ecf20Sopenharmony_ci		return;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	idst = this_cpu_ptr(dst_cache->cache);
1148c2ecf20Sopenharmony_ci	dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
1158c2ecf20Sopenharmony_ci				  rt6_get_cookie((struct rt6_info *)dst));
1168c2ecf20Sopenharmony_ci	idst->in6_saddr = *saddr;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_set_ip6);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache,
1218c2ecf20Sopenharmony_ci				    struct in6_addr *saddr)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct dst_cache_pcpu *idst;
1248c2ecf20Sopenharmony_ci	struct dst_entry *dst;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
1278c2ecf20Sopenharmony_ci		return NULL;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	idst = this_cpu_ptr(dst_cache->cache);
1308c2ecf20Sopenharmony_ci	dst = dst_cache_per_cpu_get(dst_cache, idst);
1318c2ecf20Sopenharmony_ci	if (!dst)
1328c2ecf20Sopenharmony_ci		return NULL;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	*saddr = idst->in6_saddr;
1358c2ecf20Sopenharmony_ci	return dst;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get_ip6);
1388c2ecf20Sopenharmony_ci#endif
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciint dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
1438c2ecf20Sopenharmony_ci					    gfp | __GFP_ZERO);
1448c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
1458c2ecf20Sopenharmony_ci		return -ENOMEM;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	dst_cache_reset(dst_cache);
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_init);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid dst_cache_destroy(struct dst_cache *dst_cache)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	int i;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for_each_possible_cpu(i)
1608c2ecf20Sopenharmony_ci		dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	free_percpu(dst_cache->cache);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_destroy);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid dst_cache_reset_now(struct dst_cache *dst_cache)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	int i;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (!dst_cache->cache)
1718c2ecf20Sopenharmony_ci		return;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	dst_cache->reset_ts = jiffies;
1748c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
1758c2ecf20Sopenharmony_ci		struct dst_cache_pcpu *idst = per_cpu_ptr(dst_cache->cache, i);
1768c2ecf20Sopenharmony_ci		struct dst_entry *dst = idst->dst;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		idst->cookie = 0;
1798c2ecf20Sopenharmony_ci		idst->dst = NULL;
1808c2ecf20Sopenharmony_ci		dst_release(dst);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_reset_now);
184