0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/percpu.h>
0010 #include <net/dst_cache.h>
0011 #include <net/route.h>
0012 #if IS_ENABLED(CONFIG_IPV6)
0013 #include <net/ip6_fib.h>
0014 #endif
0015 #include <uapi/linux/in.h>
0016
0017 struct dst_cache_pcpu {
0018 unsigned long refresh_ts;
0019 struct dst_entry *dst;
0020 u32 cookie;
0021 union {
0022 struct in_addr in_saddr;
0023 struct in6_addr in6_saddr;
0024 };
0025 };
0026
0027 static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
0028 struct dst_entry *dst, u32 cookie)
0029 {
0030 dst_release(dst_cache->dst);
0031 if (dst)
0032 dst_hold(dst);
0033
0034 dst_cache->cookie = cookie;
0035 dst_cache->dst = dst;
0036 }
0037
0038 static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache,
0039 struct dst_cache_pcpu *idst)
0040 {
0041 struct dst_entry *dst;
0042
0043 dst = idst->dst;
0044 if (!dst)
0045 goto fail;
0046
0047
0048 dst_hold(dst);
0049
0050 if (unlikely(!time_after(idst->refresh_ts, dst_cache->reset_ts) ||
0051 (dst->obsolete && !dst->ops->check(dst, idst->cookie)))) {
0052 dst_cache_per_cpu_dst_set(idst, NULL, 0);
0053 dst_release(dst);
0054 goto fail;
0055 }
0056 return dst;
0057
0058 fail:
0059 idst->refresh_ts = jiffies;
0060 return NULL;
0061 }
0062
0063 struct dst_entry *dst_cache_get(struct dst_cache *dst_cache)
0064 {
0065 if (!dst_cache->cache)
0066 return NULL;
0067
0068 return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
0069 }
0070 EXPORT_SYMBOL_GPL(dst_cache_get);
0071
0072 struct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
0073 {
0074 struct dst_cache_pcpu *idst;
0075 struct dst_entry *dst;
0076
0077 if (!dst_cache->cache)
0078 return NULL;
0079
0080 idst = this_cpu_ptr(dst_cache->cache);
0081 dst = dst_cache_per_cpu_get(dst_cache, idst);
0082 if (!dst)
0083 return NULL;
0084
0085 *saddr = idst->in_saddr.s_addr;
0086 return container_of(dst, struct rtable, dst);
0087 }
0088 EXPORT_SYMBOL_GPL(dst_cache_get_ip4);
0089
0090 void dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
0091 __be32 saddr)
0092 {
0093 struct dst_cache_pcpu *idst;
0094
0095 if (!dst_cache->cache)
0096 return;
0097
0098 idst = this_cpu_ptr(dst_cache->cache);
0099 dst_cache_per_cpu_dst_set(idst, dst, 0);
0100 idst->in_saddr.s_addr = saddr;
0101 }
0102 EXPORT_SYMBOL_GPL(dst_cache_set_ip4);
0103
0104 #if IS_ENABLED(CONFIG_IPV6)
0105 void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
0106 const struct in6_addr *saddr)
0107 {
0108 struct dst_cache_pcpu *idst;
0109
0110 if (!dst_cache->cache)
0111 return;
0112
0113 idst = this_cpu_ptr(dst_cache->cache);
0114 dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
0115 rt6_get_cookie((struct rt6_info *)dst));
0116 idst->in6_saddr = *saddr;
0117 }
0118 EXPORT_SYMBOL_GPL(dst_cache_set_ip6);
0119
0120 struct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache,
0121 struct in6_addr *saddr)
0122 {
0123 struct dst_cache_pcpu *idst;
0124 struct dst_entry *dst;
0125
0126 if (!dst_cache->cache)
0127 return NULL;
0128
0129 idst = this_cpu_ptr(dst_cache->cache);
0130 dst = dst_cache_per_cpu_get(dst_cache, idst);
0131 if (!dst)
0132 return NULL;
0133
0134 *saddr = idst->in6_saddr;
0135 return dst;
0136 }
0137 EXPORT_SYMBOL_GPL(dst_cache_get_ip6);
0138 #endif
0139
0140 int dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
0141 {
0142 dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
0143 gfp | __GFP_ZERO);
0144 if (!dst_cache->cache)
0145 return -ENOMEM;
0146
0147 dst_cache_reset(dst_cache);
0148 return 0;
0149 }
0150 EXPORT_SYMBOL_GPL(dst_cache_init);
0151
0152 void dst_cache_destroy(struct dst_cache *dst_cache)
0153 {
0154 int i;
0155
0156 if (!dst_cache->cache)
0157 return;
0158
0159 for_each_possible_cpu(i)
0160 dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
0161
0162 free_percpu(dst_cache->cache);
0163 }
0164 EXPORT_SYMBOL_GPL(dst_cache_destroy);
0165
0166 void dst_cache_reset_now(struct dst_cache *dst_cache)
0167 {
0168 int i;
0169
0170 if (!dst_cache->cache)
0171 return;
0172
0173 dst_cache->reset_ts = jiffies;
0174 for_each_possible_cpu(i) {
0175 struct dst_cache_pcpu *idst = per_cpu_ptr(dst_cache->cache, i);
0176 struct dst_entry *dst = idst->dst;
0177
0178 idst->cookie = 0;
0179 idst->dst = NULL;
0180 dst_release(dst);
0181 }
0182 }
0183 EXPORT_SYMBOL_GPL(dst_cache_reset_now);