linux/net/core/dst_cache.c
<<
>>
Prefs
   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
  17struct 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
  27static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache,
  28                                      struct dst_entry *dst, u32 cookie)
  29{
  30        dst_release(dst_cache->dst);
  31        if (dst)
  32                dst_hold(dst);
  33
  34        dst_cache->cookie = cookie;
  35        dst_cache->dst = dst;
  36}
  37
  38static 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
  58fail:
  59        idst->refresh_ts = jiffies;
  60        return NULL;
  61}
  62
  63struct dst_entry *dst_cache_get(struct dst_cache *dst_cache)
  64{
  65        if (!dst_cache->cache)
  66                return NULL;
  67
  68        return dst_cache_per_cpu_get(dst_cache, this_cpu_ptr(dst_cache->cache));
  69}
  70EXPORT_SYMBOL_GPL(dst_cache_get);
  71
  72struct rtable *dst_cache_get_ip4(struct dst_cache *dst_cache, __be32 *saddr)
  73{
  74        struct dst_cache_pcpu *idst;
  75        struct dst_entry *dst;
  76
  77        if (!dst_cache->cache)
  78                return NULL;
  79
  80        idst = this_cpu_ptr(dst_cache->cache);
  81        dst = dst_cache_per_cpu_get(dst_cache, idst);
  82        if (!dst)
  83                return NULL;
  84
  85        *saddr = idst->in_saddr.s_addr;
  86        return container_of(dst, struct rtable, dst);
  87}
  88EXPORT_SYMBOL_GPL(dst_cache_get_ip4);
  89
  90void dst_cache_set_ip4(struct dst_cache *dst_cache, struct dst_entry *dst,
  91                       __be32 saddr)
  92{
  93        struct dst_cache_pcpu *idst;
  94
  95        if (!dst_cache->cache)
  96                return;
  97
  98        idst = this_cpu_ptr(dst_cache->cache);
  99        dst_cache_per_cpu_dst_set(idst, dst, 0);
 100        idst->in_saddr.s_addr = saddr;
 101}
 102EXPORT_SYMBOL_GPL(dst_cache_set_ip4);
 103
 104#if IS_ENABLED(CONFIG_IPV6)
 105void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst,
 106                       const struct in6_addr *saddr)
 107{
 108        struct dst_cache_pcpu *idst;
 109
 110        if (!dst_cache->cache)
 111                return;
 112
 113        idst = this_cpu_ptr(dst_cache->cache);
 114        dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst,
 115                                  rt6_get_cookie((struct rt6_info *)dst));
 116        idst->in6_saddr = *saddr;
 117}
 118EXPORT_SYMBOL_GPL(dst_cache_set_ip6);
 119
 120struct dst_entry *dst_cache_get_ip6(struct dst_cache *dst_cache,
 121                                    struct in6_addr *saddr)
 122{
 123        struct dst_cache_pcpu *idst;
 124        struct dst_entry *dst;
 125
 126        if (!dst_cache->cache)
 127                return NULL;
 128
 129        idst = this_cpu_ptr(dst_cache->cache);
 130        dst = dst_cache_per_cpu_get(dst_cache, idst);
 131        if (!dst)
 132                return NULL;
 133
 134        *saddr = idst->in6_saddr;
 135        return dst;
 136}
 137EXPORT_SYMBOL_GPL(dst_cache_get_ip6);
 138#endif
 139
 140int dst_cache_init(struct dst_cache *dst_cache, gfp_t gfp)
 141{
 142        dst_cache->cache = alloc_percpu_gfp(struct dst_cache_pcpu,
 143                                            gfp | __GFP_ZERO);
 144        if (!dst_cache->cache)
 145                return -ENOMEM;
 146
 147        dst_cache_reset(dst_cache);
 148        return 0;
 149}
 150EXPORT_SYMBOL_GPL(dst_cache_init);
 151
 152void dst_cache_destroy(struct dst_cache *dst_cache)
 153{
 154        int i;
 155
 156        if (!dst_cache->cache)
 157                return;
 158
 159        for_each_possible_cpu(i)
 160                dst_release(per_cpu_ptr(dst_cache->cache, i)->dst);
 161
 162        free_percpu(dst_cache->cache);
 163}
 164EXPORT_SYMBOL_GPL(dst_cache_destroy);
 165