tcp: seq_file: Add listening_get_first()
authorMartin KaFai Lau <kafai@fb.com>
Thu, 1 Jul 2021 20:06:00 +0000 (13:06 -0700)
committerAndrii Nakryiko <andrii@kernel.org>
Fri, 23 Jul 2021 23:44:53 +0000 (16:44 -0700)
The current listening_get_next() is overloaded by passing
NULL to the 2nd arg, like listening_get_next(seq, NULL), to
mean get_first().

This patch moves some logic from the listening_get_next() into
a new function listening_get_first().  It will be equivalent
to the current established_get_first() and established_get_next()
setup.  get_first() is to find a non empty bucket and return
the first sk.  get_next() is to find the next sk of the current
bucket and then resorts to get_first() if the current bucket is
exhausted.

The next patch is to move the listener seq_file iteration from
listening_hash (port only) to lhash2 (port+addr).
Separating out listening_get_first() from listening_get_next()
here will make the following lhash2 changes cleaner and easier to
follow.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20210701200600.1035353-1-kafai@fb.com
net/ipv4/tcp_ipv4.c

index 665f99d144367ea24a4611e1053197e650c8930a..48a0a3873c7aed3aee6401698ab9f2a3dea06a0e 100644 (file)
@@ -2288,10 +2288,38 @@ static bool seq_sk_match(struct seq_file *seq, const struct sock *sk)
                net_eq(sock_net(sk), seq_file_net(seq)));
 }
 
-/*
- * Get next listener socket follow cur.  If cur is NULL, get first socket
- * starting from bucket given in st->bucket; when st->bucket is zero the
- * very first socket in the hash table is returned.
+/* Find a non empty bucket (starting from st->bucket)
+ * and return the first sk from it.
+ */
+static void *listening_get_first(struct seq_file *seq)
+{
+       struct tcp_iter_state *st = seq->private;
+
+       st->offset = 0;
+       for (; st->bucket < INET_LHTABLE_SIZE; st->bucket++) {
+               struct inet_listen_hashbucket *ilb;
+               struct hlist_nulls_node *node;
+               struct sock *sk;
+
+               ilb = &tcp_hashinfo.listening_hash[st->bucket];
+               if (hlist_nulls_empty(&ilb->nulls_head))
+                       continue;
+
+               spin_lock(&ilb->lock);
+               sk_nulls_for_each(sk, node, &ilb->nulls_head) {
+                       if (seq_sk_match(seq, sk))
+                               return sk;
+               }
+               spin_unlock(&ilb->lock);
+       }
+
+       return NULL;
+}
+
+/* Find the next sk of "cur" within the same bucket (i.e. st->bucket).
+ * If "cur" is the last one in the st->bucket,
+ * call listening_get_first() to return the first sk of the next
+ * non empty bucket.
  */
 static void *listening_get_next(struct seq_file *seq, void *cur)
 {
@@ -2300,29 +2328,20 @@ static void *listening_get_next(struct seq_file *seq, void *cur)
        struct hlist_nulls_node *node;
        struct sock *sk = cur;
 
-       if (!sk) {
-get_head:
-               ilb = &tcp_hashinfo.listening_hash[st->bucket];
-               spin_lock(&ilb->lock);
-               sk = sk_nulls_head(&ilb->nulls_head);
-               st->offset = 0;
-               goto get_sk;
-       }
-       ilb = &tcp_hashinfo.listening_hash[st->bucket];
        ++st->num;
        ++st->offset;
 
        sk = sk_nulls_next(sk);
-get_sk:
+
        sk_nulls_for_each_from(sk, node) {
                if (seq_sk_match(seq, sk))
                        return sk;
        }
+
+       ilb = &tcp_hashinfo.listening_hash[st->bucket];
        spin_unlock(&ilb->lock);
-       st->offset = 0;
-       if (++st->bucket < INET_LHTABLE_SIZE)
-               goto get_head;
-       return NULL;
+       ++st->bucket;
+       return listening_get_first(seq);
 }
 
 static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
@@ -2332,7 +2351,7 @@ static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
 
        st->bucket = 0;
        st->offset = 0;
-       rc = listening_get_next(seq, NULL);
+       rc = listening_get_first(seq);
 
        while (rc && *pos) {
                rc = listening_get_next(seq, rc);
@@ -2440,7 +2459,7 @@ static void *tcp_seek_last_pos(struct seq_file *seq)
                if (st->bucket >= INET_LHTABLE_SIZE)
                        break;
                st->state = TCP_SEQ_STATE_LISTENING;
-               rc = listening_get_next(seq, NULL);
+               rc = listening_get_first(seq);
                while (offset-- && rc && bucket == st->bucket)
                        rc = listening_get_next(seq, rc);
                if (rc)