Commit b72acf45 authored by Martin KaFai Lau's avatar Martin KaFai Lau Committed by Andrii Nakryiko
Browse files

tcp: seq_file: Add listening_get_first()



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: default avatarMartin KaFai Lau <kafai@fb.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarKuniyuki Iwashima <kuniyu@amazon.co.jp>
Acked-by: default avatarYonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20210701200600.1035353-1-kafai@fb.com
parent 62001372
Loading
Loading
Loading
Loading
+39 −20
Original line number Diff line number Diff line
@@ -2288,41 +2288,60 @@ 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_next(struct seq_file *seq, void *cur)
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 = cur;
		struct sock *sk;

	if (!sk) {
get_head:
		ilb = &tcp_hashinfo.listening_hash[st->bucket];
		if (hlist_nulls_empty(&ilb->nulls_head))
			continue;

		spin_lock(&ilb->lock);
		sk = sk_nulls_head(&ilb->nulls_head);
		st->offset = 0;
		goto get_sk;
		sk_nulls_for_each(sk, node, &ilb->nulls_head) {
			if (seq_sk_match(seq, sk))
				return sk;
		}
	ilb = &tcp_hashinfo.listening_hash[st->bucket];
		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)
{
	struct tcp_iter_state *st = seq->private;
	struct inet_listen_hashbucket *ilb;
	struct hlist_nulls_node *node;
	struct sock *sk = cur;

	++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)