Commit 7fda702f authored by Xin Long's avatar Xin Long Committed by David S. Miller

sctp: use new rhlist interface on sctp transport rhashtable

Now sctp transport rhashtable uses hash(lport, dport, daddr) as the key
to hash a node to one chain. If in one host thousands of assocs connect
to one server with the same lport and different laddrs (although it's
not a normal case), all the transports would be hashed into the same
chain.

It may cause to keep returning -EBUSY when inserting a new node, as the
chain is too long and sctp inserts a transport node in a loop, which
could even lead to system hangs there.

The new rhlist interface works for this case that there are many nodes
with the same key in one chain. It puts them into a list then makes this
list be as a node of the chain.

This patch is to replace rhashtable_ interface with rhltable_ interface.
Since a chain would not be too long and it would not return -EBUSY with
this fix when inserting a node, the reinsert loop is also removed here.
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Acked-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 00a636e7
...@@ -164,7 +164,7 @@ void sctp_backlog_migrate(struct sctp_association *assoc, ...@@ -164,7 +164,7 @@ void sctp_backlog_migrate(struct sctp_association *assoc,
struct sock *oldsk, struct sock *newsk); struct sock *oldsk, struct sock *newsk);
int sctp_transport_hashtable_init(void); int sctp_transport_hashtable_init(void);
void sctp_transport_hashtable_destroy(void); void sctp_transport_hashtable_destroy(void);
void sctp_hash_transport(struct sctp_transport *t); int sctp_hash_transport(struct sctp_transport *t);
void sctp_unhash_transport(struct sctp_transport *t); void sctp_unhash_transport(struct sctp_transport *t);
struct sctp_transport *sctp_addrs_lookup_transport( struct sctp_transport *sctp_addrs_lookup_transport(
struct net *net, struct net *net,
......
...@@ -124,7 +124,7 @@ extern struct sctp_globals { ...@@ -124,7 +124,7 @@ extern struct sctp_globals {
/* This is the sctp port control hash. */ /* This is the sctp port control hash. */
struct sctp_bind_hashbucket *port_hashtable; struct sctp_bind_hashbucket *port_hashtable;
/* This is the hash of all transports. */ /* This is the hash of all transports. */
struct rhashtable transport_hashtable; struct rhltable transport_hashtable;
/* Sizes of above hashtables. */ /* Sizes of above hashtables. */
int ep_hashsize; int ep_hashsize;
...@@ -761,7 +761,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet) ...@@ -761,7 +761,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet)
struct sctp_transport { struct sctp_transport {
/* A list of transports. */ /* A list of transports. */
struct list_head transports; struct list_head transports;
struct rhash_head node; struct rhlist_head node;
/* Reference counting. */ /* Reference counting. */
atomic_t refcnt; atomic_t refcnt;
......
...@@ -700,11 +700,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -700,11 +700,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Set the peer's active state. */ /* Set the peer's active state. */
peer->state = peer_state; peer->state = peer_state;
/* Add this peer into the transport hashtable */
if (sctp_hash_transport(peer)) {
sctp_transport_free(peer);
return NULL;
}
/* Attach the remote transport to our asoc. */ /* Attach the remote transport to our asoc. */
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list); list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++; asoc->peer.transport_count++;
/* Add this peer into the transport hashtable */
sctp_hash_transport(peer);
/* If we do not yet have a primary path, set one. */ /* If we do not yet have a primary path, set one. */
if (!asoc->peer.primary_path) { if (!asoc->peer.primary_path) {
......
...@@ -790,10 +790,9 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, ...@@ -790,10 +790,9 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
/* rhashtable for transport */ /* rhashtable for transport */
struct sctp_hash_cmp_arg { struct sctp_hash_cmp_arg {
const struct sctp_endpoint *ep; const union sctp_addr *paddr;
const union sctp_addr *laddr; const struct net *net;
const union sctp_addr *paddr; u16 lport;
const struct net *net;
}; };
static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
...@@ -801,7 +800,6 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, ...@@ -801,7 +800,6 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
{ {
struct sctp_transport *t = (struct sctp_transport *)ptr; struct sctp_transport *t = (struct sctp_transport *)ptr;
const struct sctp_hash_cmp_arg *x = arg->key; const struct sctp_hash_cmp_arg *x = arg->key;
struct sctp_association *asoc;
int err = 1; int err = 1;
if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr)) if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
...@@ -809,19 +807,10 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, ...@@ -809,19 +807,10 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
if (!sctp_transport_hold(t)) if (!sctp_transport_hold(t))
return err; return err;
asoc = t->asoc; if (!net_eq(sock_net(t->asoc->base.sk), x->net))
if (!net_eq(sock_net(asoc->base.sk), x->net)) goto out;
if (x->lport != htons(t->asoc->base.bind_addr.port))
goto out; goto out;
if (x->ep) {
if (x->ep != asoc->ep)
goto out;
} else {
if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
goto out;
if (!sctp_bind_addr_match(&asoc->base.bind_addr,
x->laddr, sctp_sk(asoc->base.sk)))
goto out;
}
err = 0; err = 0;
out: out:
...@@ -851,11 +840,9 @@ static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed) ...@@ -851,11 +840,9 @@ static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
const struct sctp_hash_cmp_arg *x = data; const struct sctp_hash_cmp_arg *x = data;
const union sctp_addr *paddr = x->paddr; const union sctp_addr *paddr = x->paddr;
const struct net *net = x->net; const struct net *net = x->net;
u16 lport; u16 lport = x->lport;
u32 addr; u32 addr;
lport = x->ep ? htons(x->ep->base.bind_addr.port) :
x->laddr->v4.sin_port;
if (paddr->sa.sa_family == AF_INET6) if (paddr->sa.sa_family == AF_INET6)
addr = jhash(&paddr->v6.sin6_addr, 16, seed); addr = jhash(&paddr->v6.sin6_addr, 16, seed);
else else
...@@ -875,29 +862,32 @@ static const struct rhashtable_params sctp_hash_params = { ...@@ -875,29 +862,32 @@ static const struct rhashtable_params sctp_hash_params = {
int sctp_transport_hashtable_init(void) int sctp_transport_hashtable_init(void)
{ {
return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params); return rhltable_init(&sctp_transport_hashtable, &sctp_hash_params);
} }
void sctp_transport_hashtable_destroy(void) void sctp_transport_hashtable_destroy(void)
{ {
rhashtable_destroy(&sctp_transport_hashtable); rhltable_destroy(&sctp_transport_hashtable);
} }
void sctp_hash_transport(struct sctp_transport *t) int sctp_hash_transport(struct sctp_transport *t)
{ {
struct sctp_hash_cmp_arg arg; struct sctp_hash_cmp_arg arg;
int err;
if (t->asoc->temp) if (t->asoc->temp)
return; return 0;
arg.ep = t->asoc->ep;
arg.paddr = &t->ipaddr;
arg.net = sock_net(t->asoc->base.sk); arg.net = sock_net(t->asoc->base.sk);
arg.paddr = &t->ipaddr;
arg.lport = htons(t->asoc->base.bind_addr.port);
reinsert: err = rhltable_insert_key(&sctp_transport_hashtable, &arg,
if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg, &t->node, sctp_hash_params);
&t->node, sctp_hash_params) == -EBUSY) if (err)
goto reinsert; pr_err_once("insert transport fail, errno %d\n", err);
return err;
} }
void sctp_unhash_transport(struct sctp_transport *t) void sctp_unhash_transport(struct sctp_transport *t)
...@@ -905,39 +895,62 @@ void sctp_unhash_transport(struct sctp_transport *t) ...@@ -905,39 +895,62 @@ void sctp_unhash_transport(struct sctp_transport *t)
if (t->asoc->temp) if (t->asoc->temp)
return; return;
rhashtable_remove_fast(&sctp_transport_hashtable, &t->node, rhltable_remove(&sctp_transport_hashtable, &t->node,
sctp_hash_params); sctp_hash_params);
} }
/* return a transport with holding it */
struct sctp_transport *sctp_addrs_lookup_transport( struct sctp_transport *sctp_addrs_lookup_transport(
struct net *net, struct net *net,
const union sctp_addr *laddr, const union sctp_addr *laddr,
const union sctp_addr *paddr) const union sctp_addr *paddr)
{ {
struct rhlist_head *tmp, *list;
struct sctp_transport *t;
struct sctp_hash_cmp_arg arg = { struct sctp_hash_cmp_arg arg = {
.ep = NULL,
.laddr = laddr,
.paddr = paddr, .paddr = paddr,
.net = net, .net = net,
.lport = laddr->v4.sin_port,
}; };
return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg, list = rhltable_lookup(&sctp_transport_hashtable, &arg,
sctp_hash_params); sctp_hash_params);
rhl_for_each_entry_rcu(t, tmp, list, node) {
if (!sctp_transport_hold(t))
continue;
if (sctp_bind_addr_match(&t->asoc->base.bind_addr,
laddr, sctp_sk(t->asoc->base.sk)))
return t;
sctp_transport_put(t);
}
return NULL;
} }
/* return a transport without holding it, as it's only used under sock lock */
struct sctp_transport *sctp_epaddr_lookup_transport( struct sctp_transport *sctp_epaddr_lookup_transport(
const struct sctp_endpoint *ep, const struct sctp_endpoint *ep,
const union sctp_addr *paddr) const union sctp_addr *paddr)
{ {
struct net *net = sock_net(ep->base.sk); struct net *net = sock_net(ep->base.sk);
struct rhlist_head *tmp, *list;
struct sctp_transport *t;
struct sctp_hash_cmp_arg arg = { struct sctp_hash_cmp_arg arg = {
.ep = ep,
.paddr = paddr, .paddr = paddr,
.net = net, .net = net,
.lport = htons(ep->base.bind_addr.port),
}; };
return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg, list = rhltable_lookup(&sctp_transport_hashtable, &arg,
sctp_hash_params); sctp_hash_params);
rhl_for_each_entry_rcu(t, tmp, list, node)
if (ep == t->asoc->ep)
return t;
return NULL;
} }
/* Look up an association. */ /* Look up an association. */
...@@ -951,7 +964,7 @@ static struct sctp_association *__sctp_lookup_association( ...@@ -951,7 +964,7 @@ static struct sctp_association *__sctp_lookup_association(
struct sctp_association *asoc = NULL; struct sctp_association *asoc = NULL;
t = sctp_addrs_lookup_transport(net, local, peer); t = sctp_addrs_lookup_transport(net, local, peer);
if (!t || !sctp_transport_hold(t)) if (!t)
goto out; goto out;
asoc = t->asoc; asoc = t->asoc;
......
...@@ -4392,10 +4392,7 @@ int sctp_transport_walk_start(struct rhashtable_iter *iter) ...@@ -4392,10 +4392,7 @@ int sctp_transport_walk_start(struct rhashtable_iter *iter)
{ {
int err; int err;
err = rhashtable_walk_init(&sctp_transport_hashtable, iter, rhltable_walk_enter(&sctp_transport_hashtable, iter);
GFP_KERNEL);
if (err)
return err;
err = rhashtable_walk_start(iter); err = rhashtable_walk_start(iter);
if (err && err != -EAGAIN) { if (err && err != -EAGAIN) {
...@@ -4479,7 +4476,7 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *), ...@@ -4479,7 +4476,7 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
rcu_read_lock(); rcu_read_lock();
transport = sctp_addrs_lookup_transport(net, laddr, paddr); transport = sctp_addrs_lookup_transport(net, laddr, paddr);
if (!transport || !sctp_transport_hold(transport)) if (!transport)
goto out; goto out;
rcu_read_unlock(); rcu_read_unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment