162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/core/dst_cache.c - dst entry cache 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/percpu.h> 1062306a36Sopenharmony_ci#include <net/dst_cache.h> 1162306a36Sopenharmony_ci#include <net/route.h> 1262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1362306a36Sopenharmony_ci#include <net/ip6_fib.h> 1462306a36Sopenharmony_ci#endif 1562306a36Sopenharmony_ci#include <uapi/linux/in.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct dst_cache_pcpu { 1862306a36Sopenharmony_ci unsigned long refresh_ts; 1962306a36Sopenharmony_ci struct dst_entry *dst; 2062306a36Sopenharmony_ci u32 cookie; 2162306a36Sopenharmony_ci union { 2262306a36Sopenharmony_ci struct in_addr in_saddr; 2362306a36Sopenharmony_ci struct in6_addr in6_saddr; 2462306a36Sopenharmony_ci }; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache, 2862306a36Sopenharmony_ci struct dst_entry *dst, u32 cookie) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci dst_release(dst_cache->dst); 3162306a36Sopenharmony_ci if (dst) 3262306a36Sopenharmony_ci dst_hold(dst); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci dst_cache->cookie = cookie; 3562306a36Sopenharmony_ci dst_cache->dst = dst; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache, 3962306a36Sopenharmony_ci struct dst_cache_pcpu *idst) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct dst_entry *dst; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci dst = idst->dst; 4462306a36Sopenharmony_ci if (!dst) 4562306a36Sopenharmony_ci goto fail; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* the cache already hold a dst reference; it can't go away */ 4862306a36Sopenharmony_ci dst_hold(dst); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) || 5162306a36Sopenharmony_ci (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) { 5262306a36Sopenharmony_ci dst_cache_per_cpu_dst_set(idst, NULL, 0); 5362306a36Sopenharmony_ci dst_release(dst); 5462306a36Sopenharmony_ci goto fail; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci return dst; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cifail: 5962306a36Sopenharmony_ci idst->refresh_ts = jiffies; 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct dst_entry *dst_cache_get(struct dst_cache *dst_cache) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci if (!dst_cache->cache) 6662306a36Sopenharmony_ci return NULL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache)); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct dst_cache_pcpu *idst; 7562306a36Sopenharmony_ci struct dst_entry *dst; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!dst_cache->cache) 7862306a36Sopenharmony_ci return NULL; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci idst = this_cpu_ptr(dst_cache->cache); 8162306a36Sopenharmony_ci dst = dst_cache_per_cpu_get(dst_cache, idst); 8262306a36Sopenharmony_ci if (!dst) 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci *saddr = idst->in_saddr.s_addr; 8662306a36Sopenharmony_ci return container_of(dst, struct rtable, dst); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get_ip4); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst, 9162306a36Sopenharmony_ci __be32 saddr) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct dst_cache_pcpu *idst; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!dst_cache->cache) 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci idst = this_cpu_ptr(dst_cache->cache); 9962306a36Sopenharmony_ci dst_cache_per_cpu_dst_set(idst, dst, 0); 10062306a36Sopenharmony_ci idst->in_saddr.s_addr = saddr; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_set_ip4); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 10562306a36Sopenharmony_civoid dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst, 10662306a36Sopenharmony_ci const struct in6_addr *saddr) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct dst_cache_pcpu *idst; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!dst_cache->cache) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci idst = this_cpu_ptr(dst_cache->cache); 11462306a36Sopenharmony_ci dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst, 11562306a36Sopenharmony_ci rt6_get_cookie((struct rt6_info *)dst)); 11662306a36Sopenharmony_ci idst->in6_saddr = *saddr; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_set_ip6); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache, 12162306a36Sopenharmony_ci struct in6_addr *saddr) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct dst_cache_pcpu *idst; 12462306a36Sopenharmony_ci struct dst_entry *dst; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!dst_cache->cache) 12762306a36Sopenharmony_ci return NULL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci idst = this_cpu_ptr(dst_cache->cache); 13062306a36Sopenharmony_ci dst = dst_cache_per_cpu_get(dst_cache, idst); 13162306a36Sopenharmony_ci if (!dst) 13262306a36Sopenharmony_ci return NULL; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci *saddr = idst->in6_saddr; 13562306a36Sopenharmony_ci return dst; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_get_ip6); 13862306a36Sopenharmony_ci#endif 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu, 14362306a36Sopenharmony_ci gfp | __GFP_ZERO); 14462306a36Sopenharmony_ci if (!dst_cache->cache) 14562306a36Sopenharmony_ci return -ENOMEM; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dst_cache_reset(dst_cache); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_init); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_civoid dst_cache_destroy(struct dst_cache *dst_cache) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int i; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!dst_cache->cache) 15762306a36Sopenharmony_ci return; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for_each_possible_cpu(i) 16062306a36Sopenharmony_ci dst_release(per_cpu_ptr(dst_cache->cache, i)->dst); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci free_percpu(dst_cache->cache); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_destroy); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid dst_cache_reset_now(struct dst_cache *dst_cache) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!dst_cache->cache) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci dst_cache->reset_ts = jiffies; 17462306a36Sopenharmony_ci for_each_possible_cpu(i) { 17562306a36Sopenharmony_ci struct dst_cache_pcpu *idst = per_cpu_ptr(dst_cache->cache, i); 17662306a36Sopenharmony_ci struct dst_entry *dst = idst->dst; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci idst->cookie = 0; 17962306a36Sopenharmony_ci idst->dst = NULL; 18062306a36Sopenharmony_ci dst_release(dst); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dst_cache_reset_now); 184