tipc: fix socket list regression in new nl api
authorRichard Alpe <richard.alpe@ericsson.com>
Fri, 16 Jan 2015 11:30:40 +0000 (12:30 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 18 Jan 2015 05:27:05 +0000 (00:27 -0500)
Commit 07f6c4bc (tipc: convert tipc reference table to use generic
rhashtable) introduced a problem with port listing in the new netlink
API. It broke the resume functionality resulting in a never ending
loop. This was caused by starting with the first hash table every time
subsequently never returning an empty skb (terminating).

This patch fixes the resume mechanism by keeping a logical reference
to the last hash table along with a logical reference to the socket
(port) that didn't fit in the previous message.

Signed-off-by: Richard Alpe <richard.alpe@ericsson.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Reviewed-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/tipc/socket.c

index 720fda6cc2e6223771f3df5b21885cb10d5c7b2d..679a22082fcbc73480df514fe2b0dafc7aa5844c 100644 (file)
@@ -2749,29 +2749,35 @@ int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb)
        struct tipc_sock *tsk;
        const struct bucket_table *tbl;
        struct rhash_head *pos;
-       u32 prev_portid = cb->args[0];
-       u32 portid = prev_portid;
        struct net *net = sock_net(skb->sk);
        struct tipc_net *tn = net_generic(net, tipc_net_id);
-       int i;
+       u32 tbl_id = cb->args[0];
+       u32 prev_portid = cb->args[1];
 
        rcu_read_lock();
        tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht);
-       for (i = 0; i < tbl->size; i++) {
-               rht_for_each_entry_rcu(tsk, pos, tbl, i, node) {
+       for (; tbl_id < tbl->size; tbl_id++) {
+               rht_for_each_entry_rcu(tsk, pos, tbl, tbl_id, node) {
                        spin_lock_bh(&tsk->sk.sk_lock.slock);
-                       portid = tsk->portid;
+                       if (prev_portid && prev_portid != tsk->portid) {
+                               spin_unlock_bh(&tsk->sk.sk_lock.slock);
+                               continue;
+                       }
+
                        err = __tipc_nl_add_sk(skb, cb, tsk);
+                       if (err) {
+                               prev_portid = tsk->portid;
+                               spin_unlock_bh(&tsk->sk.sk_lock.slock);
+                               goto out;
+                       }
+                       prev_portid = 0;
                        spin_unlock_bh(&tsk->sk.sk_lock.slock);
-                       if (err)
-                               break;
-
-                       prev_portid = portid;
                }
        }
+out:
        rcu_read_unlock();
-
-       cb->args[0] = prev_portid;
+       cb->args[0] = tbl_id;
+       cb->args[1] = prev_portid;
 
        return skb->len;
 }