Commit 33c15297 authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp-transport-rhashtable'

Xin Long says:

====================
sctp: use transport hashtable to replace association's with rhashtable

for telecom center, the usual case is that a server is connected by thousands
of clients. but if the server with only one enpoint(udp style) use the same
sport and dport to communicate with every clients, and every assoc in server
will be hashed in the same chain of global assoc hashtable due to currently we
choose dport and sport as the hash key.

when a packet is received, sctp_rcv try to find the assoc with sport and dport,
since that chain is too long to find it fast, it make the performance turn to
very low, some test data is as follow:

in server:
$./ss [start a udp style server there]
in client:
$./cc [start 2500 sockets to connect server with same port and different ip,
       and use one of them to send data to server]

===== test on net-next
-- perf top
server:
  55.73%  [kernel]             [k] sctp_assoc_is_match
   6.80%  [kernel]             [k] sctp_assoc_lookup_paddr
   4.81%  [kernel]             [k] sctp_v4_cmp_addr
   3.12%  [kernel]             [k] _raw_spin_unlock_irqrestore
   1.94%  [kernel]             [k] sctp_cmp_addr_exact

client:
  46.01%  [kernel]                    [k] sctp_endpoint_lookup_assoc
   5.55%  libc-2.17.so                [.] __libc_calloc
   5.39%  libc-2.17.so                [.] _int_free
   3.92%  libc-2.17.so                [.] _int_malloc
   3.23%  [kernel]                    [k] __memset

-- spent time
time is 487s, send pkt is 10000000

we need to change the way to calculate the hash key, to use lport +
rport + paddr as the hash key can avoid this issue.

besides, this patchset will use transport hashtable to replace
association hashtable to lookup with rhashtable api. get transport
first then get association by t->asoc. and also it will make tcp
style work better.

===== test with this patchset:
-- perf top
server:
  15.98%  [kernel]                 [k] _raw_spin_unlock_irqrestore
   9.92%  [kernel]                 [k] __pv_queued_spin_lock_slowpath
   7.22%  [kernel]                 [k] copy_user_generic_string
   2.38%  libpthread-2.17.so       [.] __recvmsg_nocancel
   1.88%  [kernel]                 [k] sctp_recvmsg

client:
  11.90%  [kernel]                   [k] sctp_hash_cmp
   8.52%  [kernel]                   [k] rht_deferred_worker
   4.94%  [kernel]                   [k] __pv_queued_spin_lock_slowpath
   3.95%  [kernel]                   [k] sctp_bind_addr_match
   2.49%  [kernel]                   [k] __memset

-- spent time
time is 22s, send pkt is 10000000
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6a5ef90c c79c0666
...@@ -126,8 +126,6 @@ int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg); ...@@ -126,8 +126,6 @@ int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
*/ */
int sctp_rcv(struct sk_buff *skb); int sctp_rcv(struct sk_buff *skb);
void sctp_v4_err(struct sk_buff *skb, u32 info); void sctp_v4_err(struct sk_buff *skb, u32 info);
void sctp_hash_established(struct sctp_association *);
void sctp_unhash_established(struct sctp_association *);
void sctp_hash_endpoint(struct sctp_endpoint *); void sctp_hash_endpoint(struct sctp_endpoint *);
void sctp_unhash_endpoint(struct sctp_endpoint *); void sctp_unhash_endpoint(struct sctp_endpoint *);
struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *, struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
...@@ -143,6 +141,17 @@ void sctp_icmp_proto_unreachable(struct sock *sk, ...@@ -143,6 +141,17 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
struct sctp_transport *t); struct sctp_transport *t);
void sctp_backlog_migrate(struct sctp_association *assoc, 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);
void sctp_transport_hashtable_destroy(void);
void sctp_hash_transport(struct sctp_transport *t);
void sctp_unhash_transport(struct sctp_transport *t);
struct sctp_transport *sctp_addrs_lookup_transport(
struct net *net,
const union sctp_addr *laddr,
const union sctp_addr *paddr);
struct sctp_transport *sctp_epaddr_lookup_transport(
const struct sctp_endpoint *ep,
const union sctp_addr *paddr);
/* /*
* sctp/proc.c * sctp/proc.c
...@@ -519,25 +528,6 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport) ...@@ -519,25 +528,6 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport)
return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1); return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1);
} }
/* This is the hash function for the association hash table. */
static inline int sctp_assoc_hashfn(struct net *net, __u16 lport, __u16 rport)
{
int h = (lport << 16) + rport + net_hash_mix(net);
h ^= h>>8;
return h & (sctp_assoc_hashsize - 1);
}
/* This is the hash function for the association hash table. This is
* not used yet, but could be used as a better hash function when
* we have a vtag.
*/
static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
{
int h = (lport << 16) + rport;
h ^= vtag;
return h & (sctp_assoc_hashsize - 1);
}
#define sctp_for_each_hentry(epb, head) \ #define sctp_for_each_hentry(epb, head) \
hlist_for_each_entry(epb, head, node) hlist_for_each_entry(epb, head, node)
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#define __sctp_structs_h__ #define __sctp_structs_h__
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/rhashtable.h>
#include <linux/socket.h> /* linux/in.h needs this!! */ #include <linux/socket.h> /* linux/in.h needs this!! */
#include <linux/in.h> /* We get struct sockaddr_in. */ #include <linux/in.h> /* We get struct sockaddr_in. */
#include <linux/in6.h> /* We get struct in6_addr */ #include <linux/in6.h> /* We get struct in6_addr */
...@@ -119,14 +120,13 @@ extern struct sctp_globals { ...@@ -119,14 +120,13 @@ extern struct sctp_globals {
/* This is the hash of all endpoints. */ /* This is the hash of all endpoints. */
struct sctp_hashbucket *ep_hashtable; struct sctp_hashbucket *ep_hashtable;
/* This is the hash of all associations. */
struct sctp_hashbucket *assoc_hashtable;
/* 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. */
struct rhashtable transport_hashtable;
/* Sizes of above hashtables. */ /* Sizes of above hashtables. */
int ep_hashsize; int ep_hashsize;
int assoc_hashsize;
int port_hashsize; int port_hashsize;
/* Default initialization values to be applied to new associations. */ /* Default initialization values to be applied to new associations. */
...@@ -143,10 +143,9 @@ extern struct sctp_globals { ...@@ -143,10 +143,9 @@ extern struct sctp_globals {
#define sctp_address_families (sctp_globals.address_families) #define sctp_address_families (sctp_globals.address_families)
#define sctp_ep_hashsize (sctp_globals.ep_hashsize) #define sctp_ep_hashsize (sctp_globals.ep_hashsize)
#define sctp_ep_hashtable (sctp_globals.ep_hashtable) #define sctp_ep_hashtable (sctp_globals.ep_hashtable)
#define sctp_assoc_hashsize (sctp_globals.assoc_hashsize)
#define sctp_assoc_hashtable (sctp_globals.assoc_hashtable)
#define sctp_port_hashsize (sctp_globals.port_hashsize) #define sctp_port_hashsize (sctp_globals.port_hashsize)
#define sctp_port_hashtable (sctp_globals.port_hashtable) #define sctp_port_hashtable (sctp_globals.port_hashtable)
#define sctp_transport_hashtable (sctp_globals.transport_hashtable)
#define sctp_checksum_disable (sctp_globals.checksum_disable) #define sctp_checksum_disable (sctp_globals.checksum_disable)
/* SCTP Socket type: UDP or TCP style. */ /* SCTP Socket type: UDP or TCP style. */
...@@ -753,6 +752,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet) ...@@ -753,6 +752,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;
/* Reference counting. */ /* Reference counting. */
atomic_t refcnt; atomic_t refcnt;
......
...@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc) ...@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc)
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports); transport = list_entry(pos, struct sctp_transport, transports);
list_del_rcu(pos); list_del_rcu(pos);
sctp_unhash_transport(transport);
sctp_transport_free(transport); sctp_transport_free(transport);
} }
...@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, ...@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
/* Remove this peer from the list. */ /* Remove this peer from the list. */
list_del_rcu(&peer->transports); list_del_rcu(&peer->transports);
/* Remove this peer from the transport hashtable */
sctp_unhash_transport(peer);
/* Get the first transport of asoc. */ /* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next; pos = asoc->peer.transport_addr_list.next;
...@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* 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) {
......
...@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, ...@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
} }
/* Find the association that goes with this chunk. /* Find the association that goes with this chunk.
* We do a linear search of the associations for this endpoint. * We lookup the transport from hashtable at first, then get association
* We return the matching transport address too. * through t->assoc.
*/ */
static struct sctp_association *__sctp_endpoint_lookup_assoc( struct sctp_association *sctp_endpoint_lookup_assoc(
const struct sctp_endpoint *ep, const struct sctp_endpoint *ep,
const union sctp_addr *paddr, const union sctp_addr *paddr,
struct sctp_transport **transport) struct sctp_transport **transport)
{ {
struct sctp_association *asoc = NULL; struct sctp_association *asoc = NULL;
struct sctp_association *tmp; struct sctp_transport *t;
struct sctp_transport *t = NULL;
struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
int hash;
int rport;
*transport = NULL; *transport = NULL;
...@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( ...@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
*/ */
if (!ep->base.bind_addr.port) if (!ep->base.bind_addr.port)
goto out; goto out;
t = sctp_epaddr_lookup_transport(ep, paddr);
if (!t || t->asoc->temp)
goto out;
rport = ntohs(paddr->v4.sin_port); *transport = t;
asoc = t->asoc;
hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port,
rport);
head = &sctp_assoc_hashtable[hash];
read_lock(&head->lock);
sctp_for_each_hentry(epb, &head->chain) {
tmp = sctp_assoc(epb);
if (tmp->ep != ep || rport != tmp->peer.port)
continue;
t = sctp_assoc_lookup_paddr(tmp, paddr);
if (t) {
asoc = tmp;
*transport = t;
break;
}
}
read_unlock(&head->lock);
out: out:
return asoc; return asoc;
} }
/* Lookup association on an endpoint based on a peer address. BH-safe. */
struct sctp_association *sctp_endpoint_lookup_assoc(
const struct sctp_endpoint *ep,
const union sctp_addr *paddr,
struct sctp_transport **transport)
{
struct sctp_association *asoc;
local_bh_disable();
asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
local_bh_enable();
return asoc;
}
/* Look for any peeled off association from the endpoint that matches the /* Look for any peeled off association from the endpoint that matches the
* given peer address. * given peer address.
*/ */
......
...@@ -782,65 +782,135 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net, ...@@ -782,65 +782,135 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
return ep; return ep;
} }
/* Insert association into the hash table. */ /* rhashtable for transport */
static void __sctp_hash_established(struct sctp_association *asoc) struct sctp_hash_cmp_arg {
const union sctp_addr *laddr;
const union sctp_addr *paddr;
const struct net *net;
};
static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
const void *ptr)
{ {
struct net *net = sock_net(asoc->base.sk); const struct sctp_hash_cmp_arg *x = arg->key;
struct sctp_ep_common *epb; const struct sctp_transport *t = ptr;
struct sctp_hashbucket *head; struct sctp_association *asoc = t->asoc;
const struct net *net = x->net;
epb = &asoc->base; if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
return 1;
if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
return 1;
if (!net_eq(sock_net(asoc->base.sk), net))
return 1;
if (!sctp_bind_addr_match(&asoc->base.bind_addr,
x->laddr, sctp_sk(asoc->base.sk)))
return 1;
/* Calculate which chain this entry will belong to. */ return 0;
epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, }
asoc->peer.port);
head = &sctp_assoc_hashtable[epb->hashent]; static inline u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
{
const struct sctp_transport *t = data;
const union sctp_addr *paddr = &t->ipaddr;
const struct net *net = sock_net(t->asoc->base.sk);
u16 lport = htons(t->asoc->base.bind_addr.port);
u32 addr;
if (paddr->sa.sa_family == AF_INET6)
addr = jhash(&paddr->v6.sin6_addr, 16, seed);
else
addr = paddr->v4.sin_addr.s_addr;
write_lock(&head->lock); return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
hlist_add_head(&epb->node, &head->chain); (__force __u32)lport, net_hash_mix(net), seed);
write_unlock(&head->lock);
} }
/* Add an association to the hash. Local BH-safe. */ static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
void sctp_hash_established(struct sctp_association *asoc)
{ {
if (asoc->temp) const struct sctp_hash_cmp_arg *x = data;
return; const union sctp_addr *paddr = x->paddr;
const struct net *net = x->net;
u16 lport = x->laddr->v4.sin_port;
u32 addr;
if (paddr->sa.sa_family == AF_INET6)
addr = jhash(&paddr->v6.sin6_addr, 16, seed);
else
addr = paddr->v4.sin_addr.s_addr;
local_bh_disable(); return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
__sctp_hash_established(asoc); (__force __u32)lport, net_hash_mix(net), seed);
local_bh_enable();
} }
/* Remove association from the hash table. */ static const struct rhashtable_params sctp_hash_params = {
static void __sctp_unhash_established(struct sctp_association *asoc) .head_offset = offsetof(struct sctp_transport, node),
.hashfn = sctp_hash_key,
.obj_hashfn = sctp_hash_obj,
.obj_cmpfn = sctp_hash_cmp,
.automatic_shrinking = true,
};
int sctp_transport_hashtable_init(void)
{ {
struct net *net = sock_net(asoc->base.sk); return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
struct sctp_hashbucket *head; }
struct sctp_ep_common *epb;
epb = &asoc->base; void sctp_transport_hashtable_destroy(void)
{
rhashtable_destroy(&sctp_transport_hashtable);
}
epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port, void sctp_hash_transport(struct sctp_transport *t)
asoc->peer.port); {
struct sctp_sockaddr_entry *addr;
struct sctp_hash_cmp_arg arg;
addr = list_entry(t->asoc->base.bind_addr.address_list.next,
struct sctp_sockaddr_entry, list);
arg.laddr = &addr->a;
arg.paddr = &t->ipaddr;
arg.net = sock_net(t->asoc->base.sk);
reinsert:
if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
&t->node, sctp_hash_params) == -EBUSY)
goto reinsert;
}
head = &sctp_assoc_hashtable[epb->hashent]; void sctp_unhash_transport(struct sctp_transport *t)
{
rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
sctp_hash_params);
}
write_lock(&head->lock); struct sctp_transport *sctp_addrs_lookup_transport(
hlist_del_init(&epb->node); struct net *net,
write_unlock(&head->lock); const union sctp_addr *laddr,
const union sctp_addr *paddr)
{
struct sctp_hash_cmp_arg arg = {
.laddr = laddr,
.paddr = paddr,
.net = net,
};
return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
sctp_hash_params);
} }
/* Remove association from the hash table. Local BH-safe. */ struct sctp_transport *sctp_epaddr_lookup_transport(
void sctp_unhash_established(struct sctp_association *asoc) const struct sctp_endpoint *ep,
const union sctp_addr *paddr)
{ {
if (asoc->temp) struct sctp_sockaddr_entry *addr;
return; struct net *net = sock_net(ep->base.sk);
local_bh_disable(); addr = list_entry(ep->base.bind_addr.address_list.next,
__sctp_unhash_established(asoc); struct sctp_sockaddr_entry, list);
local_bh_enable();
return sctp_addrs_lookup_transport(net, &addr->a, paddr);
} }
/* Look up an association. */ /* Look up an association. */
...@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association( ...@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer, const union sctp_addr *peer,
struct sctp_transport **pt) struct sctp_transport **pt)
{ {
struct sctp_hashbucket *head; struct sctp_transport *t;
struct sctp_ep_common *epb;
struct sctp_association *asoc;
struct sctp_transport *transport;
int hash;
/* Optimize here for direct hit, only listening connections can t = sctp_addrs_lookup_transport(net, local, peer);
* have wildcards anyways. if (!t || t->dead || t->asoc->temp)
*/ return NULL;
hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
ntohs(peer->v4.sin_port));
head = &sctp_assoc_hashtable[hash];
read_lock(&head->lock);
sctp_for_each_hentry(epb, &head->chain) {
asoc = sctp_assoc(epb);
transport = sctp_assoc_is_match(asoc, net, local, peer);
if (transport)
goto hit;
}
read_unlock(&head->lock);
return NULL; sctp_association_hold(t->asoc);
*pt = t;
hit: return t->asoc;
*pt = transport;
sctp_association_hold(asoc);
read_unlock(&head->lock);
return asoc;
} }
/* Look up an association. BH-safe. */ /* Look up an association. protected by RCU read lock */
static static
struct sctp_association *sctp_lookup_association(struct net *net, struct sctp_association *sctp_lookup_association(struct net *net,
const union sctp_addr *laddr, const union sctp_addr *laddr,
...@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net, ...@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net,
{ {
struct sctp_association *asoc; struct sctp_association *asoc;
local_bh_disable(); rcu_read_lock();
asoc = __sctp_lookup_association(net, laddr, paddr, transportp); asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
local_bh_enable(); rcu_read_unlock();
return asoc; return asoc;
} }
......
...@@ -281,88 +281,136 @@ void sctp_eps_proc_exit(struct net *net) ...@@ -281,88 +281,136 @@ void sctp_eps_proc_exit(struct net *net)
remove_proc_entry("eps", net->sctp.proc_net_sctp); remove_proc_entry("eps", net->sctp.proc_net_sctp);
} }
struct sctp_ht_iter {
struct seq_net_private p;
struct rhashtable_iter hti;
};
static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos) static struct sctp_transport *sctp_transport_get_next(struct seq_file *seq)
{ {
if (*pos >= sctp_assoc_hashsize) struct sctp_ht_iter *iter = seq->private;
return NULL; struct sctp_transport *t;
if (*pos < 0) t = rhashtable_walk_next(&iter->hti);
*pos = 0; for (; t; t = rhashtable_walk_next(&iter->hti)) {
if (IS_ERR(t)) {
if (PTR_ERR(t) == -EAGAIN)
continue;
break;
}
if (*pos == 0) if (net_eq(sock_net(t->asoc->base.sk), seq_file_net(seq)) &&
seq_printf(seq, " ASSOC SOCK STY SST ST HBKT " t->asoc->peer.primary_path == t)
"ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT " break;
"RPORT LADDRS <-> RADDRS " }
"HBINT INS OUTS MAXRT T1X T2X RTXC "
"wmema wmemq sndbuf rcvbuf\n");
return (void *)pos; return t;
} }
static void sctp_assocs_seq_stop(struct seq_file *seq, void *v) static struct sctp_transport *sctp_transport_get_idx(struct seq_file *seq,
loff_t pos)
{
void *obj;
while (pos && (obj = sctp_transport_get_next(seq)) && !IS_ERR(obj))
pos--;
return obj;
}
static int sctp_transport_walk_start(struct seq_file *seq)
{ {
struct sctp_ht_iter *iter = seq->private;
int err;
err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti);
if (err)
return err;
err = rhashtable_walk_start(&iter->hti);
return err == -EAGAIN ? 0 : err;
} }
static void sctp_transport_walk_stop(struct seq_file *seq)
{
struct sctp_ht_iter *iter = seq->private;
rhashtable_walk_stop(&iter->hti);
rhashtable_walk_exit(&iter->hti);
}
static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
{
int err = sctp_transport_walk_start(seq);
if (err)
return ERR_PTR(err);
return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
}
static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
{
sctp_transport_walk_stop(seq);
}
static void *sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
if (++*pos >= sctp_assoc_hashsize) ++*pos;
return NULL;
return pos; return sctp_transport_get_next(seq);
} }
/* Display sctp associations (/proc/net/sctp/assocs). */ /* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v) static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{ {
struct sctp_hashbucket *head; struct sctp_transport *transport;
struct sctp_ep_common *epb;
struct sctp_association *assoc; struct sctp_association *assoc;
struct sctp_ep_common *epb;
struct sock *sk; struct sock *sk;
int hash = *(loff_t *)v;
if (hash >= sctp_assoc_hashsize)
return -ENOMEM;
head = &sctp_assoc_hashtable[hash]; if (v == SEQ_START_TOKEN) {
local_bh_disable(); seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
read_lock(&head->lock); "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
sctp_for_each_hentry(epb, &head->chain) { "RPORT LADDRS <-> RADDRS "
assoc = sctp_assoc(epb); "HBINT INS OUTS MAXRT T1X T2X RTXC "
sk = epb->sk; "wmema wmemq sndbuf rcvbuf\n");
if (!net_eq(sock_net(sk), seq_file_net(seq))) return 0;
continue;
seq_printf(seq,
"%8pK %8pK %-3d %-3d %-2d %-4d "
"%4d %8d %8d %7u %5lu %-5d %5d ",
assoc, sk, sctp_sk(sk)->type, sk->sk_state,
assoc->state, hash,
assoc->assoc_id,
assoc->sndbuf_used,
atomic_read(&assoc->rmem_alloc),
from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
sock_i_ino(sk),
epb->bind_addr.port,
assoc->peer.port);
seq_printf(seq, " ");
sctp_seq_dump_local_addrs(seq, epb);
seq_printf(seq, "<-> ");
sctp_seq_dump_remote_addrs(seq, assoc);
seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
"%8d %8d %8d %8d",
assoc->hbinterval, assoc->c.sinit_max_instreams,
assoc->c.sinit_num_ostreams, assoc->max_retrans,
assoc->init_retries, assoc->shutdown_retries,
assoc->rtx_data_chunks,
atomic_read(&sk->sk_wmem_alloc),
sk->sk_wmem_queued,
sk->sk_sndbuf,
sk->sk_rcvbuf);
seq_printf(seq, "\n");
} }
read_unlock(&head->lock);
local_bh_enable(); transport = (struct sctp_transport *)v;
assoc = transport->asoc;
epb = &assoc->base;
sk = epb->sk;
seq_printf(seq,
"%8pK %8pK %-3d %-3d %-2d %-4d "
"%4d %8d %8d %7u %5lu %-5d %5d ",
assoc, sk, sctp_sk(sk)->type, sk->sk_state,
assoc->state, 0,
assoc->assoc_id,
assoc->sndbuf_used,
atomic_read(&assoc->rmem_alloc),
from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
sock_i_ino(sk),
epb->bind_addr.port,
assoc->peer.port);
seq_printf(seq, " ");
sctp_seq_dump_local_addrs(seq, epb);
seq_printf(seq, "<-> ");
sctp_seq_dump_remote_addrs(seq, assoc);
seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
"%8d %8d %8d %8d",
assoc->hbinterval, assoc->c.sinit_max_instreams,
assoc->c.sinit_num_ostreams, assoc->max_retrans,
assoc->init_retries, assoc->shutdown_retries,
assoc->rtx_data_chunks,
atomic_read(&sk->sk_wmem_alloc),
sk->sk_wmem_queued,
sk->sk_sndbuf,
sk->sk_rcvbuf);
seq_printf(seq, "\n");
return 0; return 0;
} }
...@@ -378,7 +426,7 @@ static const struct seq_operations sctp_assoc_ops = { ...@@ -378,7 +426,7 @@ static const struct seq_operations sctp_assoc_ops = {
static int sctp_assocs_seq_open(struct inode *inode, struct file *file) static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{ {
return seq_open_net(inode, file, &sctp_assoc_ops, return seq_open_net(inode, file, &sctp_assoc_ops,
sizeof(struct seq_net_private)); sizeof(struct sctp_ht_iter));
} }
static const struct file_operations sctp_assocs_seq_fops = { static const struct file_operations sctp_assocs_seq_fops = {
...@@ -409,112 +457,94 @@ void sctp_assocs_proc_exit(struct net *net) ...@@ -409,112 +457,94 @@ void sctp_assocs_proc_exit(struct net *net)
static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos) static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
{ {
if (*pos >= sctp_assoc_hashsize) int err = sctp_transport_walk_start(seq);
return NULL;
if (*pos < 0)
*pos = 0;
if (*pos == 0) if (err)
seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX " return ERR_PTR(err);
"REM_ADDR_RTX START STATE\n");
return (void *)pos; return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
} }
static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
if (++*pos >= sctp_assoc_hashsize) ++*pos;
return NULL;
return pos; return sctp_transport_get_next(seq);
} }
static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v) static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
{ {
sctp_transport_walk_stop(seq);
} }
static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
{ {
struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
struct sctp_association *assoc; struct sctp_association *assoc;
struct sctp_transport *tsp; struct sctp_transport *tsp;
int hash = *(loff_t *)v;
if (hash >= sctp_assoc_hashsize) if (v == SEQ_START_TOKEN) {
return -ENOMEM; seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
"REM_ADDR_RTX START STATE\n");
return 0;
}
head = &sctp_assoc_hashtable[hash]; tsp = (struct sctp_transport *)v;
local_bh_disable(); assoc = tsp->asoc;
read_lock(&head->lock);
rcu_read_lock(); list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
sctp_for_each_hentry(epb, &head->chain) { transports) {
if (!net_eq(sock_net(epb->sk), seq_file_net(seq))) if (tsp->dead)
continue; continue;
assoc = sctp_assoc(epb); /*
list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list, * The remote address (ADDR)
transports) { */
if (tsp->dead) tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
continue; seq_printf(seq, " ");
/*
* The association ID (ASSOC_ID)
*/
seq_printf(seq, "%d ", tsp->asoc->assoc_id);
/*
* If the Heartbeat is active (HB_ACT)
* Note: 1 = Active, 0 = Inactive
*/
seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
/*
* Retransmit time out (RTO)
*/
seq_printf(seq, "%lu ", tsp->rto);
/*
* Maximum path retransmit count (PATH_MAX_RTX)
*/
seq_printf(seq, "%d ", tsp->pathmaxrxt);
/*
* remote address retransmit count (REM_ADDR_RTX)
* Note: We don't have a way to tally this at the moment
* so lets just leave it as zero for the moment
*/
seq_puts(seq, "0 ");
/*
* remote address start time (START). This is also not
* currently implemented, but we can record it with a
* jiffies marker in a subsequent patch
*/
seq_puts(seq, "0 ");
/*
* The current state of this destination. I.e.
* SCTP_ACTIVE, SCTP_INACTIVE, ...
*/
seq_printf(seq, "%d", tsp->state);
/* seq_printf(seq, "\n");
* The remote address (ADDR)
*/
tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
seq_printf(seq, " ");
/*
* The association ID (ASSOC_ID)
*/
seq_printf(seq, "%d ", tsp->asoc->assoc_id);
/*
* If the Heartbeat is active (HB_ACT)
* Note: 1 = Active, 0 = Inactive
*/
seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
/*
* Retransmit time out (RTO)
*/
seq_printf(seq, "%lu ", tsp->rto);
/*
* Maximum path retransmit count (PATH_MAX_RTX)
*/
seq_printf(seq, "%d ", tsp->pathmaxrxt);
/*
* remote address retransmit count (REM_ADDR_RTX)
* Note: We don't have a way to tally this at the moment
* so lets just leave it as zero for the moment
*/
seq_puts(seq, "0 ");
/*
* remote address start time (START). This is also not
* currently implemented, but we can record it with a
* jiffies marker in a subsequent patch
*/
seq_puts(seq, "0 ");
/*
* The current state of this destination. I.e.
* SCTP_ACTIVE, SCTP_INACTIVE, ...
*/
seq_printf(seq, "%d", tsp->state);
seq_printf(seq, "\n");
}
} }
rcu_read_unlock();
read_unlock(&head->lock);
local_bh_enable();
return 0; return 0;
} }
static const struct seq_operations sctp_remaddr_ops = { static const struct seq_operations sctp_remaddr_ops = {
...@@ -533,7 +563,7 @@ void sctp_remaddr_proc_exit(struct net *net) ...@@ -533,7 +563,7 @@ void sctp_remaddr_proc_exit(struct net *net)
static int sctp_remaddr_seq_open(struct inode *inode, struct file *file) static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{ {
return seq_open_net(inode, file, &sctp_remaddr_ops, return seq_open_net(inode, file, &sctp_remaddr_ops,
sizeof(struct seq_net_private)); sizeof(struct sctp_ht_iter));
} }
static const struct file_operations sctp_remaddr_seq_fops = { static const struct file_operations sctp_remaddr_seq_fops = {
......
...@@ -1416,24 +1416,6 @@ static __init int sctp_init(void) ...@@ -1416,24 +1416,6 @@ static __init int sctp_init(void)
for (order = 0; (1UL << order) < goal; order++) for (order = 0; (1UL << order) < goal; order++)
; ;
do {
sctp_assoc_hashsize = (1UL << order) * PAGE_SIZE /
sizeof(struct sctp_hashbucket);
if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0)
continue;
sctp_assoc_hashtable = (struct sctp_hashbucket *)
__get_free_pages(GFP_KERNEL | __GFP_NOWARN, order);
} while (!sctp_assoc_hashtable && --order > 0);
if (!sctp_assoc_hashtable) {
pr_err("Failed association hash alloc\n");
status = -ENOMEM;
goto err_ahash_alloc;
}
for (i = 0; i < sctp_assoc_hashsize; i++) {
rwlock_init(&sctp_assoc_hashtable[i].lock);
INIT_HLIST_HEAD(&sctp_assoc_hashtable[i].chain);
}
/* Allocate and initialize the endpoint hash table. */ /* Allocate and initialize the endpoint hash table. */
sctp_ep_hashsize = 64; sctp_ep_hashsize = 64;
sctp_ep_hashtable = sctp_ep_hashtable =
...@@ -1467,8 +1449,10 @@ static __init int sctp_init(void) ...@@ -1467,8 +1449,10 @@ static __init int sctp_init(void)
INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain); INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
} }
pr_info("Hash tables configured (established %d bind %d)\n", if (sctp_transport_hashtable_init())
sctp_assoc_hashsize, sctp_port_hashsize); goto err_thash_alloc;
pr_info("Hash tables configured (bind %d)\n", sctp_port_hashsize);
sctp_sysctl_register(); sctp_sysctl_register();
...@@ -1521,12 +1505,10 @@ static __init int sctp_init(void) ...@@ -1521,12 +1505,10 @@ static __init int sctp_init(void)
get_order(sctp_port_hashsize * get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket))); sizeof(struct sctp_bind_hashbucket)));
err_bhash_alloc: err_bhash_alloc:
sctp_transport_hashtable_destroy();
err_thash_alloc:
kfree(sctp_ep_hashtable); kfree(sctp_ep_hashtable);
err_ehash_alloc: err_ehash_alloc:
free_pages((unsigned long)sctp_assoc_hashtable,
get_order(sctp_assoc_hashsize *
sizeof(struct sctp_hashbucket)));
err_ahash_alloc:
percpu_counter_destroy(&sctp_sockets_allocated); percpu_counter_destroy(&sctp_sockets_allocated);
err_percpu_counter_init: err_percpu_counter_init:
kmem_cache_destroy(sctp_chunk_cachep); kmem_cache_destroy(sctp_chunk_cachep);
...@@ -1560,13 +1542,11 @@ static __exit void sctp_exit(void) ...@@ -1560,13 +1542,11 @@ static __exit void sctp_exit(void)
sctp_sysctl_unregister(); sctp_sysctl_unregister();
free_pages((unsigned long)sctp_assoc_hashtable,
get_order(sctp_assoc_hashsize *
sizeof(struct sctp_hashbucket)));
kfree(sctp_ep_hashtable);
free_pages((unsigned long)sctp_port_hashtable, free_pages((unsigned long)sctp_port_hashtable,
get_order(sctp_port_hashsize * get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket))); sizeof(struct sctp_bind_hashbucket)));
kfree(sctp_ep_hashtable);
sctp_transport_hashtable_destroy();
percpu_counter_destroy(&sctp_sockets_allocated); percpu_counter_destroy(&sctp_sockets_allocated);
......
...@@ -866,7 +866,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, ...@@ -866,7 +866,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
(!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK)) (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
return; return;
sctp_unhash_established(asoc);
sctp_association_free(asoc); sctp_association_free(asoc);
} }
...@@ -1269,7 +1268,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1269,7 +1268,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
asoc = cmd->obj.asoc; asoc = cmd->obj.asoc;
BUG_ON(asoc->peer.primary_path == NULL); BUG_ON(asoc->peer.primary_path == NULL);
sctp_endpoint_add_asoc(ep, asoc); sctp_endpoint_add_asoc(ep, asoc);
sctp_hash_established(asoc);
break; break;
case SCTP_CMD_UPDATE_ASSOC: case SCTP_CMD_UPDATE_ASSOC:
......
...@@ -1228,7 +1228,6 @@ static int __sctp_connect(struct sock *sk, ...@@ -1228,7 +1228,6 @@ static int __sctp_connect(struct sock *sk,
* To the hash table, try to unhash it, just in case, its a noop * To the hash table, try to unhash it, just in case, its a noop
* if it wasn't hashed so we're safe * if it wasn't hashed so we're safe
*/ */
sctp_unhash_established(asoc);
sctp_association_free(asoc); sctp_association_free(asoc);
} }
return err; return err;
...@@ -1504,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout) ...@@ -1504,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout)
* ABORT or SHUTDOWN based on the linger options. * ABORT or SHUTDOWN based on the linger options.
*/ */
if (sctp_state(asoc, CLOSED)) { if (sctp_state(asoc, CLOSED)) {
sctp_unhash_established(asoc);
sctp_association_free(asoc); sctp_association_free(asoc);
continue; continue;
} }
...@@ -1986,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) ...@@ -1986,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
goto out_unlock; goto out_unlock;
out_free: out_free:
if (new_asoc) { if (new_asoc)
sctp_unhash_established(asoc);
sctp_association_free(asoc); sctp_association_free(asoc);
}
out_unlock: out_unlock:
release_sock(sk); release_sock(sk);
......
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