Skip to content

Commit 7fda702

Browse files
lxindavem330
authored andcommitted
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: Xin Long <[email protected]> Acked-by: Neil Horman <[email protected]> Acked-by: Marcelo Ricardo Leitner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 00a636e commit 7fda702

File tree

5 files changed

+64
-50
lines changed

5 files changed

+64
-50
lines changed

include/net/sctp/sctp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ void sctp_backlog_migrate(struct sctp_association *assoc,
164164
struct sock *oldsk, struct sock *newsk);
165165
int sctp_transport_hashtable_init(void);
166166
void sctp_transport_hashtable_destroy(void);
167-
void sctp_hash_transport(struct sctp_transport *t);
167+
int sctp_hash_transport(struct sctp_transport *t);
168168
void sctp_unhash_transport(struct sctp_transport *t);
169169
struct sctp_transport *sctp_addrs_lookup_transport(
170170
struct net *net,

include/net/sctp/structs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ extern struct sctp_globals {
124124
/* This is the sctp port control hash. */
125125
struct sctp_bind_hashbucket *port_hashtable;
126126
/* This is the hash of all transports. */
127-
struct rhashtable transport_hashtable;
127+
struct rhltable transport_hashtable;
128128

129129
/* Sizes of above hashtables. */
130130
int ep_hashsize;
@@ -761,7 +761,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet)
761761
struct sctp_transport {
762762
/* A list of transports. */
763763
struct list_head transports;
764-
struct rhash_head node;
764+
struct rhlist_head node;
765765

766766
/* Reference counting. */
767767
atomic_t refcnt;

net/sctp/associola.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,11 +700,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
700700
/* Set the peer's active state. */
701701
peer->state = peer_state;
702702

703+
/* Add this peer into the transport hashtable */
704+
if (sctp_hash_transport(peer)) {
705+
sctp_transport_free(peer);
706+
return NULL;
707+
}
708+
703709
/* Attach the remote transport to our asoc. */
704710
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
705711
asoc->peer.transport_count++;
706-
/* Add this peer into the transport hashtable */
707-
sctp_hash_transport(peer);
708712

709713
/* If we do not yet have a primary path, set one. */
710714
if (!asoc->peer.primary_path) {

net/sctp/input.c

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -790,38 +790,27 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
790790

791791
/* rhashtable for transport */
792792
struct sctp_hash_cmp_arg {
793-
const struct sctp_endpoint *ep;
794-
const union sctp_addr *laddr;
795-
const union sctp_addr *paddr;
796-
const struct net *net;
793+
const union sctp_addr *paddr;
794+
const struct net *net;
795+
u16 lport;
797796
};
798797

799798
static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
800799
const void *ptr)
801800
{
802801
struct sctp_transport *t = (struct sctp_transport *)ptr;
803802
const struct sctp_hash_cmp_arg *x = arg->key;
804-
struct sctp_association *asoc;
805803
int err = 1;
806804

807805
if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
808806
return err;
809807
if (!sctp_transport_hold(t))
810808
return err;
811809

812-
asoc = t->asoc;
813-
if (!net_eq(sock_net(asoc->base.sk), x->net))
810+
if (!net_eq(sock_net(t->asoc->base.sk), x->net))
811+
goto out;
812+
if (x->lport != htons(t->asoc->base.bind_addr.port))
814813
goto out;
815-
if (x->ep) {
816-
if (x->ep != asoc->ep)
817-
goto out;
818-
} else {
819-
if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
820-
goto out;
821-
if (!sctp_bind_addr_match(&asoc->base.bind_addr,
822-
x->laddr, sctp_sk(asoc->base.sk)))
823-
goto out;
824-
}
825814

826815
err = 0;
827816
out:
@@ -851,11 +840,9 @@ static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
851840
const struct sctp_hash_cmp_arg *x = data;
852841
const union sctp_addr *paddr = x->paddr;
853842
const struct net *net = x->net;
854-
u16 lport;
843+
u16 lport = x->lport;
855844
u32 addr;
856845

857-
lport = x->ep ? htons(x->ep->base.bind_addr.port) :
858-
x->laddr->v4.sin_port;
859846
if (paddr->sa.sa_family == AF_INET6)
860847
addr = jhash(&paddr->v6.sin6_addr, 16, seed);
861848
else
@@ -875,69 +862,95 @@ static const struct rhashtable_params sctp_hash_params = {
875862

876863
int sctp_transport_hashtable_init(void)
877864
{
878-
return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
865+
return rhltable_init(&sctp_transport_hashtable, &sctp_hash_params);
879866
}
880867

881868
void sctp_transport_hashtable_destroy(void)
882869
{
883-
rhashtable_destroy(&sctp_transport_hashtable);
870+
rhltable_destroy(&sctp_transport_hashtable);
884871
}
885872

886-
void sctp_hash_transport(struct sctp_transport *t)
873+
int sctp_hash_transport(struct sctp_transport *t)
887874
{
888875
struct sctp_hash_cmp_arg arg;
876+
int err;
889877

890878
if (t->asoc->temp)
891-
return;
879+
return 0;
892880

893-
arg.ep = t->asoc->ep;
894-
arg.paddr = &t->ipaddr;
895881
arg.net = sock_net(t->asoc->base.sk);
882+
arg.paddr = &t->ipaddr;
883+
arg.lport = htons(t->asoc->base.bind_addr.port);
896884

897-
reinsert:
898-
if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
899-
&t->node, sctp_hash_params) == -EBUSY)
900-
goto reinsert;
885+
err = rhltable_insert_key(&sctp_transport_hashtable, &arg,
886+
&t->node, sctp_hash_params);
887+
if (err)
888+
pr_err_once("insert transport fail, errno %d\n", err);
889+
890+
return err;
901891
}
902892

903893
void sctp_unhash_transport(struct sctp_transport *t)
904894
{
905895
if (t->asoc->temp)
906896
return;
907897

908-
rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
909-
sctp_hash_params);
898+
rhltable_remove(&sctp_transport_hashtable, &t->node,
899+
sctp_hash_params);
910900
}
911901

902+
/* return a transport with holding it */
912903
struct sctp_transport *sctp_addrs_lookup_transport(
913904
struct net *net,
914905
const union sctp_addr *laddr,
915906
const union sctp_addr *paddr)
916907
{
908+
struct rhlist_head *tmp, *list;
909+
struct sctp_transport *t;
917910
struct sctp_hash_cmp_arg arg = {
918-
.ep = NULL,
919-
.laddr = laddr,
920911
.paddr = paddr,
921912
.net = net,
913+
.lport = laddr->v4.sin_port,
922914
};
923915

924-
return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
925-
sctp_hash_params);
916+
list = rhltable_lookup(&sctp_transport_hashtable, &arg,
917+
sctp_hash_params);
918+
919+
rhl_for_each_entry_rcu(t, tmp, list, node) {
920+
if (!sctp_transport_hold(t))
921+
continue;
922+
923+
if (sctp_bind_addr_match(&t->asoc->base.bind_addr,
924+
laddr, sctp_sk(t->asoc->base.sk)))
925+
return t;
926+
sctp_transport_put(t);
927+
}
928+
929+
return NULL;
926930
}
927931

932+
/* return a transport without holding it, as it's only used under sock lock */
928933
struct sctp_transport *sctp_epaddr_lookup_transport(
929934
const struct sctp_endpoint *ep,
930935
const union sctp_addr *paddr)
931936
{
932937
struct net *net = sock_net(ep->base.sk);
938+
struct rhlist_head *tmp, *list;
939+
struct sctp_transport *t;
933940
struct sctp_hash_cmp_arg arg = {
934-
.ep = ep,
935941
.paddr = paddr,
936942
.net = net,
943+
.lport = htons(ep->base.bind_addr.port),
937944
};
938945

939-
return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
940-
sctp_hash_params);
946+
list = rhltable_lookup(&sctp_transport_hashtable, &arg,
947+
sctp_hash_params);
948+
949+
rhl_for_each_entry_rcu(t, tmp, list, node)
950+
if (ep == t->asoc->ep)
951+
return t;
952+
953+
return NULL;
941954
}
942955

943956
/* Look up an association. */
@@ -951,7 +964,7 @@ static struct sctp_association *__sctp_lookup_association(
951964
struct sctp_association *asoc = NULL;
952965

953966
t = sctp_addrs_lookup_transport(net, local, peer);
954-
if (!t || !sctp_transport_hold(t))
967+
if (!t)
955968
goto out;
956969

957970
asoc = t->asoc;

net/sctp/socket.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,10 +4392,7 @@ int sctp_transport_walk_start(struct rhashtable_iter *iter)
43924392
{
43934393
int err;
43944394

4395-
err = rhashtable_walk_init(&sctp_transport_hashtable, iter,
4396-
GFP_KERNEL);
4397-
if (err)
4398-
return err;
4395+
rhltable_walk_enter(&sctp_transport_hashtable, iter);
43994396

44004397
err = rhashtable_walk_start(iter);
44014398
if (err && err != -EAGAIN) {
@@ -4479,7 +4476,7 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
44794476

44804477
rcu_read_lock();
44814478
transport = sctp_addrs_lookup_transport(net, laddr, paddr);
4482-
if (!transport || !sctp_transport_hold(transport))
4479+
if (!transport)
44834480
goto out;
44844481

44854482
rcu_read_unlock();

0 commit comments

Comments
 (0)