Thread (13 messages) 13 messages, 4 authors, 2012-05-30

Re: [RFC PATCH 4/4] inet: use second hash in inet_csk_get_port

From: Alexandru Copot <hidden>
Date: 2012-05-30 19:11:04

On Wed, May 30, 2012 at 8:20 PM, Eric Dumazet [off-list ref] wrote:
On Wed, 2012-05-30 at 10:36 +0300, Alexandru Copot wrote:
quoted
+struct inet_bind_bucket *
+inet4_find_bind_buckets(struct sock *sk,
+                     unsigned short port,
+                     struct inet_bind_hashbucket **p_bhead,
+                     struct inet_bind_hashbucket **p_portaddr_bhead)
+{
+     struct net *net = sock_net(sk);
+     struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
+     struct inet_bind_bucket *tb = NULL;
+     struct hlist_node *node;
+
+     struct inet_bind_hashbucket *bhead, *portaddr_bhead, *portaddrany_bhead;
+     bhead = &hinfo->bhash[inet_bhashfn(net, port, hinfo->bhash_size)];
+     portaddr_bhead = inet4_portaddr_hashbucket(hinfo, net,
+                             sk_rcv_saddr(sk), port);
+     portaddrany_bhead = inet4_portaddr_hashbucket(hinfo, net,
+                                             INADDR_ANY, port);
+
+     *p_portaddr_bhead = portaddr_bhead;
+     *p_bhead = bhead;
+
+     /*
+      * prevent dead locks by always taking locks in a fixed order:
+      * - always take the port-only lock first. This is done because in some
+      *   other places this is the lock taken, being folllowed in only some
+      *   cases by the portaddr lock.
+      * - between portaddr and portaddrany always choose the one with the
+      *   lower address. Unlock ordering is not important, as long as the
+      *   locking order is consistent.
+      * - make sure to not take the same lock twice
+      */
+     spin_lock(&bhead->lock);
+     if (portaddr_bhead > portaddrany_bhead) {
+             spin_lock(&portaddrany_bhead->lock);
+             spin_lock(&portaddr_bhead->lock);
+     } else if (portaddr_bhead < portaddrany_bhead) {
+             spin_lock(&portaddr_bhead->lock);
+             spin_lock(&portaddrany_bhead->lock);
+     } else {
+             spin_lock(&portaddr_bhead->lock);
+     }
+
+     if (sk_rcv_saddr(sk) != INADDR_ANY) {
+             struct inet_bind_hashbucket *_head;
+
+             _head = portaddr_bhead;
+             if (bhead->count < portaddr_bhead->count) {
+                     _head = bhead;
+                     inet_bind_bucket_for_each(tb, node, &_head->chain)
+                             if ((net_eq(ib_net(tb), net)) &&
+                                 (tb->port == port) &&
+                                 (tb->ib_addr_ipv4 == sk_rcv_saddr(sk)))
+                                     goto found;
+             } else {
+                     inet_portaddr_bind_bucket_for_each(tb, node, &_head->chain)
+                             if ((net_eq(ib_net(tb), net)) &&
+                                 (tb->port == port) &&
+                                 (tb->ib_addr_ipv4 == sk_rcv_saddr(sk)))
+                                     goto found;
+             }
+             _head = portaddrany_bhead;
+             if (bhead->count < portaddrany_bhead->count) {
+                     _head = bhead;
+                     inet_bind_bucket_for_each(tb, node, &_head->chain)
+                             if ((ib_net(tb) == net) &&
+                                 (tb->port == port) &&
+                                 (tb->ib_addr_ipv4 == INADDR_ANY))
+                                     goto found;
+             } else {
+                     inet_portaddr_bind_bucket_for_each(tb, node, &_head->chain)
+                             if ((ib_net(tb) == net) &&
+                                 (tb->port == port) &&
+                                 (tb->ib_addr_ipv4 == INADDR_ANY))
+                                     goto found;
+             }
+     } else {
+             inet_bind_bucket_for_each(tb, node, &bhead->chain)
+                     if ((ib_net(tb) == net) && (tb->port == port))
+                             goto found;
+     }
+
+     tb = NULL;
+found:
+     if (portaddr_bhead != portaddrany_bhead)
+             spin_unlock(&portaddrany_bhead->lock);
+
+     /* the other locks remain taken, as the caller
+      * may want to change the hash tabels */
+     return tb;
+}
+
+
How this is going to work with IPv6 sockets in the middle of the
chains ?
Now I see it might not work that well. I think I should just skip them
here and only check the IPv4 sockets.
Also, comments are not properly formatted, they should all look like :

       /* the other locks remain taken, as the caller
        * may want to change the hash tables
        */

And finally, make sure LOCKDEP is happy with your locking code.
I will check that too.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help