Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/core/dst_cache.c - dst entry cache
0004  *
0005  * Copyright (c) 2016 Paolo Abeni <pabeni@redhat.com>
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     /* the cache already hold a dst reference; it can't go away */
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);