summaryrefslogtreecommitdiff
path: root/net/ipv4/inetpeer.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/inetpeer.c')
-rw-r--r--net/ipv4/inetpeer.c38
1 files changed, 35 insertions, 3 deletions
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index 5b957a831e7c..adf6dc8a95b4 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -21,6 +21,7 @@
#include <net/ip.h>
#include <net/inetpeer.h>
#include <net/secure_seq.h>
+#include <linux/siphash.h>
/*
* Theory of operations.
@@ -52,6 +53,34 @@
*/
static struct kmem_cache *peer_cachep __ro_after_init;
+static siphash_aligned_key_t inetpeer_hash_key __read_mostly;
+
+static u64 inetpeer_addr_hash(const struct inetpeer_addr *a)
+{
+ net_get_random_once(&inetpeer_hash_key, sizeof(inetpeer_hash_key));
+
+ if (a->family == AF_INET)
+ return siphash_2u32((__force u32)a->a4.addr, a->a4.vif,
+ &inetpeer_hash_key);
+
+ return siphash_4u32((__force u32)a->a6.s6_addr32[0],
+ (__force u32)a->a6.s6_addr32[1],
+ (__force u32)a->a6.s6_addr32[2],
+ (__force u32)a->a6.s6_addr32[3],
+ &inetpeer_hash_key);
+}
+
+static int inetpeer_entry_cmp(u64 dhash,
+ const struct inetpeer_addr *daddr,
+ const struct inet_peer *p)
+{
+ if (dhash < p->hash)
+ return -1;
+ if (dhash > p->hash)
+ return 1;
+
+ return inetpeer_addr_cmp(daddr, &p->daddr);
+}
void inet_peer_base_init(struct inet_peer_base *bp)
{
@@ -84,6 +113,7 @@ void __init inet_initpeers(void)
/* Called with rcu_read_lock() or base->lock held */
static struct inet_peer *lookup(const struct inetpeer_addr *daddr,
+ u64 dhash,
struct inet_peer_base *base,
unsigned int seq,
struct inet_peer *gc_stack[],
@@ -105,7 +135,7 @@ static struct inet_peer *lookup(const struct inetpeer_addr *daddr,
break;
parent = next;
p = rb_entry(parent, struct inet_peer, rb_node);
- cmp = inetpeer_addr_cmp(daddr, &p->daddr);
+ cmp = inetpeer_entry_cmp(dhash, daddr, p);
if (cmp == 0) {
now = jiffies;
if (READ_ONCE(p->dtime) != now)
@@ -170,6 +200,7 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
const struct inetpeer_addr *daddr)
{
struct inet_peer *p, *gc_stack[PEER_MAX_GC];
+ u64 dhash = inetpeer_addr_hash(daddr);
struct rb_node **pp, *parent;
unsigned int gc_cnt, seq;
@@ -177,7 +208,7 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
* Because of a concurrent writer, we might not find an existing entry.
*/
seq = read_seqbegin(&base->lock);
- p = lookup(daddr, base, seq, NULL, &gc_cnt, &parent, &pp);
+ p = lookup(daddr, dhash, base, seq, NULL, &gc_cnt, &parent, &pp);
/* Make sure tree was not modified during our lookup. */
if (p && !read_seqretry(&base->lock, seq))
@@ -190,11 +221,12 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
write_seqlock_bh(&base->lock);
gc_cnt = 0;
- p = lookup(daddr, base, seq, gc_stack, &gc_cnt, &parent, &pp);
+ p = lookup(daddr, dhash, base, seq, gc_stack, &gc_cnt, &parent, &pp);
if (!p) {
p = kmem_cache_alloc(peer_cachep, GFP_ATOMIC);
if (p) {
p->daddr = *daddr;
+ p->hash = dhash;
p->dtime = (__u32)jiffies;
refcount_set(&p->refcnt, 1);
atomic_set(&p->rid, 0);