From 2d22f334ea6f0a6c51fc4ac0b15f25631f4c85ea Mon Sep 17 00:00:00 2001 From: Robert Watson Date: Mon, 22 Feb 2010 15:03:16 +0000 Subject: Export netisr configuration and statistics to userspace via sysctl(9). MFC after: 1 week Sponsored by: Juniper Networks --- sys/net/netisr.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sys/net/netisr.h | 88 +++++++++++++++++++++++++++-- 2 files changed, 251 insertions(+), 5 deletions(-) (limited to 'sys') diff --git a/sys/net/netisr.c b/sys/net/netisr.c index 3574718735c8..d74682f6e924 100644 --- a/sys/net/netisr.c +++ b/sys/net/netisr.c @@ -1,7 +1,11 @@ /*- * Copyright (c) 2007-2009 Robert N. M. Watson + * Copyright (c) 2010 Juniper Networks, Inc. * All rights reserved. * + * This software was developed by Robert N. M. Watson under contract + * to Juniper Networks, Inc. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -1126,6 +1130,170 @@ netisr_start(void *arg) } SYSINIT(netisr_start, SI_SUB_SMP, SI_ORDER_MIDDLE, netisr_start, NULL); +/* + * Sysctl monitoring for netisr: query a list of registered protocols. + */ +static int +sysctl_netisr_proto(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker tracker; + struct sysctl_netisr_proto *snpp, *snp_array; + struct netisr_proto *npp; + u_int counter, proto; + int error; + + if (req->newptr != NULL) + return (EINVAL); + snp_array = malloc(sizeof(*snp_array) * NETISR_MAXPROT, M_TEMP, + M_ZERO | M_WAITOK); + counter = 0; + NETISR_RLOCK(&tracker); + for (proto = 0; proto < NETISR_MAXPROT; proto++) { + npp = &np[proto]; + if (npp->np_name == NULL) + continue; + snpp = &snp_array[counter]; + snpp->snp_version = sizeof(*snpp); + strlcpy(snpp->snp_name, npp->np_name, NETISR_NAMEMAXLEN); + snpp->snp_proto = proto; + 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; + counter++; + } + NETISR_RUNLOCK(&tracker); + KASSERT(counter < NETISR_MAXPROT, + ("sysctl_netisr_proto: counter too big (%d)", counter)); + error = SYSCTL_OUT(req, snp_array, sizeof(*snp_array) * counter); + free(snp_array, M_TEMP); + return (error); +} + +SYSCTL_PROC(_net_isr, OID_AUTO, proto, + CTLFLAG_RD|CTLTYPE_STRUCT|CTLFLAG_MPSAFE, 0, 0, sysctl_netisr_proto, + "S,sysctl_netisr_proto", + "Return list of protocols registered with netisr"); + +/* + * Sysctl monitoring for netisr: query a list of workstreams. + */ +static int +sysctl_netisr_workstream(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker tracker; + struct sysctl_netisr_workstream *snwsp, *snws_array; + struct netisr_workstream *nwsp; + u_int counter, cpuid; + int error; + + if (req->newptr != NULL) + return (EINVAL); + snws_array = malloc(sizeof(*snws_array) * MAXCPU, M_TEMP, + M_ZERO | M_WAITOK); + counter = 0; + NETISR_RLOCK(&tracker); + for (cpuid = 0; cpuid < MAXCPU; cpuid++) { + if (CPU_ABSENT(cpuid)) + continue; + nwsp = DPCPU_ID_PTR(cpuid, nws); + if (nwsp->nws_intr_event == NULL) + continue; + NWS_LOCK(nwsp); + snwsp = &snws_array[counter]; + snwsp->snws_version = sizeof(*snwsp); + + /* + * For now, we equate workstream IDs and CPU IDs in the + * kernel, but expose them independently to userspace in case + * that assumption changes in the future. + */ + snwsp->snws_wsid = cpuid; + snwsp->snws_cpu = cpuid; + if (nwsp->nws_intr_event != NULL) + snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR; + NWS_UNLOCK(nwsp); + counter++; + } + NETISR_RUNLOCK(&tracker); + KASSERT(counter < MAXCPU, + ("sysctl_netisr_workstream: counter too big (%d)", counter)); + error = SYSCTL_OUT(req, snws_array, sizeof(*snws_array) * counter); + free(snws_array, M_TEMP); + return (error); +} + +SYSCTL_PROC(_net_isr, OID_AUTO, workstream, + CTLFLAG_RD|CTLTYPE_STRUCT|CTLFLAG_MPSAFE, 0, 0, sysctl_netisr_workstream, + "S,sysctl_netisr_workstream", + "Return list of workstreams implemented by netisr"); + +/* + * Sysctl monitoring for netisr: query per-protocol data across all + * workstreams. + */ +static int +sysctl_netisr_work(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker tracker; + struct sysctl_netisr_work *snwp, *snw_array; + struct netisr_workstream *nwsp; + struct netisr_proto *npp; + struct netisr_work *nwp; + u_int counter, cpuid, proto; + int error; + + if (req->newptr != NULL) + return (EINVAL); + snw_array = malloc(sizeof(*snw_array) * MAXCPU * NETISR_MAXPROT, + M_TEMP, M_ZERO | M_WAITOK); + counter = 0; + NETISR_RLOCK(&tracker); + for (cpuid = 0; cpuid < MAXCPU; cpuid++) { + if (CPU_ABSENT(cpuid)) + continue; + nwsp = DPCPU_ID_PTR(cpuid, nws); + if (nwsp->nws_intr_event == NULL) + continue; + NWS_LOCK(nwsp); + for (proto = 0; proto < NETISR_MAXPROT; proto++) { + npp = &np[proto]; + if (npp->np_name == NULL) + continue; + nwp = &nwsp->nws_work[proto]; + snwp = &snw_array[counter]; + snwp->snw_version = sizeof(*snwp); + snwp->snw_wsid = cpuid; /* See comment above. */ + 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++; + } + NWS_UNLOCK(nwsp); + } + KASSERT(counter < MAXCPU * NETISR_MAXPROT, + ("sysctl_netisr_work: counter too big (%d)", counter)); + NETISR_RUNLOCK(&tracker); + error = SYSCTL_OUT(req, snw_array, sizeof(*snw_array) * counter); + free(snw_array, M_TEMP); + return (error); +} + +SYSCTL_PROC(_net_isr, OID_AUTO, work, + CTLFLAG_RD|CTLTYPE_STRUCT|CTLFLAG_MPSAFE, 0, 0, sysctl_netisr_work, + "S,sysctl_netisr_work", + "Return list of per-workstream, per-protocol work in netisr"); + #ifdef DDB DB_SHOW_COMMAND(netisr, db_show_netisr) { diff --git a/sys/net/netisr.h b/sys/net/netisr.h index 0ab424fe5221..e5ccbdb8dfeb 100644 --- a/sys/net/netisr.h +++ b/sys/net/netisr.h @@ -1,7 +1,11 @@ /*- * Copyright (c) 2007-2009 Robert N. M. Watson + * Copyright (c) 2010 Juniper Networks, Inc. * All rights reserved. * + * This software was developed by Robert N. M. Watson under contract + * to Juniper Networks, Inc. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -28,7 +32,6 @@ #ifndef _NET_NETISR_H_ #define _NET_NETISR_H_ -#ifdef _KERNEL /* * The netisr (network interrupt service routine) provides a deferred @@ -39,6 +42,13 @@ * Historically, this was implemented by the BSD software ISR facility; it is * now implemented via a software ithread (SWI). */ + +/* + * Protocol numbers, which are encoded in monitoring applications and kernel + * modules. Internally, these are used in bit shift operations so must have + * a value 0 < proto < 32; we currently further limit at compile-time to 16 + * for array-sizing purposes. + */ #define NETISR_IP 1 #define NETISR_IGMP 2 /* IGMPv3 output queue */ #define NETISR_ROUTE 3 /* routing socket */ @@ -52,6 +62,78 @@ #define NETISR_NATM 11 #define NETISR_EPAIR 12 /* if_epair(4) */ +/* + * Protocol ordering and affinity policy constants. See the detailed + * discussion of policies later in the file. + */ +#define NETISR_POLICY_SOURCE 1 /* Maintain source ordering. */ +#define NETISR_POLICY_FLOW 2 /* Maintain flow ordering. */ +#define NETISR_POLICY_CPU 3 /* Protocol determines CPU placement. */ + +/* + * Monitoring data structures, exported by sysctl(2). + * + * Three sysctls are defined. First, a per-protocol structure exported by + * net.isr.proto. + */ +#define NETISR_NAMEMAXLEN 32 +struct sysctl_netisr_proto { + u_int snp_version; /* Length of struct. */ + char snp_name[NETISR_NAMEMAXLEN]; /* nh_name */ + u_int snp_proto; /* nh_proto */ + u_int snp_qlimit; /* nh_qlimit */ + u_int snp_policy; /* nh_policy */ + u_int snp_flags; /* Various flags. */ + u_int _snp_ispare[7]; +}; + +/* + * Flags for sysctl_netisr_proto.snp_flags. + */ +#define NETISR_SNP_FLAGS_M2FLOW 0x00000001 /* nh_m2flow */ +#define NETISR_SNP_FLAGS_M2CPUID 0x00000002 /* nh_m2cpuid */ +#define NETISR_SNP_FLAGS_DRAINEDCPU 0x00000003 /* nh_drainedcpu */ + +/* + * Next, a structure per-workstream, with per-protocol data, exported as + * net.isr.workstream. + */ +struct sysctl_netisr_workstream { + u_int snws_version; /* Length of struct. */ + u_int snws_flags; /* Various flags. */ + u_int snws_wsid; /* Workstream ID. */ + u_int snws_cpu; /* nws_cpu */ + u_int _snws_ispare[12]; +}; + +/* + * Flags for sysctl_netisr_workstream.snws_flags + */ +#define NETISR_SNWS_FLAGS_INTR 0x00000001 /* nws_intr_event */ + +/* + * Finally, a per-workstream-per-protocol structure, exported as + * net.isr.work. + */ +struct sysctl_netisr_work { + u_int snw_version; /* Length of struct. */ + u_int snw_wsid; /* Workstream ID. */ + u_int snw_proto; /* Protocol number. */ + u_int snw_len; /* nw_len */ + u_int snw_watermark; /* nw_watermark */ + u_int _snw_ispare[3]; + + uint64_t snw_dispatched; /* nw_dispatched */ + uint64_t snw_hybrid_dispatched; /* nw_hybrid_dispatched */ + uint64_t snw_qdrops; /* nw_qdrops */ + uint64_t snw_queued; /* nw_queued */ + uint64_t snw_handled; /* nw_handled */ + + uint64_t _snw_llspare[7]; +}; + +#ifdef _KERNEL + /*- * Protocols express ordering constraints and affinity preferences by * implementing one or neither of nh_m2flow and nh_m2cpuid, which are used by @@ -91,10 +173,6 @@ typedef struct mbuf *netisr_m2cpuid_t(struct mbuf *m, uintptr_t source, typedef struct mbuf *netisr_m2flow_t(struct mbuf *m, uintptr_t source); typedef void netisr_drainedcpu_t(u_int cpuid); -#define NETISR_POLICY_SOURCE 1 /* Maintain source ordering. */ -#define NETISR_POLICY_FLOW 2 /* Maintain flow ordering. */ -#define NETISR_POLICY_CPU 3 /* Protocol determines CPU placement. */ - /* * Data structure describing a protocol handler. */ -- cgit v1.2.3