From 83dd106948480bbd62ba428d3ba47142ba13b4b8 Mon Sep 17 00:00:00 2001 From: thorpej Date: Tue, 15 Apr 2008 16:02:03 +0000 Subject: [PATCH] Make IGMP stats per-cpu. --- sys/netinet/igmp.c | 112 ++++++++++++++++++++++++++++++++++------- sys/netinet/igmp_var.h | 29 ++++++----- sys/netinet/in_proto.c | 5 +- usr.bin/netstat/inet.c | 47 ++++++++++------- 4 files changed, 140 insertions(+), 53 deletions(-) diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c index a71944950e24..a785f75c2f3f 100644 --- a/sys/netinet/igmp.c +++ b/sys/netinet/igmp.c @@ -1,4 +1,4 @@ -/* $NetBSD: igmp.c,v 1.45 2007/04/25 00:11:18 dyoung Exp $ */ +/* $NetBSD: igmp.c,v 1.46 2008/04/15 16:02:03 thorpej Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -40,7 +40,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.45 2007/04/25 00:11:18 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.46 2008/04/15 16:02:03 thorpej Exp $"); #include "opt_mrouting.h" @@ -49,6 +49,8 @@ __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.45 2007/04/25 00:11:18 dyoung Exp $"); #include #include #include +#include +#include #include #include @@ -67,7 +69,16 @@ __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.45 2007/04/25 00:11:18 dyoung Exp $"); POOL_INIT(igmp_rti_pool, sizeof(struct router_info), 0, 0, 0, "igmppl", NULL, IPL_SOFTNET); -struct igmpstat igmpstat; + +static percpu_t *igmpstat_percpu; + +#define IGMP_STATINC(x) \ +do { \ + uint64_t *_igmps_ = percpu_getref(igmpstat_percpu); \ + _igmps_[x]++; \ + percpu_putref(igmpstat_percpu); \ +} while (/*CONSTCOND*/0) + int igmp_timers_are_running; static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(rti_head); @@ -139,6 +150,13 @@ rti_delete(struct ifnet *ifp) /* MUST be called at splsoftnet */ } } +void +igmp_init(void) +{ + + igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS); +} + void igmp_input(struct mbuf *m, ...) { @@ -161,7 +179,7 @@ igmp_input(struct mbuf *m, ...) proto = va_arg(ap, int); va_end(ap); - ++igmpstat.igps_rcv_total; + IGMP_STATINC(IGMP_STAT_RCV_TOTAL); /* * Validate lengths @@ -169,14 +187,14 @@ igmp_input(struct mbuf *m, ...) minlen = iphlen + IGMP_MINLEN; ip_len = ntohs(ip->ip_len); if (ip_len < minlen) { - ++igmpstat.igps_rcv_tooshort; + IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); m_freem(m); return; } if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0) || m->m_len < minlen) { if ((m = m_pullup(m, minlen)) == 0) { - ++igmpstat.igps_rcv_tooshort; + IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); return; } ip = mtod(m, struct ip *); @@ -190,7 +208,7 @@ igmp_input(struct mbuf *m, ...) igmp = mtod(m, struct igmp *); /* No need to assert alignment here. */ if (in_cksum(m, ip_len - iphlen)) { - ++igmpstat.igps_rcv_badsum; + IGMP_STATINC(IGMP_STAT_RCV_BADSUM); m_freem(m); return; } @@ -200,7 +218,7 @@ igmp_input(struct mbuf *m, ...) switch (igmp->igmp_type) { case IGMP_HOST_MEMBERSHIP_QUERY: - ++igmpstat.igps_rcv_queries; + IGMP_STATINC(IGMP_STAT_RCV_QUERIES); if (ifp->if_flags & IFF_LOOPBACK) break; @@ -213,7 +231,7 @@ igmp_input(struct mbuf *m, ...) rti->rti_age = 0; if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { - ++igmpstat.igps_rcv_badqueries; + IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); m_freem(m); return; } @@ -238,7 +256,7 @@ igmp_input(struct mbuf *m, ...) } } else { if (!IN_MULTICAST(ip->ip_dst.s_addr)) { - ++igmpstat.igps_rcv_badqueries; + IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); m_freem(m); return; } @@ -288,14 +306,14 @@ igmp_input(struct mbuf *m, ...) break; case IGMP_v1_HOST_MEMBERSHIP_REPORT: - ++igmpstat.igps_rcv_reports; + IGMP_STATINC(IGMP_STAT_RCV_REPORTS); if (ifp->if_flags & IFF_LOOPBACK) break; if (!IN_MULTICAST(igmp->igmp_group.s_addr) || !in_hosteq(igmp->igmp_group, ip->ip_dst)) { - ++igmpstat.igps_rcv_badreports; + IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); m_freem(m); return; } @@ -322,7 +340,7 @@ igmp_input(struct mbuf *m, ...) IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); if (inm != NULL) { inm->inm_timer = 0; - ++igmpstat.igps_rcv_ourreports; + IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); switch (inm->inm_state) { case IGMP_IDLE_MEMBER: @@ -354,14 +372,14 @@ igmp_input(struct mbuf *m, ...) break; #endif - ++igmpstat.igps_rcv_reports; + IGMP_STATINC(IGMP_STAT_RCV_REPORTS); if (ifp->if_flags & IFF_LOOPBACK) break; if (!IN_MULTICAST(igmp->igmp_group.s_addr) || !in_hosteq(igmp->igmp_group, ip->ip_dst)) { - ++igmpstat.igps_rcv_badreports; + IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); m_freem(m); return; } @@ -390,7 +408,7 @@ igmp_input(struct mbuf *m, ...) IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); if (inm != NULL) { inm->inm_timer = 0; - ++igmpstat.igps_rcv_ourreports; + IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); switch (inm->inm_state) { case IGMP_DELAYING_MEMBER: @@ -573,7 +591,7 @@ igmp_sendpkt(struct in_multi *inm, int type) ip_output(m, NULL, NULL, IP_MULTICASTOPTS, &imo, NULL); - ++igmpstat.igps_snd_reports; + IGMP_STATINC(IGMP_STAT_SND_REPORTS); } void @@ -581,3 +599,63 @@ igmp_purgeif(struct ifnet *ifp) /* MUST be called at splsoftnet() */ { rti_delete(ifp); /* manipulates pools */ } + +static void +igmpstat_convert_to_user_cb(void *v1, void *v2, struct cpu_info *ci) +{ + uint64_t *igmpsc = v1; + uint64_t *igmps = v2; + u_int i; + + for (i = 0; i < IGMP_NSTATS; i++) + igmps[i] += igmpsc[i]; +} + +static void +igmpstat_convert_to_user(uint64_t *igmps) +{ + + memset(igmps, 0, sizeof(uint64_t) * IGMP_NSTATS); + percpu_foreach(igmpstat_percpu, igmpstat_convert_to_user_cb, igmps); +} + +static int +sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + uint64_t igmps[IGMP_NSTATS]; + + igmpstat_convert_to_user(igmps); + node = *rnode; + node.sysctl_data = igmps; + node.sysctl_size = sizeof(igmps); + return (sysctl_lookup(SYSCTLFN_CALL(&node))); +} + +SYSCTL_SETUP(sysctl_net_inet_igmp_setup, "sysctl net.inet.igmp subtree setup") +{ + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "net", NULL, + NULL, 0, NULL, 0, + CTL_NET, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "inet", NULL, + NULL, 0, NULL, 0, + CTL_NET, PF_INET, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "igmp", + SYSCTL_DESCR("Internet Group Management Protocol"), + NULL, 0, NULL, 0, + CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_STRUCT, "stats", + SYSCTL_DESCR("IGMP statistics"), + sysctl_net_inet_igmp_stats, 0, NULL, 0, + CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); +} diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h index a090155b61e8..e3fb40e2166d 100644 --- a/sys/netinet/igmp_var.h +++ b/sys/netinet/igmp_var.h @@ -1,4 +1,4 @@ -/* $NetBSD: igmp_var.h,v 1.22 2005/12/10 23:36:23 elad Exp $ */ +/* $NetBSD: igmp_var.h,v 1.23 2008/04/15 16:02:03 thorpej Exp $ */ /* * Copyright (c) 1992, 1993 @@ -85,21 +85,19 @@ * MULTICAST 1.3 */ -struct igmpstat { - u_quad_t igps_rcv_total; /* total IGMP messages received */ - u_quad_t igps_rcv_tooshort; /* received with too few bytes */ - u_quad_t igps_rcv_badsum; /* received with bad checksum */ - u_quad_t igps_rcv_queries; /* received membership queries */ - u_quad_t igps_rcv_badqueries; /* received invalid queries */ - u_quad_t igps_rcv_reports; /* received membership reports */ - u_quad_t igps_rcv_badreports; /* received invalid reports */ - u_quad_t igps_rcv_ourreports; /* received reports for our groups */ - u_quad_t igps_snd_reports; /* sent membership reports */ -}; +#define IGMP_STAT_RCV_TOTAL 1 /* total IGMP messages received */ +#define IGMP_STAT_RCV_TOOSHORT 2 /* received with too few bytes */ +#define IGMP_STAT_RCV_BADSUM 3 /* received with bad checksum */ +#define IGMP_STAT_RCV_QUERIES 4 /* received membership queries */ +#define IGMP_STAT_RCV_BADQUERIES 5 /* received invalid queries */ +#define IGMP_STAT_RCV_REPORTS 6 /* received membership reports */ +#define IGMP_STAT_RCV_BADREPORTS 7 /* received invalid reports */ +#define IGMP_STAT_RCV_OURREPORTS 8 /* received reports for our groups */ +#define IGMP_STAT_SND_REPORTS 9 /* sent membership reports */ + +#define IGMP_NSTATS 10 #ifdef _KERNEL -extern struct igmpstat igmpstat; - /* * Macro to compute a random timer value between 1 and (IGMP_MAX_REPORTING_ * DELAY * countdown frequency). We assume that the routine random() @@ -113,12 +111,13 @@ extern struct igmpstat igmpstat; #define IGMP_HDR_ALIGNED_P(ig) ((((vaddr_t) (ig)) & 3) == 0) #endif +void igmp_init(void); void igmp_input(struct mbuf *, ...); int igmp_joingroup(struct in_multi *); void igmp_leavegroup(struct in_multi *); void igmp_fasttimo(void); void igmp_slowtimo(void); void igmp_purgeif(struct ifnet *); -#endif +#endif /* _KERNEL */ #endif /* !_NETINET_IGMP_VAR_H_ */ diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index 9dbc0edf8bcc..0958a667ed73 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -1,4 +1,4 @@ -/* $NetBSD: in_proto.c,v 1.91 2007/10/05 03:28:13 dyoung Exp $ */ +/* $NetBSD: in_proto.c,v 1.92 2008/04/15 16:02:03 thorpej Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -61,7 +61,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: in_proto.c,v 1.91 2007/10/05 03:28:13 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: in_proto.c,v 1.92 2008/04/15 16:02:03 thorpej Exp $"); #include "opt_mrouting.h" #include "opt_eon.h" /* ISO CLNL over IP */ @@ -310,6 +310,7 @@ const struct protosw inetsw[] = { .pr_usrreq = rip_usrreq, .pr_fasttimo = igmp_fasttimo, .pr_slowtimo = igmp_slowtimo, + .pr_init = igmp_init, }, #ifdef PIM { .pr_type = SOCK_RAW, diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index b98ab16c133d..bc3c613fdc49 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -1,4 +1,4 @@ -/* $NetBSD: inet.c,v 1.86 2008/04/15 15:17:54 thorpej Exp $ */ +/* $NetBSD: inet.c,v 1.87 2008/04/15 16:02:04 thorpej Exp $ */ /* * Copyright (c) 1983, 1988, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "from: @(#)inet.c 8.4 (Berkeley) 4/20/94"; #else -__RCSID("$NetBSD: inet.c,v 1.86 2008/04/15 15:17:54 thorpej Exp $"); +__RCSID("$NetBSD: inet.c,v 1.87 2008/04/15 16:02:04 thorpej Exp $"); #endif #endif /* not lint */ @@ -601,26 +601,35 @@ icmp_stats(u_long off, char *name) void igmp_stats(u_long off, char *name) { - struct igmpstat igmpstat; + uint64_t igmpstat[IGMP_NSTATS]; + + if (use_sysctl) { + size_t size = sizeof(igmpstat); + + if (sysctlbyname("net.inet.igmp.stats", igmpstat, &size, + NULL, 0) == -1) + err(1, "net.inet.igmp.stats"); + } else { + if (off == 0) + return; + kread(off, (char *)igmpstat, sizeof (igmpstat)); + } - if (off == 0) - return; - kread(off, (char *)&igmpstat, sizeof (igmpstat)); printf("%s:\n", name); -#define p(f, m) if (igmpstat.f || sflag <= 1) \ - printf(m, (unsigned long long)igmpstat.f, plural(igmpstat.f)) -#define py(f, m) if (igmpstat.f || sflag <= 1) \ - printf(m, (unsigned long long)igmpstat.f, igmpstat.f != 1 ? "ies" : "y") - p(igps_rcv_total, "\t%llu message%s received\n"); - p(igps_rcv_tooshort, "\t%llu message%s received with too few bytes\n"); - p(igps_rcv_badsum, "\t%llu message%s received with bad checksum\n"); - py(igps_rcv_queries, "\t%llu membership quer%s received\n"); - py(igps_rcv_badqueries, "\t%llu membership quer%s received with invalid field(s)\n"); - p(igps_rcv_reports, "\t%llu membership report%s received\n"); - p(igps_rcv_badreports, "\t%llu membership report%s received with invalid field(s)\n"); - p(igps_rcv_ourreports, "\t%llu membership report%s received for groups to which we belong\n"); - p(igps_snd_reports, "\t%llu membership report%s sent\n"); +#define p(f, m) if (igmpstat[f] || sflag <= 1) \ + printf(m, (unsigned long long)igmpstat[f], plural(igmpstat[f])) +#define py(f, m) if (igmpstat[f] || sflag <= 1) \ + printf(m, (unsigned long long)igmpstat[f], igmpstat[f] != 1 ? "ies" : "y") + p(IGMP_STAT_RCV_TOTAL, "\t%llu message%s received\n"); + p(IGMP_STAT_RCV_TOOSHORT, "\t%llu message%s received with too few bytes\n"); + p(IGMP_STAT_RCV_BADSUM, "\t%llu message%s received with bad checksum\n"); + py(IGMP_STAT_RCV_QUERIES, "\t%llu membership quer%s received\n"); + py(IGMP_STAT_RCV_BADQUERIES, "\t%llu membership quer%s received with invalid field(s)\n"); + p(IGMP_STAT_RCV_REPORTS, "\t%llu membership report%s received\n"); + p(IGMP_STAT_RCV_BADREPORTS, "\t%llu membership report%s received with invalid field(s)\n"); + p(IGMP_STAT_RCV_OURREPORTS, "\t%llu membership report%s received for groups to which we belong\n"); + p(IGMP_STAT_SND_REPORTS, "\t%llu membership report%s sent\n"); #undef p #undef py }