sockstat(1): use tree(3) rbtree instead of hash

o Use tree to lookup by socket kvaddr. The size of hash is too big for a
  small virtual machine and at the same time too little for a large
  production server.  A tree would better fit here.
o For those pcbs, that don't have a socket associated, use a list.
o Provide a second tree to lookup by pcb kvaddr.  These removes full hash
  traversal when printing every unix(4) socket.

Reviewed by:		tuexen
Differential revision:	https://reviews.freebsd.org/D35724
This commit is contained in:
Gleb Smirnoff 2022-07-06 22:19:08 -07:00
parent 7d016011f4
commit a83d596f43

View File

@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/jail.h> #include <sys/jail.h>
#include <sys/user.h> #include <sys/user.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/unpcb.h> #include <sys/unpcb.h>
@ -119,6 +121,11 @@ struct addr {
}; };
struct sock { struct sock {
union {
RB_ENTRY(sock) socket_tree; /* tree of pcbs with socket */
SLIST_ENTRY(sock) socket_list; /* list of pcbs w/o socket */
};
RB_ENTRY(sock) pcb_tree;
kvaddr_t socket; kvaddr_t socket;
kvaddr_t pcb; kvaddr_t pcb;
uint64_t inp_gencnt; uint64_t inp_gencnt;
@ -132,11 +139,25 @@ struct sock {
char cc[TCP_CA_NAME_MAX]; char cc[TCP_CA_NAME_MAX];
struct addr *laddr; struct addr *laddr;
struct addr *faddr; struct addr *faddr;
struct sock *next;
}; };
#define HASHSIZE 1009 static RB_HEAD(socks_t, sock) socks = RB_INITIALIZER(&socks);
static struct sock *sockhash[HASHSIZE]; static int64_t
socket_compare(const struct sock *a, const struct sock *b)
{
return ((int64_t)(a->socket/2 - b->socket/2));
}
RB_GENERATE_STATIC(socks_t, sock, socket_tree, socket_compare);
static RB_HEAD(pcbs_t, sock) pcbs = RB_INITIALIZER(&pcbs);
static int64_t
pcb_compare(const struct sock *a, const struct sock *b)
{
return ((int64_t)(a->pcb/2 - b->pcb/2));
}
RB_GENERATE_STATIC(pcbs_t, sock, pcb_tree, pcb_compare);
static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks);
static struct xfile *xfiles; static struct xfile *xfiles;
static int nxfiles; static int nxfiles;
@ -350,7 +371,7 @@ gather_sctp(void)
const char *varname; const char *varname;
size_t len, offset; size_t len, offset;
char *buf; char *buf;
int hash, vflag; int vflag;
int no_stcb, local_all_loopback, foreign_all_loopback; int no_stcb, local_all_loopback, foreign_all_loopback;
vflag = 0; vflag = 0;
@ -469,10 +490,7 @@ gather_sctp(void)
(!opt_L || !local_all_loopback) && (!opt_L || !local_all_loopback) &&
((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) || ((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) ||
(xstcb->last == 1))) { (xstcb->last == 1))) {
hash = (int)((uintptr_t)sock->socket % RB_INSERT(socks_t, &socks, sock);
HASHSIZE);
sock->next = sockhash[hash];
sockhash[hash] = sock;
} else { } else {
free_socket(sock); free_socket(sock);
} }
@ -597,10 +615,7 @@ gather_sctp(void)
(!opt_L || (!opt_L ||
!(local_all_loopback || !(local_all_loopback ||
foreign_all_loopback))) { foreign_all_loopback))) {
hash = (int)((uintptr_t)sock->socket % RB_INSERT(socks_t, &socks, sock);
HASHSIZE);
sock->next = sockhash[hash];
sockhash[hash] = sock;
} else { } else {
free_socket(sock); free_socket(sock);
} }
@ -624,7 +639,7 @@ gather_inet(int proto)
const char *varname, *protoname; const char *varname, *protoname;
size_t len, bufsize; size_t len, bufsize;
void *buf; void *buf;
int hash, retry, vflag; int retry, vflag;
vflag = 0; vflag = 0;
if (opt_4) if (opt_4)
@ -760,9 +775,10 @@ gather_inet(int proto)
memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX); memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX);
} }
sock->protoname = protoname; sock->protoname = protoname;
hash = (int)((uintptr_t)sock->socket % HASHSIZE); if (sock->socket != 0)
sock->next = sockhash[hash]; RB_INSERT(socks_t, &socks, sock);
sockhash[hash] = sock; else
SLIST_INSERT_HEAD(&nosocks, sock, socket_list);
} }
out: out:
free(buf); free(buf);
@ -778,7 +794,7 @@ gather_unix(int proto)
const char *varname, *protoname; const char *varname, *protoname;
size_t len, bufsize; size_t len, bufsize;
void *buf; void *buf;
int hash, retry; int retry;
switch (proto) { switch (proto) {
case SOCK_STREAM: case SOCK_STREAM:
@ -852,9 +868,8 @@ gather_unix(int proto)
faddr->next = NULL; faddr->next = NULL;
sock->laddr = laddr; sock->laddr = laddr;
sock->faddr = faddr; sock->faddr = faddr;
hash = (int)((uintptr_t)sock->socket % HASHSIZE); RB_INSERT(socks_t, &socks, sock);
sock->next = sockhash[hash]; RB_INSERT(pcbs_t, &pcbs, sock);
sockhash[hash] = sock;
} }
out: out:
free(buf); free(buf);
@ -1052,7 +1067,7 @@ static void
displaysock(struct sock *s, int pos) displaysock(struct sock *s, int pos)
{ {
kvaddr_t p; kvaddr_t p;
int hash, first, offset; int first, offset;
struct addr *laddr, *faddr; struct addr *laddr, *faddr;
struct sock *s_tmp; struct sock *s_tmp;
@ -1104,15 +1119,8 @@ displaysock(struct sock *s, int pos)
break; break;
} }
pos += xprintf("-> "); pos += xprintf("-> ");
for (hash = 0; hash < HASHSIZE; ++hash) { s_tmp = RB_FIND(pcbs_t, &pcbs,
for (s_tmp = sockhash[hash]; &(struct sock){ .pcb = p });
s_tmp != NULL;
s_tmp = s_tmp->next)
if (s_tmp->pcb == p)
break;
if (s_tmp != NULL)
break;
}
if (s_tmp == NULL || s_tmp->laddr == NULL || if (s_tmp == NULL || s_tmp->laddr == NULL ||
s_tmp->laddr->address.ss_len == 0) s_tmp->laddr->address.ss_len == 0)
pos += xprintf("??"); pos += xprintf("??");
@ -1222,7 +1230,7 @@ display(void)
struct passwd *pwd; struct passwd *pwd;
struct xfile *xf; struct xfile *xf;
struct sock *s; struct sock *s;
int hash, n, pos; int n, pos;
if (opt_q != 1) { if (opt_q != 1) {
printf("%-8s %-10s %-5s %-2s %-6s %-*s %-*s", printf("%-8s %-10s %-5s %-2s %-6s %-*s %-*s",
@ -1250,12 +1258,9 @@ display(void)
continue; continue;
if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid)) if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
continue; continue;
hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); s = RB_FIND(socks_t, &socks,
for (s = sockhash[hash]; s != NULL; s = s->next) { &(struct sock){ .socket = xf->xf_data});
if (s->socket != xf->xf_data) if (s != NULL && check_ports(s)) {
continue;
if (!check_ports(s))
continue;
s->shown = 1; s->shown = 1;
pos = 0; pos = 0;
if (opt_n || if (opt_n ||
@ -1277,17 +1282,21 @@ display(void)
} }
if (opt_j >= 0) if (opt_j >= 0)
return; return;
for (hash = 0; hash < HASHSIZE; hash++) { SLIST_FOREACH(s, &nosocks, socket_list) {
for (s = sockhash[hash]; s != NULL; s = s->next) { if (!check_ports(s))
if (s->shown) continue;
continue; pos = xprintf("%-8s %-10s %-5s %-2s ",
if (!check_ports(s)) "?", "?", "?", "?");
continue; displaysock(s, pos);
pos = 0; }
pos += xprintf("%-8s %-10s %-5s %-2s ", RB_FOREACH(s, socks_t, &socks) {
"?", "?", "?", "?"); if (s->shown)
displaysock(s, pos); continue;
} if (!check_ports(s))
continue;
pos = xprintf("%-8s %-10s %-5s %-2s ",
"?", "?", "?", "?");
displaysock(s, pos);
} }
} }