kern/167204: [kernel] terrible "netstat -rn" performance due to slow kvm_nlist()

Jilles Tjoelker jilles at stack.nl
Sat Oct 19 22:20:02 UTC 2013


The following reply was made to PR kern/167204; it has been noted by GNATS.

From: Jilles Tjoelker <jilles at stack.nl>
To: bug-followup at FreeBSD.org, eugen at grosbein.net
Cc: "Eugene M. Zheganin" <emz at zhegan.in>
Subject: Re: kern/167204: [kernel] terrible "netstat -rn" performance due to
 slow kvm_nlist()
Date: Sun, 20 Oct 2013 00:15:33 +0200

 In PR kern/167204, you wrote:
 > ['netstat -rn' is extremely slow]
 
 On a machine here with a faster CPU, 'netstat -rn' takes about 100ms
 which I still consider very slow.
 
 I see two main causes of the slowness here:
 
 * kldsym(2) is very slow, particularly if debug symbols are loaded or on
   architectures such as amd64 that use 'ELF relocatable' as their kld
   module format (architectures such as i386 use 'ELF shared object'
   which has a hash table for non-debug symbols).
 
 * netstat(1) looks up all kernel symbols it could ever need, with all
   possible compile options, when it needs any kernel symbol. This
   appears to be the usual way to use libkvm but netstat is particularly
   egregious in the number of kernel symbols it may need.
 
 The below ugly patch (for head only; stable/10 has a simple conflict and
 stable/9 is full of conflicts) ensures the '-r' and '-rs' modes only
 look up the symbols necessary. This speeds up those modes considerably.
 
 Index: usr.bin/netstat/main.c
 ===================================================================
 --- usr.bin/netstat/main.c	(revision 256728)
 +++ usr.bin/netstat/main.c	(working copy)
 @@ -69,100 +69,108 @@
  #include <unistd.h>
  #include "netstat.h"
  
 +static struct nlist rs_nl[] = {
 +#define	RS_N_RTSTAT	0
 +	{ .n_name = "_rtstat" },
 +#define	RS_N_RTTRASH	1
 +	{ .n_name = "_rttrash" },
 +	{ .n_name = NULL },
 +};
 +
 +static struct nlist r_nl[] = {
 +#define	R_N_RTREE	0
 +	{ .n_name = "_rt_tables"},
 +	{ .n_name = NULL },
 +};
 +
  static struct nlist nl[] = {
  #define	N_IFNET		0
  	{ .n_name = "_ifnet" },		/* XXXGL: can be deleted */
 -#define	N_RTSTAT	1
 -	{ .n_name = "_rtstat" },
 -#define	N_RTREE		2
 -	{ .n_name = "_rt_tables"},
 -#define	N_MRTSTAT	3
 +#define	N_MRTSTAT	1
  	{ .n_name = "_mrtstat" },
 -#define	N_MFCHASHTBL	4
 +#define	N_MFCHASHTBL	2
  	{ .n_name = "_mfchashtbl" },
 -#define	N_VIFTABLE	5
 +#define	N_VIFTABLE	3
  	{ .n_name = "_viftable" },
 -#define	N_IPX		6
 +#define	N_IPX		3
  	{ .n_name = "_ipxpcb_list"},
 -#define	N_IPXSTAT	7
 +#define	N_IPXSTAT	4
  	{ .n_name = "_ipxstat"},
 -#define	N_SPXSTAT	8
 +#define	N_SPXSTAT	5
  	{ .n_name = "_spx_istat"},
 -#define	N_DDPSTAT	9
 +#define	N_DDPSTAT	6
  	{ .n_name = "_ddpstat"},
 -#define	N_DDPCB		10
 +#define	N_DDPCB		7
  	{ .n_name = "_ddpcb"},
 -#define	N_NGSOCKS	11
 +#define	N_NGSOCKS	8
  	{ .n_name = "_ngsocklist"},
 -#define	N_IP6STAT	12
 +#define	N_IP6STAT	9
  	{ .n_name = "_ip6stat" },
 -#define	N_ICMP6STAT	13
 +#define	N_ICMP6STAT	10
  	{ .n_name = "_icmp6stat" },
 -#define	N_IPSECSTAT	14
 +#define	N_IPSECSTAT	11
  	{ .n_name = "_ipsec4stat" },
 -#define	N_IPSEC6STAT	15
 +#define	N_IPSEC6STAT	12
  	{ .n_name = "_ipsec6stat" },
 -#define	N_PIM6STAT	16
 +#define	N_PIM6STAT	13
  	{ .n_name = "_pim6stat" },
 -#define	N_MRT6STAT	17
 +#define	N_MRT6STAT	14
  	{ .n_name = "_mrt6stat" },
 -#define	N_MF6CTABLE	18
 +#define	N_MF6CTABLE	15
  	{ .n_name = "_mf6ctable" },
 -#define	N_MIF6TABLE	19
 +#define	N_MIF6TABLE	16
  	{ .n_name = "_mif6table" },
 -#define	N_PFKEYSTAT	20
 +#define	N_PFKEYSTAT	17
  	{ .n_name = "_pfkeystat" },
 -#define	N_RTTRASH	21
 -	{ .n_name = "_rttrash" },
 -#define	N_CARPSTAT	22
 +#define	N_CARPSTAT	19
  	{ .n_name = "_carpstats" },
 -#define	N_PFSYNCSTAT	23
 +#define	N_PFSYNCSTAT	20
  	{ .n_name = "_pfsyncstats" },
 -#define	N_AHSTAT	24
 +#define	N_AHSTAT	21
  	{ .n_name = "_ahstat" },
 -#define	N_ESPSTAT	25
 +#define	N_ESPSTAT	22
  	{ .n_name = "_espstat" },
 -#define	N_IPCOMPSTAT	26
 +#define	N_IPCOMPSTAT	23
  	{ .n_name = "_ipcompstat" },
 -#define	N_TCPSTAT	27
 +#define	N_TCPSTAT	24
  	{ .n_name = "_tcpstat" },
 -#define	N_UDPSTAT	28
 +#define	N_UDPSTAT	25
  	{ .n_name = "_udpstat" },
 -#define	N_IPSTAT	29
 +#define	N_IPSTAT	26
  	{ .n_name = "_ipstat" },
 -#define	N_ICMPSTAT	30
 +#define	N_ICMPSTAT	27
  	{ .n_name = "_icmpstat" },
 -#define	N_IGMPSTAT	31
 +#define	N_IGMPSTAT	28
  	{ .n_name = "_igmpstat" },
 -#define	N_PIMSTAT	32
 +#define	N_PIMSTAT	29
  	{ .n_name = "_pimstat" },
 -#define	N_TCBINFO	33
 +#define	N_TCBINFO	30
  	{ .n_name = "_tcbinfo" },
 -#define	N_UDBINFO	34
 +#define	N_UDBINFO	31
  	{ .n_name = "_udbinfo" },
 -#define	N_DIVCBINFO	35
 +#define	N_DIVCBINFO	32
  	{ .n_name = "_divcbinfo" },
 -#define	N_RIPCBINFO	36
 +#define	N_RIPCBINFO	33
  	{ .n_name = "_ripcbinfo" },
 -#define	N_UNP_COUNT	37
 +#define	N_UNP_COUNT	34
  	{ .n_name = "_unp_count" },
 -#define	N_UNP_GENCNT	38
 +#define	N_UNP_GENCNT	35
  	{ .n_name = "_unp_gencnt" },
 -#define	N_UNP_DHEAD	39
 +#define	N_UNP_DHEAD	36
  	{ .n_name = "_unp_dhead" },
 -#define	N_UNP_SHEAD	40
 +#define	N_UNP_SHEAD	37
  	{ .n_name = "_unp_shead" },
 -#define	N_RIP6STAT	41
 +#define	N_RIP6STAT	38
  	{ .n_name = "_rip6stat" },
 -#define	N_SCTPSTAT	42
 +#define	N_SCTPSTAT	39
  	{ .n_name = "_sctpstat" },
 -#define	N_MFCTABLESIZE	43
 +#define	N_MFCTABLESIZE	40
  	{ .n_name = "_mfctablesize" },
 -#define	N_ARPSTAT       44
 +#define	N_ARPSTAT       41
  	{ .n_name = "_arpstat" },
 -#define	N_UNP_SPHEAD	45
 +#define	N_UNP_SPHEAD	42
  	{ .n_name = "unp_sphead" },
 -#define	N_SFSTAT	46
 +#define	N_SFSTAT	43
  	{ .n_name = "_sfstat"},
  	{ .n_name = NULL },
  };
 @@ -305,6 +313,8 @@
  static struct protox *name2protox(const char *);
  static struct protox *knownname(const char *);
  
 +static int kvmd_init(struct nlist *);
 +
  static kvm_t *kvmd;
  static char *nlistf = NULL, *memf = NULL;
  
 @@ -550,18 +560,22 @@
  	 * used for the queries, which is slower.
  	 */
  #endif
 +	if (rflag) {
 +		if (sflag) {
 +			kvmd_init(rs_nl);
 +			rt_stats(rs_nl[RS_N_RTSTAT].n_value,
 +			    rs_nl[RS_N_RTTRASH].n_value);
 +		} else {
 +			kvmd_init(r_nl);
 +			routepr(r_nl[R_N_RTREE].n_value, fib);
 +		}
 +		exit(0);
 +	}
  	kread(0, NULL, 0);
  	if (iflag && !sflag) {
  		intpr(interval, NULL);
  		exit(0);
  	}
 -	if (rflag) {
 -		if (sflag)
 -			rt_stats(nl[N_RTSTAT].n_value, nl[N_RTTRASH].n_value);
 -		else
 -			routepr(nl[N_RTREE].n_value, fib);
 -		exit(0);
 -	}
  	if (gflag) {
  		if (sflag) {
  			if (af == AF_INET || af == AF_UNSPEC)
 @@ -684,7 +698,7 @@
  }
  
  static int
 -kvmd_init(void)
 +kvmd_init(struct nlist *nl1)
  {
  	char errbuf[_POSIX2_LINE_MAX];
  
 @@ -699,7 +713,7 @@
  		return (-1);
  	}
  
 -	if (kvm_nlist(kvmd, nl) < 0) {
 +	if (kvm_nlist(kvmd, nl1) < 0) {
  		if (nlistf)
  			errx(1, "%s: kvm_nlist: %s", nlistf,
  			     kvm_geterr(kvmd));
 @@ -707,7 +721,7 @@
  			errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
  	}
  
 -	if (nl[0].n_type == 0) {
 +	if (nl1[0].n_type == 0) {
  		if (nlistf)
  			errx(1, "%s: no namelist", nlistf);
  		else
 @@ -724,7 +738,7 @@
  kread(u_long addr, void *buf, size_t size)
  {
  
 -	if (kvmd_init() < 0)
 +	if (kvmd_init(nl) < 0)
  		return (-1);
  
  	if (!buf)
 @@ -744,7 +758,7 @@
  {
  	uint64_t *c = buf;
  
 -	if (kvmd_init() < 0)
 +	if (kvmd_init(nl) < 0)
  		return (-1);
  
  	if (kread(addr, buf, size) < 0)
 
 -- 
 Jilles Tjoelker


More information about the freebsd-bugs mailing list