aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Watson <rwatson@FreeBSD.org>2010-03-01 00:46:45 +0000
committerRobert Watson <rwatson@FreeBSD.org>2010-03-01 00:46:45 +0000
commit88737be2ddd3e5de3365db49abfb4e84e191e3b6 (patch)
treeed5fcd9fe982b9611ab18cd524be07772df7e004
parent60efbc999152cf8afcd5512f6cd6cd72168923b4 (diff)
Notes
-rw-r--r--usr.bin/netstat/main.c8
-rw-r--r--usr.bin/netstat/netisr.c294
-rw-r--r--usr.bin/netstat/netstat.h2
3 files changed, 278 insertions, 26 deletions
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
index 6cdd7e4c8dfa..f54db4ee700f 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -530,9 +530,11 @@ main(int argc, char *argv[])
exit(0);
}
if (Qflag) {
- if (!live)
- usage();
- netisr_stats();
+ if (!live) {
+ if (kread(0, NULL, 0) == 0)
+ netisr_stats(kvmd);
+ } else
+ netisr_stats(NULL);
exit(0);
}
#if 0
diff --git a/usr.bin/netstat/netisr.c b/usr.bin/netstat/netisr.c
index c2503f4d70a9..d427080a2b59 100644
--- a/usr.bin/netstat/netisr.c
+++ b/usr.bin/netstat/netisr.c
@@ -31,15 +31,22 @@
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/sysctl.h>
+#include <sys/_lock.h>
+#include <sys/_mutex.h>
+
+#define _WANT_NETISR_INTERNAL
#include <net/netisr.h>
+#include <net/netisr_internal.h>
#include <err.h>
+#include <kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "netstat.h"
@@ -65,6 +72,90 @@ static u_int workstream_array_len;
static struct sysctl_netisr_work *work_array;
static u_int work_array_len;
+static u_int *nws_array;
+
+static u_int maxprot;
+
+static void
+netisr_load_kvm_uint(kvm_t *kd, char *name, u_int *p)
+{
+ struct nlist nl[] = {
+ { .n_name = name },
+ { .n_name = NULL },
+ };
+ int ret;
+
+ ret = kvm_nlist(kd, nl);
+ if (ret < 0)
+ errx(-1, "%s: kvm_nlist(%s): %s", __func__, name,
+ kvm_geterr(kd));
+ if (ret != 0)
+ errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__,
+ name);
+ if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p))
+ errx(-1, "%s: kvm_read(%s): %s", __func__, name,
+ kvm_geterr(kd));
+}
+
+/*
+ * Load a nul-terminated string from KVM up to 'limit', guarantee that the
+ * string in local memory is nul-terminated.
+ */
+static void
+netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit)
+{
+ u_int i;
+
+ for (i = 0; i < limit; i++) {
+ if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) !=
+ sizeof(dest[i]))
+ err(-1, "%s: kvm_read: %s", __func__,
+ kvm_geterr(kd));
+ if (dest[i] == '\0')
+ break;
+ }
+ dest[limit - 1] = '\0';
+}
+
+static const char *
+netisr_proto2name(u_int proto)
+{
+ u_int i;
+
+ for (i = 0; i < proto_array_len; i++) {
+ if (proto_array[i].snp_proto == proto)
+ return (proto_array[i].snp_name);
+ }
+ return ("unknown");
+}
+
+static int
+netisr_protoispresent(u_int proto)
+{
+ u_int i;
+
+ for (i = 0; i < proto_array_len; i++) {
+ if (proto_array[i].snp_proto == proto)
+ return (1);
+ }
+ return (0);
+}
+
+static void
+netisr_load_kvm_config(kvm_t *kd)
+{
+
+ netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads);
+ netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads);
+ netisr_load_kvm_uint(kd, "_nws_count", &numthreads);
+
+ netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit);
+ netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit);
+
+ netisr_load_kvm_uint(kd, "_netisr_direct", &direct);
+ netisr_load_kvm_uint(kd, "_netisr_direct_force", &direct_force);
+}
+
static void
netisr_load_sysctl_uint(const char *name, u_int *p)
{
@@ -78,7 +169,7 @@ netisr_load_sysctl_uint(const char *name, u_int *p)
}
static void
-netisr_load_config(void)
+netisr_load_sysctl_config(void)
{
netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads);
@@ -93,7 +184,80 @@ netisr_load_config(void)
}
static void
-netisr_load_proto(void)
+netisr_load_kvm_proto(kvm_t *kd)
+{
+ struct nlist nl[] = {
+#define NLIST_NETISR_PROTO 0
+ { .n_name = "_netisr_proto" },
+ { .n_name = NULL },
+ };
+ struct netisr_proto *np_array, *npp;
+ u_int i, protocount;
+ struct sysctl_netisr_proto *snpp;
+ size_t len;
+ int ret;
+
+ /*
+ * Kernel compile-time and user compile-time definitions of
+ * NETISR_MAXPROT must match, as we use that to size work arrays.
+ */
+ netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot);
+ if (maxprot != NETISR_MAXPROT)
+ errx(-1, "%s: NETISR_MAXPROT mismatch", __func__);
+ len = maxprot * sizeof(*np_array);
+ np_array = malloc(len);
+ if (np_array == NULL)
+ err(-1, "%s: malloc", __func__);
+ ret = kvm_nlist(kd, nl);
+ if (ret < 0)
+ errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__,
+ kvm_geterr(kd));
+ if (ret != 0)
+ errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol",
+ __func__);
+ if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) !=
+ (ssize_t)len)
+ errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__,
+ kvm_geterr(kd));
+
+ /*
+ * Size and allocate memory to hold only live protocols.
+ */
+ protocount = 0;
+ for (i = 0; i < maxprot; i++) {
+ if (np_array[i].np_name == NULL)
+ continue;
+ protocount++;
+ }
+ proto_array = calloc(protocount, sizeof(*proto_array));
+ if (proto_array == NULL)
+ err(-1, "malloc");
+ protocount = 0;
+ for (i = 0; i < maxprot; i++) {
+ npp = &np_array[i];
+ if (npp->np_name == NULL)
+ continue;
+ snpp = &proto_array[protocount];
+ snpp->snp_version = sizeof(*snpp);
+ netisr_load_kvm_string(kd, (uintptr_t)npp->np_name,
+ snpp->snp_name, sizeof(snpp->snp_name));
+ snpp->snp_proto = i;
+ snpp->snp_qlimit = npp->np_qlimit;
+ snpp->snp_policy = npp->np_policy;
+ if (npp->np_m2flow != NULL)
+ snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW;
+ if (npp->np_m2cpuid != NULL)
+ snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID;
+ if (npp->np_drainedcpu != NULL)
+ snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU;
+ protocount++;
+ }
+ proto_array_len = protocount;
+ free(np_array);
+}
+
+static void
+netisr_load_sysctl_proto(void)
{
size_t len;
@@ -116,7 +280,96 @@ netisr_load_proto(void)
}
static void
-netisr_load_workstream(void)
+netisr_load_kvm_workstream(kvm_t *kd)
+{
+ struct nlist nl[] = {
+#define NLIST_NWS_ARRAY 0
+ { .n_name = "_nws_array" },
+ { .n_name = NULL },
+ };
+ struct netisr_workstream nws;
+ struct sysctl_netisr_workstream *snwsp;
+ struct sysctl_netisr_work *snwp;
+ struct netisr_work *nwp;
+ struct nlist nl_nws[2];
+ u_int counter, cpuid, proto, wsid;
+ size_t len;
+ int ret;
+
+ len = numthreads * sizeof(*nws_array);
+ nws_array = malloc(len);
+ if (nws_array == NULL)
+ err(-1, "malloc");
+ ret = kvm_nlist(kd, nl);
+ if (ret < 0)
+ errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd));
+ if (ret != 0)
+ errx(-1, "%s: kvm_nlist: unresolved symbol", __func__);
+ if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) !=
+ (ssize_t)len)
+ errx(-1, "%s: kvm_read(_nws_array): %s", __func__,
+ kvm_geterr(kd));
+ workstream_array = calloc(numthreads, sizeof(*workstream_array));
+ if (workstream_array == NULL)
+ err(-1, "calloc");
+ workstream_array_len = numthreads;
+ work_array = calloc(numthreads * proto_array_len, sizeof(*work_array));
+ if (work_array == NULL)
+ err(-1, "calloc");
+ counter = 0;
+ for (wsid = 0; wsid < numthreads; wsid++) {
+ cpuid = nws_array[wsid];
+ if (kvm_dpcpu_setcpu(kd, cpuid) < 0)
+ errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__,
+ cpuid, kvm_geterr(kd));
+ bzero(nl_nws, sizeof(nl_nws));
+ nl_nws[0].n_name = "_nws";
+ ret = kvm_nlist(kd, nl_nws);
+ if (ret < 0)
+ errx(-1, "%s: kvm_nlist looking up nws on CPU %u: %s",
+ __func__, cpuid, kvm_geterr(kd));
+ if (ret != 0)
+ errx(-1, "%s: kvm_nlist(nws): unresolved symbol on "
+ "CPU %u", __func__, cpuid);
+ if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) !=
+ sizeof(nws))
+ errx(-1, "%s: kvm_read(nw): %s", __func__,
+ kvm_geterr(kd));
+ snwsp = &workstream_array[wsid];
+ snwsp->snws_version = sizeof(*snwsp);
+ snwsp->snws_wsid = cpuid;
+ snwsp->snws_cpu = cpuid;
+ if (nws.nws_intr_event != NULL)
+ snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR;
+
+ /*
+ * Extract the CPU's per-protocol work information.
+ */
+ printf("counting to maxprot: %u\n", maxprot);
+ for (proto = 0; proto < maxprot; proto++) {
+ if (!netisr_protoispresent(proto))
+ continue;
+ nwp = &nws.nws_work[proto];
+ snwp = &work_array[counter];
+ snwp->snw_version = sizeof(*snwp);
+ snwp->snw_wsid = cpuid;
+ snwp->snw_proto = proto;
+ snwp->snw_len = nwp->nw_len;
+ snwp->snw_watermark = nwp->nw_watermark;
+ snwp->snw_dispatched = nwp->nw_dispatched;
+ snwp->snw_hybrid_dispatched =
+ nwp->nw_hybrid_dispatched;
+ snwp->snw_qdrops = nwp->nw_qdrops;
+ snwp->snw_queued = nwp->nw_queued;
+ snwp->snw_handled = nwp->nw_handled;
+ counter++;
+ }
+ }
+ work_array_len = counter;
+}
+
+static void
+netisr_load_sysctl_workstream(void)
{
size_t len;
@@ -140,7 +393,7 @@ netisr_load_workstream(void)
}
static void
-netisr_load_work(void)
+netisr_load_sysctl_work(void)
{
size_t len;
@@ -179,18 +432,6 @@ netisr_print_proto(struct sysctl_netisr_proto *snpp)
(snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-");
}
-static const char *
-netisr_proto2name(u_int proto)
-{
- u_int i;
-
- for (i = 0; i < proto_array_len; i++) {
- if (proto_array[i].snp_proto == proto)
- return (proto_array[i].snp_name);
- }
- return ("unknown");
-}
-
static void
netisr_print_workstream(struct sysctl_netisr_workstream *snwsp)
{
@@ -223,16 +464,25 @@ netisr_print_workstream(struct sysctl_netisr_workstream *snwsp)
}
void
-netisr_stats(void)
+netisr_stats(void *kvmd)
{
struct sysctl_netisr_workstream *snwsp;
struct sysctl_netisr_proto *snpp;
+ kvm_t *kd = kvmd;
u_int i;
- netisr_load_config();
- netisr_load_proto();
- netisr_load_workstream();
- netisr_load_work();
+ if (live) {
+ netisr_load_sysctl_config();
+ netisr_load_sysctl_proto();
+ netisr_load_sysctl_workstream();
+ netisr_load_sysctl_work();
+ } else {
+ if (kd == NULL)
+ errx(-1, "netisr_stats: !live but !kd");
+ netisr_load_kvm_config(kd);
+ netisr_load_kvm_proto(kd);
+ netisr_load_kvm_workstream(kd); /* Also does work. */
+ }
printf("Configuration:\n");
printf("%-25s %12s %12s\n", "Setting", "Value", "Maximum");
diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h
index 009a0c80d097..da3f8f38d593 100644
--- a/usr.bin/netstat/netstat.h
+++ b/usr.bin/netstat/netstat.h
@@ -115,7 +115,7 @@ void pfkey_stats(u_long, const char *, int, int);
void mbpr(void *, u_long);
-void netisr_stats(void);
+void netisr_stats(void *);
void hostpr(u_long, u_long);
void impstats(u_long, u_long);