Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* 3 : : * net/core/dst_cache.c - dst entry cache 4 : : * 5 : : * Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com> 6 : : */ 7 : : 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> 14 : : #endif 15 : : #include <uapi/linux/in.h> 16 : : 17 : : struct dst_cache_pcpu { 18 : : unsigned long refresh_ts; 19 : : struct dst_entry *dst; 20 : : u32 cookie; 21 : : union { 22 : : struct in_addr in_saddr; 23 : : struct in6_addr in6_saddr; 24 : : }; 25 : : }; 26 : : 27 : 0 : static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache, 28 : : struct dst_entry *dst, u32 cookie) 29 : : { 30 : 0 : dst_release(dst_cache->dst); 31 [ # # # # ]: 0 : if (dst) 32 : 0 : dst_hold(dst); 33 : : 34 : 0 : dst_cache->cookie = cookie; 35 : 0 : dst_cache->dst = dst; 36 : : } 37 : : 38 : : static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache, 39 : : struct dst_cache_pcpu *idst) 40 : : { 41 : : struct dst_entry *dst; 42 : : 43 : : dst = idst->dst; 44 : : if (!dst) 45 : : goto fail; 46 : : 47 : : /* the cache already hold a dst reference; it can't go away */ 48 : : dst_hold(dst); 49 : : 50 : : if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) || 51 : : (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) { 52 : : dst_cache_per_cpu_dst_set(idst, NULL, 0); 53 : : dst_release(dst); 54 : : goto fail; 55 : : } 56 : : return dst; 57 : : 58 : : fail: 59 : : idst->refresh_ts = jiffies; 60 : : return NULL; 61 : : } 62 : : 63 : 0 : struct dst_entry *dst_cache_get(struct dst_cache *dst_cache) 64 : : { 65 [ # # ]: 0 : if (!dst_cache->cache) 66 : : return NULL; 67 : : 68 : 0 : return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache)); 69 : : } 70 : : EXPORT_SYMBOL_GPL(dst_cache_get); 71 : : 72 : 0 : struct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr) 73 : : { 74 : 0 : struct dst_cache_pcpu *idst; 75 : 0 : struct dst_entry *dst; 76 : : 77 [ # # ]: 0 : if (!dst_cache->cache) 78 : : return NULL; 79 : : 80 : 0 : idst = this_cpu_ptr(dst_cache->cache); 81 : 0 : dst = dst_cache_per_cpu_get(dst_cache, idst); 82 [ # # ]: 0 : if (!dst) 83 : : return NULL; 84 : : 85 : 0 : *saddr = idst->in_saddr.s_addr; 86 : 0 : return container_of(dst, struct rtable, dst); 87 : : } 88 : : EXPORT_SYMBOL_GPL(dst_cache_get_ip4); 89 : : 90 : 0 : void dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst, 91 : : __be32 saddr) 92 : : { 93 : 0 : struct dst_cache_pcpu *idst; 94 : : 95 [ # # ]: 0 : if (!dst_cache->cache) 96 : : return; 97 : : 98 : 0 : idst = this_cpu_ptr(dst_cache->cache); 99 : 0 : dst_cache_per_cpu_dst_set(idst, dst, 0); 100 : 0 : idst->in_saddr.s_addr = saddr; 101 : : } 102 : : EXPORT_SYMBOL_GPL(dst_cache_set_ip4); 103 : : 104 : : #if IS_ENABLED(CONFIG_IPV6) 105 : 0 : void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst, 106 : : const struct in6_addr *saddr) 107 : : { 108 : 0 : struct dst_cache_pcpu *idst; 109 : : 110 [ # # ]: 0 : if (!dst_cache->cache) 111 : : return; 112 : : 113 : 0 : idst = this_cpu_ptr(dst_cache->cache); 114 : 0 : dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst, 115 : : rt6_get_cookie((struct rt6_info *)dst)); 116 : 0 : idst->in6_saddr = *saddr; 117 : : } 118 : : EXPORT_SYMBOL_GPL(dst_cache_set_ip6); 119 : : 120 : 0 : struct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache, 121 : : struct in6_addr *saddr) 122 : : { 123 : 0 : struct dst_cache_pcpu *idst; 124 : 0 : struct dst_entry *dst; 125 : : 126 [ # # ]: 0 : if (!dst_cache->cache) 127 : : return NULL; 128 : : 129 : 0 : idst = this_cpu_ptr(dst_cache->cache); 130 : 0 : dst = dst_cache_per_cpu_get(dst_cache, idst); 131 [ # # ]: 0 : if (!dst) 132 : : return NULL; 133 : : 134 : 0 : *saddr = idst->in6_saddr; 135 : 0 : return dst; 136 : : } 137 : : EXPORT_SYMBOL_GPL(dst_cache_get_ip6); 138 : : #endif 139 : : 140 : 21 : int dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp) 141 : : { 142 : 21 : dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu, 143 : : gfp | __GFP_ZERO); 144 [ + - ]: 21 : if (!dst_cache->cache) 145 : : return -ENOMEM; 146 : : 147 : 21 : dst_cache_reset(dst_cache); 148 : 21 : return 0; 149 : : } 150 : : EXPORT_SYMBOL_GPL(dst_cache_init); 151 : : 152 : 0 : void dst_cache_destroy(struct dst_cache *dst_cache) 153 : : { 154 : 0 : int i; 155 : : 156 [ # # ]: 0 : if (!dst_cache->cache) 157 : : return; 158 : : 159 [ # # ]: 0 : for_each_possible_cpu(i) 160 : 0 : dst_release(per_cpu_ptr(dst_cache->cache, i)->dst); 161 : : 162 : 0 : free_percpu(dst_cache->cache); 163 : : } 164 : : EXPORT_SYMBOL_GPL(dst_cache_destroy);