summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/sctp/sctp.h32
-rw-r--r--include/net/sctp/structs.h10
-rw-r--r--net/sctp/associola.c5
-rw-r--r--net/sctp/endpointola.c52
-rw-r--r--net/sctp/input.c187
-rw-r--r--net/sctp/proc.c316
-rw-r--r--net/sctp/protocol.c36
-rw-r--r--net/sctp/sm_sideeffect.c2
-rw-r--r--net/sctp/socket.c6
9 files changed, 331 insertions, 315 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ce13cf2..835aa2e 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -126,8 +126,6 @@ int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
*/
int sctp_rcv(struct sk_buff *skb);
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_unhash_endpoint(struct sctp_endpoint *);
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,
struct sctp_transport *t);
void sctp_backlog_migrate(struct sctp_association *assoc,
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
@@ -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);
}
-/* 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) \
hlist_for_each_entry(epb, head, node)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index eea9bde..20e7212 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -48,6 +48,7 @@
#define __sctp_structs_h__
#include <linux/ktime.h>
+#include <linux/rhashtable.h>
#include <linux/socket.h> /* linux/in.h needs this!! */
#include <linux/in.h> /* We get struct sockaddr_in. */
#include <linux/in6.h> /* We get struct in6_addr */
@@ -119,14 +120,13 @@ extern struct sctp_globals {
/* This is the hash of all endpoints. */
struct sctp_hashbucket *ep_hashtable;
- /* This is the hash of all associations. */
- struct sctp_hashbucket *assoc_hashtable;
/* This is the sctp port control hash. */
struct sctp_bind_hashbucket *port_hashtable;
+ /* This is the hash of all transports. */
+ struct rhashtable transport_hashtable;
/* Sizes of above hashtables. */
int ep_hashsize;
- int assoc_hashsize;
int port_hashsize;
/* Default initialization values to be applied to new associations. */
@@ -143,10 +143,9 @@ extern struct sctp_globals {
#define sctp_address_families (sctp_globals.address_families)
#define sctp_ep_hashsize (sctp_globals.ep_hashsize)
#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_hashtable (sctp_globals.port_hashtable)
+#define sctp_transport_hashtable (sctp_globals.transport_hashtable)
#define sctp_checksum_disable (sctp_globals.checksum_disable)
/* SCTP Socket type: UDP or TCP style. */
@@ -753,6 +752,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet)
struct sctp_transport {
/* A list of transports. */
struct list_head transports;
+ struct rhash_head node;
/* Reference counting. */
atomic_t refcnt;
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 559afd0..2bf8ec9 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc)
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
list_del_rcu(pos);
+ sctp_unhash_transport(transport);
sctp_transport_free(transport);
}
@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
/* Remove this peer from the list. */
list_del_rcu(&peer->transports);
+ /* Remove this peer from the transport hashtable */
+ sctp_unhash_transport(peer);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Attach the remote transport to our asoc. */
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
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 (!asoc->peer.primary_path) {
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 9da76ba..52838ea 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
}
/* Find the association that goes with this chunk.
- * We do a linear search of the associations for this endpoint.
- * We return the matching transport address too.
+ * We lookup the transport from hashtable at first, then get association
+ * through t->assoc.
*/
-static struct sctp_association *__sctp_endpoint_lookup_assoc(
+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 = NULL;
- struct sctp_association *tmp;
- struct sctp_transport *t = NULL;
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- int hash;
- int rport;
+ struct sctp_transport *t;
*transport = NULL;
@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
*/
if (!ep->base.bind_addr.port)
goto out;
+ t = sctp_epaddr_lookup_transport(ep, paddr);
+ if (!t || t->asoc->temp)
+ goto out;
- rport = ntohs(paddr->v4.sin_port);
-
- 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);
+ *transport = t;
+ asoc = t->asoc;
out:
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
* given peer address.
*/
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b6493b3..d9a6e66 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -782,65 +782,135 @@ hit:
return ep;
}
-/* Insert association into the hash table. */
-static void __sctp_hash_established(struct sctp_association *asoc)
+/* rhashtable for transport */
+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);
- struct sctp_ep_common *epb;
- struct sctp_hashbucket *head;
+ const struct sctp_hash_cmp_arg *x = arg->key;
+ const struct sctp_transport *t = ptr;
+ 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. */
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+ return 0;
+}
- 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);
- hlist_add_head(&epb->node, &head->chain);
- write_unlock(&head->lock);
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Add an association to the hash. Local BH-safe. */
-void sctp_hash_established(struct sctp_association *asoc)
+static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
{
- if (asoc->temp)
- return;
+ const struct sctp_hash_cmp_arg *x = data;
+ 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();
- __sctp_hash_established(asoc);
- local_bh_enable();
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Remove association from the hash table. */
-static void __sctp_unhash_established(struct sctp_association *asoc)
+static const struct rhashtable_params sctp_hash_params = {
+ .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);
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
+}
- epb = &asoc->base;
+void sctp_transport_hashtable_destroy(void)
+{
+ rhashtable_destroy(&sctp_transport_hashtable);
+}
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+void sctp_hash_transport(struct sctp_transport *t)
+{
+ 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);
- hlist_del_init(&epb->node);
- write_unlock(&head->lock);
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ 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. */
-void sctp_unhash_established(struct sctp_association *asoc)
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr)
{
- if (asoc->temp)
- return;
+ struct sctp_sockaddr_entry *addr;
+ struct net *net = sock_net(ep->base.sk);
- local_bh_disable();
- __sctp_unhash_established(asoc);
- local_bh_enable();
+ addr = list_entry(ep->base.bind_addr.address_list.next,
+ struct sctp_sockaddr_entry, list);
+
+ return sctp_addrs_lookup_transport(net, &addr->a, paddr);
}
/* Look up an association. */
@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer,
struct sctp_transport **pt)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- struct sctp_association *asoc;
- struct sctp_transport *transport;
- int hash;
+ struct sctp_transport *t;
- /* Optimize here for direct hit, only listening connections can
- * have wildcards anyways.
- */
- 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);
+ t = sctp_addrs_lookup_transport(net, local, peer);
+ if (!t || t->dead || t->asoc->temp)
+ return NULL;
- return NULL;
+ sctp_association_hold(t->asoc);
+ *pt = t;
-hit:
- *pt = transport;
- sctp_association_hold(asoc);
- read_unlock(&head->lock);
- return asoc;
+ return t->asoc;
}
-/* Look up an association. BH-safe. */
+/* Look up an association. protected by RCU read lock */
static
struct sctp_association *sctp_lookup_association(struct net *net,
const union sctp_addr *laddr,
@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net,
{
struct sctp_association *asoc;
- local_bh_disable();
+ rcu_read_lock();
asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
- local_bh_enable();
+ rcu_read_unlock();
return asoc;
}
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 0697eda..dfa7eec 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -281,88 +281,136 @@ void sctp_eps_proc_exit(struct net *net)
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)
- return NULL;
+ struct sctp_ht_iter *iter = seq->private;
+ struct sctp_transport *t;
- if (*pos < 0)
- *pos = 0;
+ t = rhashtable_walk_next(&iter->hti);
+ for (; t; t = rhashtable_walk_next(&iter->hti)) {
+ if (IS_ERR(t)) {
+ if (PTR_ERR(t) == -EAGAIN)
+ continue;
+ break;
+ }
- if (*pos == 0)
- seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
- "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
- "RPORT LADDRS <-> RADDRS "
- "HBINT INS OUTS MAXRT T1X T2X RTXC "
- "wmema wmemq sndbuf rcvbuf\n");
+ if (net_eq(sock_net(t->asoc->base.sk), seq_file_net(seq)) &&
+ t->asoc->peer.primary_path == t)
+ break;
+ }
- 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)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ struct sctp_transport *transport;
struct sctp_association *assoc;
+ struct sctp_ep_common *epb;
struct sock *sk;
- int hash = *(loff_t *)v;
-
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- assoc = sctp_assoc(epb);
- sk = epb->sk;
- if (!net_eq(sock_net(sk), seq_file_net(seq)))
- 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");
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
+ "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
+ "RPORT LADDRS <-> RADDRS "
+ "HBINT INS OUTS MAXRT T1X T2X RTXC "
+ "wmema wmemq sndbuf rcvbuf\n");
+ return 0;
}
- 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;
}
@@ -378,7 +426,7 @@ static const struct seq_operations sctp_assoc_ops = {
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
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 = {
@@ -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)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
-
- if (*pos < 0)
- *pos = 0;
+ int err = sctp_transport_walk_start(seq);
- if (*pos == 0)
- seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
- "REM_ADDR_RTX START STATE\n");
+ if (err)
+ return ERR_PTR(err);
- 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)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
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)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
struct sctp_association *assoc;
struct sctp_transport *tsp;
- int hash = *(loff_t *)v;
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
+ if (v == SEQ_START_TOKEN) {
+ 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];
- local_bh_disable();
- read_lock(&head->lock);
- rcu_read_lock();
- sctp_for_each_hentry(epb, &head->chain) {
- if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
+ tsp = (struct sctp_transport *)v;
+ assoc = tsp->asoc;
+
+ list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
+ transports) {
+ if (tsp->dead)
continue;
- assoc = sctp_assoc(epb);
- list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
- transports) {
- if (tsp->dead)
- continue;
+ /*
+ * 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);
- /*
- * 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");
- }
+ seq_printf(seq, "\n");
}
- rcu_read_unlock();
- read_unlock(&head->lock);
- local_bh_enable();
-
return 0;
-
}
static const struct seq_operations sctp_remaddr_ops = {
@@ -533,7 +563,7 @@ void sctp_remaddr_proc_exit(struct net *net)
static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{
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 = {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 010aced..ab0d538 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1416,24 +1416,6 @@ static __init int sctp_init(void)
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. */
sctp_ep_hashsize = 64;
sctp_ep_hashtable =
@@ -1467,8 +1449,10 @@ static __init int sctp_init(void)
INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
}
- pr_info("Hash tables configured (established %d bind %d)\n",
- sctp_assoc_hashsize, sctp_port_hashsize);
+ if (sctp_transport_hashtable_init())
+ goto err_thash_alloc;
+
+ pr_info("Hash tables configured (bind %d)\n", sctp_port_hashsize);
sctp_sysctl_register();
@@ -1521,12 +1505,10 @@ err_register_defaults:
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
err_bhash_alloc:
+ sctp_transport_hashtable_destroy();
+err_thash_alloc:
kfree(sctp_ep_hashtable);
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);
err_percpu_counter_init:
kmem_cache_destroy(sctp_chunk_cachep);
@@ -1560,13 +1542,11 @@ static __exit void sctp_exit(void)
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,
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
+ kfree(sctp_ep_hashtable);
+ sctp_transport_hashtable_destroy();
percpu_counter_destroy(&sctp_sockets_allocated);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 05cd164..4f170ad3 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -866,7 +866,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
(!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
return;
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
@@ -1269,7 +1268,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
asoc = cmd->obj.asoc;
BUG_ON(asoc->peer.primary_path == NULL);
sctp_endpoint_add_asoc(ep, asoc);
- sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index b5f4811..9bb80ec 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1228,7 +1228,6 @@ out_free:
* To the hash table, try to unhash it, just in case, its a noop
* if it wasn't hashed so we're safe
*/
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
return err;
@@ -1504,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout)
* ABORT or SHUTDOWN based on the linger options.
*/
if (sctp_state(asoc, CLOSED)) {
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
continue;
}
@@ -1986,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
goto out_unlock;
out_free:
- if (new_asoc) {
- sctp_unhash_established(asoc);
+ if (new_asoc)
sctp_association_free(asoc);
- }
out_unlock:
release_sock(sk);