ila: Fix use of rhashtable walk in ila_xlat.c
authorTom Herbert <tom@herbertland.com>
Wed, 27 Jun 2018 21:38:59 +0000 (14:38 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 29 Jun 2018 02:32:55 +0000 (11:32 +0900)
Perform better EAGAIN handling, handle case where ila_dump_info
fails and we missed objects in the dump, and add a skip index
to skip over ila entires in a list on a rhashtable node that have
already been visited (by a previous call to ila_nl_dump).

Signed-off-by: Tom Herbert <tom@quantonium.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/ila/ila_xlat.c

index 10ae13560b407e28643fe2ed772de868ef41804f..40f3f640e8568e41b999558074d259163720c886 100644 (file)
@@ -475,24 +475,31 @@ out_free:
 
 struct ila_dump_iter {
        struct rhashtable_iter rhiter;
+       int skip;
 };
 
 static int ila_nl_dump_start(struct netlink_callback *cb)
 {
        struct net *net = sock_net(cb->skb->sk);
        struct ila_net *ilan = net_generic(net, ila_net_id);
-       struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0];
+       struct ila_dump_iter *iter;
+       int ret;
 
-       if (!iter) {
-               iter = kmalloc(sizeof(*iter), GFP_KERNEL);
-               if (!iter)
-                       return -ENOMEM;
+       iter = kmalloc(sizeof(*iter), GFP_KERNEL);
+       if (!iter)
+               return -ENOMEM;
 
-               cb->args[0] = (long)iter;
+       ret = rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter,
+                                  GFP_KERNEL);
+       if (ret) {
+               kfree(iter);
+               return ret;
        }
 
-       return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter,
-                                   GFP_KERNEL);
+       iter->skip = 0;
+       cb->args[0] = (long)iter;
+
+       return ret;
 }
 
 static int ila_nl_dump_done(struct netlink_callback *cb)
@@ -510,20 +517,45 @@ static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0];
        struct rhashtable_iter *rhiter = &iter->rhiter;
+       int skip = iter->skip;
        struct ila_map *ila;
        int ret;
 
        rhashtable_walk_start(rhiter);
 
-       for (;;) {
-               ila = rhashtable_walk_next(rhiter);
+       /* Get first entry */
+       ila = rhashtable_walk_peek(rhiter);
+
+       if (ila && !IS_ERR(ila) && skip) {
+               /* Skip over visited entries */
+
+               while (ila && skip) {
+                       /* Skip over any ila entries in this list that we
+                        * have already dumped.
+                        */
+                       ila = rcu_access_pointer(ila->next);
+                       skip--;
+               }
+       }
 
+       skip = 0;
+
+       for (;;) {
                if (IS_ERR(ila)) {
-                       if (PTR_ERR(ila) == -EAGAIN)
-                               continue;
                        ret = PTR_ERR(ila);
-                       goto done;
+                       if (ret == -EAGAIN) {
+                               /* Table has changed and iter has reset. Return
+                                * -EAGAIN to the application even if we have
+                                * written data to the skb. The application
+                                * needs to deal with this.
+                                */
+
+                               goto out_ret;
+                       } else {
+                               break;
+                       }
                } else if (!ila) {
+                       ret = 0;
                        break;
                }
 
@@ -532,15 +564,21 @@ static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
                                             cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                             skb, ILA_CMD_GET);
                        if (ret)
-                               goto done;
+                               goto out;
 
+                       skip++;
                        ila = rcu_access_pointer(ila->next);
                }
+
+               skip = 0;
+               ila = rhashtable_walk_next(rhiter);
        }
 
-       ret = skb->len;
+out:
+       iter->skip = skip;
+       ret = (skb->len ? : ret);
 
-done:
+out_ret:
        rhashtable_walk_stop(rhiter);
        return ret;
 }