Modify "netstat -mb" to use libmemstat(3) when acting on a live system,

with a number of positive benefits:

- Start using UMA(9) statistics for mbufs and clusters, which avoids
  using the mbuf allocator statistics which suffer from races under
  load on SMP.  This should eliminate "negative" mbuf counts in
  netstat -mb.

- We are now able to track cached (free) mbufs and clusters and count
  it towards memory allocated by the network stack.

- We are now also able to track memory allocated to mbuf tags since
  libmemstat(3) can also query malloc(9).  We don't print this except
  as part of the total (for now - #if 0).

- We are now able to track mbuf/cluster/packet allocation failures,
  although they are not currently printed (#if 0).

- Don't print out sfbuf statistics when running on a kernel core, as
  currently that code is able only to query sysctl for statistics.

MFC after:	1 week
This commit is contained in:
rwatson 2005-07-18 08:34:15 +00:00
parent 4b8d4c3d7f
commit 20e476a513
2 changed files with 197 additions and 36 deletions

View File

@ -15,7 +15,7 @@ CFLAGS+=-DINET6
BINGRP= kmem
BINMODE=2555
DPADD= ${LIBKVM} ${LIBIPX} ${LIBNETGRAPH} ${LIBUTIL}
LDADD= -lkvm -lipx -lnetgraph -lutil
DPADD= ${LIBKVM} ${LIBIPX} ${LIBMEMSTAT} ${LIBNETGRAPH} ${LIBUTIL}
LDADD= -lkvm -lipx -lmemstat -lnetgraph -lutil
.include <bsd.prog.mk>

View File

@ -1,6 +1,8 @@
/*
* Copyright (c) 1983, 1988, 1993
* The Regents of the University of California. All rights reserved.
* The Regents of the University of California.
* Copyright (c) 2005 Robert N. M. Watson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -47,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <err.h>
#include <memstat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -93,15 +96,14 @@ static struct mbtypenames {
};
/*
* Print mbuf statistics.
* Print mbuf statistics extracted from kmem.
*/
void
mbpr(u_long mbaddr, u_long mbtaddr __unused, u_long nmbcaddr, u_long nmbufaddr,
u_long mbhiaddr, u_long clhiaddr, u_long mbloaddr, u_long clloaddr,
u_long cpusaddr __unused, u_long pgsaddr, u_long mbpaddr)
mbpr_kmem(u_long mbaddr, u_long nmbcaddr, u_long nmbufaddr, u_long mbhiaddr,
u_long clhiaddr, u_long mbloaddr, u_long clloaddr, u_long pgsaddr,
u_long mbpaddr)
{
int i, nmbclusters;
int nsfbufs, nsfbufspeak, nsfbufsused;
short nmbtypes;
size_t mlen;
long *mbtypes = NULL;
@ -115,25 +117,11 @@ mbpr(u_long mbaddr, u_long mbtaddr __unused, u_long nmbcaddr, u_long nmbufaddr,
goto err;
}
if (mbaddr) {
if (kread(mbaddr, (char *)mbstat, sizeof mbstat))
goto err;
if (kread(nmbcaddr, (char *)&nmbclusters, sizeof(int)))
goto err;
} else {
mlen = sizeof *mbstat;
if (sysctlbyname("kern.ipc.mbstat", mbstat, &mlen, NULL, 0)
< 0) {
warn("sysctl: retrieving mbstat");
goto err;
}
mlen = sizeof(int);
if (sysctlbyname("kern.ipc.nmbclusters", &nmbclusters, &mlen,
NULL, 0) < 0) {
warn("sysctl: retrieving nmbclusters");
goto err;
}
}
if (kread(mbaddr, (char *)mbstat, sizeof mbstat))
goto err;
if (kread(nmbcaddr, (char *)&nmbclusters, sizeof(int)))
goto err;
if (mbstat->m_mbufs < 0) mbstat->m_mbufs = 0; /* XXX */
if (mbstat->m_mclusts < 0) mbstat->m_mclusts = 0; /* XXX */
@ -170,15 +158,6 @@ mbpr(u_long mbaddr, u_long mbtaddr __unused, u_long nmbcaddr, u_long nmbufaddr,
printf("%lu/%d mbuf clusters in use (current/max)\n",
mbstat->m_mclusts, nmbclusters);
mlen = sizeof(nsfbufs);
if (!sysctlbyname("kern.ipc.nsfbufs", &nsfbufs, &mlen, NULL, 0) &&
!sysctlbyname("kern.ipc.nsfbufsused", &nsfbufsused, &mlen, NULL,
0) &&
!sysctlbyname("kern.ipc.nsfbufspeak", &nsfbufspeak, &mlen, NULL,
0)) {
printf("%d/%d/%d sfbufs in use (current/peak/max)\n",
nsfbufsused, nsfbufspeak, nsfbufs);
}
printf("%lu KBytes allocated to network\n", (mbstat->m_mbufs * MSIZE +
mbstat->m_mclusts * MCLBYTES) / 1024);
printf("%lu requests for sfbufs denied\n", mbstat->sf_allocfail);
@ -195,3 +174,185 @@ mbpr(u_long mbaddr, u_long mbtaddr __unused, u_long nmbcaddr, u_long nmbufaddr,
if (mbstat != NULL)
free(mbstat);
}
/*
* If running on a live kernel, directly query the zone allocator for stats
* from the four mbuf-related zones/types.
*/
static void
mbpr_sysctl(void)
{
struct memory_type_list *mtlp;
struct memory_type *mtp;
u_int64_t mbuf_count, mbuf_bytes, mbuf_free, mbuf_failures, mbuf_size;
u_int64_t cluster_count, cluster_bytes, cluster_limit, cluster_free;
u_int64_t cluster_failures, cluster_size;
u_int64_t packet_count, packet_bytes, packet_free, packet_failures;
u_int64_t tag_count, tag_bytes;
u_int64_t bytes_inuse, bytes_incache, bytes_total;
int nsfbufs, nsfbufspeak, nsfbufsused;
struct mbstat mbstat;
size_t mlen;
mtlp = memstat_mtl_alloc();
if (mtlp == NULL) {
warn("memstat_mtl_alloc");
return;
}
/*
* Use memstat_sysctl_all() because some mbuf-related memory is in
* uma(9), and some malloc(9).
*/
if (memstat_sysctl_all(mtlp, 0) < 0) {
warn("memstat_sysctl_all");
goto out;
}
mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_MEM_NAME);
if (mtp == NULL) {
warnx("memstat_mtl_find: zone %s not found", MBUF_MEM_NAME);
goto out;
}
mbuf_count = memstat_get_count(mtp);
mbuf_bytes = memstat_get_bytes(mtp);
mbuf_free = memstat_get_free(mtp);
mbuf_failures = memstat_get_failures(mtp);
mbuf_size = memstat_get_size(mtp);
mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_PACKET_MEM_NAME);
if (mtp == NULL) {
warnx("memstat_mtl_find: zone %s not found",
MBUF_PACKET_MEM_NAME);
goto out;
}
packet_count = memstat_get_count(mtp);
packet_bytes = memstat_get_bytes(mtp);
packet_free = memstat_get_free(mtp);
packet_failures = memstat_get_failures(mtp);
mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_CLUSTER_MEM_NAME);
if (mtp == NULL) {
warnx("memstat_mtl_find: zone %s not found",
MBUF_CLUSTER_MEM_NAME);
goto out;
}
cluster_count = memstat_get_count(mtp);
cluster_bytes = memstat_get_bytes(mtp);
cluster_limit = memstat_get_countlimit(mtp);
cluster_free = memstat_get_free(mtp);
cluster_failures = memstat_get_failures(mtp);
cluster_size = memstat_get_size(mtp);
mtp = memstat_mtl_find(mtlp, ALLOCATOR_MALLOC, MBUF_TAG_MEM_NAME);
if (mtp == NULL) {
warnx("memstat_mtl_find: malloc type %s not found",
MBUF_TAG_MEM_NAME);
goto out;
}
tag_count = memstat_get_count(mtp);
tag_bytes = memstat_get_bytes(mtp);
printf("%llu/%llu/%llu mbufs in use (current/cache/total)\n",
mbuf_count + packet_count, mbuf_free + packet_free,
mbuf_count + packet_count + mbuf_free + packet_free);
printf("%llu/%llu/%llu/%llu mbuf clusters in use "
"(current/cache/total/max)\n",
cluster_count - packet_free, cluster_free + packet_free,
cluster_count + cluster_free, cluster_limit);
#if 0
printf("%llu mbuf tags in use\n", tag_count);
#endif
mlen = sizeof(nsfbufs);
if (!sysctlbyname("kern.ipc.nsfbufs", &nsfbufs, &mlen, NULL, 0) &&
!sysctlbyname("kern.ipc.nsfbufsused", &nsfbufsused, &mlen, NULL,
0) &&
!sysctlbyname("kern.ipc.nsfbufspeak", &nsfbufspeak, &mlen, NULL,
0)) {
printf("%d/%d/%d sfbufs in use (current/peak/max)\n",
nsfbufsused, nsfbufspeak, nsfbufs);
}
/*-
* Calculate in-use bytes as:
* - straight mbuf memory
* - mbuf memory in packets
* - the clusters attached to packets
* - and the rest of the non-packet-attached clusters.
* - m_tag memory
* This avoids counting the clusters attached to packets in the cache.
* This currently excludes sf_buf space.
*/
bytes_inuse =
mbuf_bytes + /* straight mbuf memory */
packet_bytes + /* mbufs in packets */
(packet_count * cluster_size) + /* clusters in packets */
/* other clusters */
((cluster_count - packet_count - packet_free) * cluster_size) +
tag_bytes;
/*
* Calculate in-cache bytes as:
* - cached straught mbufs
* - cached packet mbufs
* - cached packet clusters
* - cached straight clusters
* This currently excludes sf_buf space.
*/
bytes_incache =
(mbuf_free * mbuf_size) + /* straight free mbufs */
(packet_free * mbuf_size) + /* mbufs in free packets */
(packet_free * cluster_size) + /* clusters in free packets */
(cluster_free * cluster_size); /* free clusters */
/*
* Total is bytes in use + bytes in cache. This doesn't take into
* account various other misc data structures, overhead, etc, but
* gives the user something useful despite that.
*/
bytes_total = bytes_inuse + bytes_incache;
printf("%lluK/%lluK/%lluK bytes allocated to network "
"(current/cache/total)\n", bytes_inuse / 1024,
bytes_incache / 1024, bytes_total / 1024);
#if 0
printf("%llu/%llu/%llu requests for mbufs denied (mbufs/clusters/"
"mbuf+clusters)\n", mbuf_failures, cluster_failures,
packet_failures);
#endif
mlen = sizeof(mbstat);
if (!sysctlbyname("kern.ipc.mbstat", &mbstat, &mlen, NULL, 0)) {
printf("%lu requests for sfbufs denied\n",
mbstat.sf_allocfail);
printf("%lu requests for sfbufs delayed\n",
mbstat.sf_allocwait);
printf("%lu requests for I/O initiated by sendfile\n",
mbstat.sf_iocnt);
printf("%lu calls to protocol drain routines\n",
mbstat.m_drain);
}
out:
memstat_mtl_free(mtlp);
}
/*
* Print mbuf statistics.
*/
void
mbpr(u_long mbaddr, u_long mbtaddr __unused, u_long nmbcaddr, u_long nmbufaddr,
u_long mbhiaddr, u_long clhiaddr, u_long mbloaddr, u_long clloaddr,
u_long cpusaddr __unused, u_long pgsaddr, u_long mbpaddr)
{
if (mbaddr != 0)
mbpr_kmem(mbaddr, nmbcaddr, nmbufaddr, mbhiaddr, clhiaddr,
mbloaddr, clloaddr, pgsaddr, mbpaddr);
else
mbpr_sysctl();
}