sctp: apply rhashtable api to send/recv path
[deliverable/linux.git] / net / sctp / input.c
index b6493b3f11a97f359d0ec70cde2812082f65910f..6f075d83576416529da5d7788e55f457be11533e 100644 (file)
@@ -782,6 +782,137 @@ hit:
        return ep;
 }
 
+/* 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)
+{
+       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;
+
+       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;
+
+       return 0;
+}
+
+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;
+
+       return  jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+                            (__force __u32)lport, net_hash_mix(net), seed);
+}
+
+static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
+{
+       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;
+
+       return  jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+                            (__force __u32)lport, net_hash_mix(net), seed);
+}
+
+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)
+{
+       return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
+}
+
+void sctp_transport_hashtable_destroy(void)
+{
+       rhashtable_destroy(&sctp_transport_hashtable);
+}
+
+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;
+}
+
+void sctp_unhash_transport(struct sctp_transport *t)
+{
+       rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
+                              sctp_hash_params);
+}
+
+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);
+}
+
+struct sctp_transport *sctp_epaddr_lookup_transport(
+                               const struct sctp_endpoint *ep,
+                               const union sctp_addr *paddr)
+{
+       struct sctp_sockaddr_entry *addr;
+       struct net *net = sock_net(ep->base.sk);
+
+       addr = list_entry(ep->base.bind_addr.address_list.next,
+                         struct sctp_sockaddr_entry, list);
+
+       return sctp_addrs_lookup_transport(net, &addr->a, paddr);
+}
+
 /* Insert association into the hash table.  */
 static void __sctp_hash_established(struct sctp_association *asoc)
 {
@@ -850,38 +981,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;
-
-       /* 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;
-       }
+       struct sctp_transport *t;
 
-       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 +1002,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;
 }
This page took 0.110876 seconds and 5 git commands to generate.