Separate IPv6 ifstats into the ones that are hit on fast path and the ones that aren't. The ones that are not can be removed as needed using sysctls.
Signed-off-by: Stephen Suryaputra <ssuryae...@gmail.com> --- include/linux/ipv6.h | 3 + include/net/if_inet6.h | 3 +- include/net/ipv6.h | 28 ++- include/net/snmp.h | 22 +++ include/uapi/linux/ipv6.h | 3 + include/uapi/linux/snmp.h | 3 +- net/ipv6/addrconf.c | 380 +++++++++++++++++++++++++++++++++++--- net/ipv6/addrconf_core.c | 3 +- net/ipv6/proc.c | 57 +++++- 9 files changed, 462 insertions(+), 40 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 495e834c1367..c477960d57c2 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -74,6 +74,9 @@ struct ipv6_devconf { __u32 addr_gen_mode; __s32 disable_policy; __s32 ndisc_tclass; + __s32 extended_ipstats; + __s32 icmpstats; + __s32 icmpmsgstats; struct ctl_table_header *sysctl_header; }; diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index d7578cf49c3a..62757829a992 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -158,7 +158,8 @@ struct ifacaddr6 { struct ipv6_devstat { struct proc_dir_entry *proc_dir_entry; - DEFINE_SNMP_STAT(struct ipstats_mib, ipv6); + DEFINE_SNMP_STAT(struct ipstats_mib_device_fast, ipv6dev_fast); + DEFINE_SNMP_STAT_ATOMIC(struct ipstats_mib_device, ipv6dev); DEFINE_SNMP_STAT_ATOMIC(struct icmpv6_mib_device, icmpv6dev); DEFINE_SNMP_STAT_ATOMIC(struct icmpv6msg_mib_device, icmpv6msgdev); }; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index ff33f498c137..4064d88d7b9d 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -166,8 +166,12 @@ extern int sysctl_mld_qrv; #define _DEVINC(net, statname, mod, idev, field) \ ({ \ struct inet6_dev *_idev = (idev); \ - if (likely(_idev != NULL)) \ - mod##SNMP_INC_STATS64((_idev)->stats.statname, (field));\ + if (likely(_idev != NULL)) { \ + if (field < __IPSTATS_MIB_FAST_MAX) \ + mod##SNMP_INC_STATS64((_idev)->stats.statname##dev_fast, (field)); \ + else if (likely((_idev)->stats.statname##dev != NULL)) \ + SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field)); \ + } \ mod##SNMP_INC_STATS64((net)->mib.statname##_statistics, (field));\ }) @@ -175,7 +179,7 @@ extern int sysctl_mld_qrv; #define _DEVINCATOMIC(net, statname, mod, idev, field) \ ({ \ struct inet6_dev *_idev = (idev); \ - if (likely(_idev != NULL)) \ + if (likely(_idev != NULL && (_idev)->stats.statname##dev != NULL)) \ SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field)); \ mod##SNMP_INC_STATS((net)->mib.statname##_statistics, (field));\ }) @@ -184,7 +188,7 @@ extern int sysctl_mld_qrv; #define _DEVINC_ATOMIC_ATOMIC(net, statname, idev, field) \ ({ \ struct inet6_dev *_idev = (idev); \ - if (likely(_idev != NULL)) \ + if (likely(_idev != NULL && (_idev)->stats.statname##dev != NULL)) \ SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field)); \ SNMP_INC_STATS_ATOMIC_LONG((net)->mib.statname##_statistics, (field));\ }) @@ -192,16 +196,24 @@ extern int sysctl_mld_qrv; #define _DEVADD(net, statname, mod, idev, field, val) \ ({ \ struct inet6_dev *_idev = (idev); \ - if (likely(_idev != NULL)) \ - mod##SNMP_ADD_STATS((_idev)->stats.statname, (field), (val)); \ + if (likely(_idev != NULL)) { \ + if (field < __IPSTATS_MIB_FAST_MAX) \ + mod##SNMP_ADD_STATS((_idev)->stats.statname##dev_fast, (field), (val)); \ + else if (likely((_idev)->stats.statname##dev != NULL)) \ + SNMP_ADD_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field), (val)); \ + } \ mod##SNMP_ADD_STATS((net)->mib.statname##_statistics, (field), (val));\ }) #define _DEVUPD(net, statname, mod, idev, field, val) \ ({ \ struct inet6_dev *_idev = (idev); \ - if (likely(_idev != NULL)) \ - mod##SNMP_UPD_PO_STATS((_idev)->stats.statname, field, (val)); \ + if (likely(_idev != NULL)) { \ + if (field##PKTS < __IPSTATS_MIB_FAST_MAX) \ + mod##SNMP_UPD_PO_STATS((_idev)->stats.statname##dev_fast, field, (val)); \ + else if (likely((_idev)->stats.statname##dev != NULL)) \ + SNMP_UPD_PO_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, field, (val)); \ + } \ mod##SNMP_UPD_PO_STATS((net)->mib.statname##_statistics, field, (val));\ }) diff --git a/include/net/snmp.h b/include/net/snmp.h index c9228ad7ee91..0b85ccdc493d 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -53,12 +53,25 @@ struct snmp_mib { /* IPstats */ #define IPSTATS_MIB_MAX __IPSTATS_MIB_MAX +#define IPSTATS_MIB_FAST_MAX __IPSTATS_MIB_FAST_MAX struct ipstats_mib { /* mibs[] must be first field of struct ipstats_mib */ u64 mibs[IPSTATS_MIB_MAX]; struct u64_stats_sync syncp; }; +/* Fast per device IPstats */ +struct ipstats_mib_device_fast { + /* mibs[] must be first field of struct ipstats_mib_device_fast */ + u64 mibs[IPSTATS_MIB_FAST_MAX]; + struct u64_stats_sync syncp; +}; + +/* Slow per device IPstats */ +struct ipstats_mib_device { + atomic_long_t mibs[IPSTATS_MIB_MAX]; +}; + /* ICMP */ #define ICMP_MIB_MAX __ICMP_MIB_MAX struct icmp_mib { @@ -140,6 +153,10 @@ struct linux_xfrm_mib { #define SNMP_ADD_STATS(mib, field, addend) \ this_cpu_add(mib->mibs[field], addend) + +#define SNMP_ADD_STATS_ATOMIC_LONG(mib, field, addend) \ + atomic_long_add(addend, &mib->mibs[field]) + #define SNMP_UPD_PO_STATS(mib, basefield, addend) \ do { \ __typeof__((mib->mibs) + 0) ptr = mib->mibs; \ @@ -152,6 +169,11 @@ struct linux_xfrm_mib { __this_cpu_inc(ptr[basefield##PKTS]); \ __this_cpu_add(ptr[basefield##OCTETS], addend); \ } while (0) +#define SNMP_UPD_PO_STATS_ATOMIC_LONG(mib, basefield, addend) \ + do { \ + atomic_long_inc(&mib->mibs[basefield##PKTS]); \ + atomic_long_add(addend, &mib->mibs[basefield##OCTETS]); \ + } while (0) #if BITS_PER_LONG==32 diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 9c0f4a92bcff..5864f4c8afbd 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -187,6 +187,9 @@ enum { DEVCONF_DISABLE_POLICY, DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_NDISC_TCLASS, + DEVCONF_EXTENDED_IPSTATS, + DEVCONF_ICMPSTATS, + DEVCONF_ICMPMSGSTATS, DEVCONF_MAX }; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index f80135e5feaa..eb689ecf21a6 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -26,8 +26,9 @@ enum IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ IPSTATS_MIB_OUTPKTS, /* OutRequests */ IPSTATS_MIB_OUTOCTETS, /* OutOctets */ + __IPSTATS_MIB_FAST_MAX, /* other fields */ - IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */ + IPSTATS_MIB_INHDRERRORS = __IPSTATS_MIB_FAST_MAX, /* InHdrErrors */ IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */ IPSTATS_MIB_INNOROUTES, /* InNoRoutes */ IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */ diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index a9a317322388..d8c15c713224 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -239,6 +239,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, .disable_policy = 0, + .extended_ipstats = 1, + .icmpstats = 1, + .icmpmsgstats = 1, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -293,6 +296,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, .disable_policy = 0, + .extended_ipstats = 1, + .icmpstats = 1, + .icmpmsgstats = 1, }; /* Check if link is ready: is it up and is a valid qdisc available */ @@ -333,33 +339,45 @@ static int snmp6_alloc_dev(struct inet6_dev *idev) { int i; - idev->stats.ipv6 = alloc_percpu(struct ipstats_mib); - if (!idev->stats.ipv6) - goto err_ip; + idev->stats.ipv6dev_fast = alloc_percpu(struct ipstats_mib_device_fast); + if (!idev->stats.ipv6dev_fast) + goto err_ip_fast; for_each_possible_cpu(i) { - struct ipstats_mib *addrconf_stats; - addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i); + struct ipstats_mib_device_fast *addrconf_stats; + addrconf_stats = per_cpu_ptr(idev->stats.ipv6dev_fast, i); u64_stats_init(&addrconf_stats->syncp); } - idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), - GFP_KERNEL); - if (!idev->stats.icmpv6dev) - goto err_icmp; - idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), - GFP_KERNEL); - if (!idev->stats.icmpv6msgdev) - goto err_icmpmsg; + if (idev->cnf.extended_ipstats) { + idev->stats.ipv6dev = kzalloc(sizeof(struct ipstats_mib_device), + GFP_KERNEL); + if (!idev->stats.ipv6dev) + goto err_ip; + } + if (idev->cnf.icmpstats) { + idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6dev) + goto err_icmp; + } + if (idev->cnf.icmpmsgstats) { + idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6msgdev) + goto err_icmpmsg; + } return 0; err_icmpmsg: kfree(idev->stats.icmpv6dev); err_icmp: - free_percpu(idev->stats.ipv6); + kfree(idev->stats.ipv6dev); err_ip: + free_percpu(idev->stats.ipv6dev_fast); +err_ip_fast: return -ENOMEM; } @@ -5263,6 +5281,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; + array[DEVCONF_EXTENDED_IPSTATS] = cnf->extended_ipstats; + array[DEVCONF_ICMPSTATS] = cnf->icmpstats; + array[DEVCONF_ICMPMSGSTATS] = cnf->icmpmsgstats; } static inline size_t inet6_ifla6_size(void) @@ -5297,14 +5318,16 @@ static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, /* Use put_unaligned() because stats may not be aligned for u64. */ put_unaligned(ICMP6_MIB_MAX, &stats[0]); - for (i = 1; i < ICMP6_MIB_MAX; i++) - put_unaligned(atomic_long_read(&mib[i]), &stats[i]); + if (mib) { + for (i = 1; i < ICMP6_MIB_MAX; i++) + put_unaligned(atomic_long_read(&mib[i]), &stats[i]); + } memset(&stats[ICMP6_MIB_MAX], 0, pad); } -static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib, - int bytes, size_t syncpoff) +static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib_fast, + atomic_long_t *mib, int bytes, size_t syncpoff) { int i, c; u64 buff[IPSTATS_MIB_MAX]; @@ -5316,10 +5339,13 @@ static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib, buff[0] = IPSTATS_MIB_MAX; for_each_possible_cpu(c) { - for (i = 1; i < IPSTATS_MIB_MAX; i++) - buff[i] += snmp_get_cpu_field64(mib, c, i, syncpoff); + for (i = 1; i < IPSTATS_MIB_FAST_MAX; i++) + buff[i] += snmp_get_cpu_field64(mib_fast, c, i, syncpoff); + } + if (mib) { + for (; i < IPSTATS_MIB_MAX; i++) + buff[i] = atomic_long_read(&mib[i]); } - memcpy(stats, buff, IPSTATS_MIB_MAX * sizeof(u64)); memset(&stats[IPSTATS_MIB_MAX], 0, pad); } @@ -5329,11 +5355,14 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, { switch (attrtype) { case IFLA_INET6_STATS: - __snmp6_fill_stats64(stats, idev->stats.ipv6, bytes, - offsetof(struct ipstats_mib, syncp)); + __snmp6_fill_stats64(stats, idev->stats.ipv6dev_fast, + idev->stats.ipv6dev ? idev->stats.ipv6dev->mibs : NULL, + bytes, offsetof(struct ipstats_mib_device_fast, syncp)); break; case IFLA_INET6_ICMP6STATS: - __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, bytes); + __snmp6_fill_statsdev(stats, + idev->stats.icmpv6dev ? idev->stats.icmpv6dev->mibs : NULL, + bytes); break; } } @@ -6205,6 +6234,288 @@ int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write, return ret; } +static +void free_ipv6dev_rcu(struct rcu_head *head) +{ + struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu); + + kfree(idev->stats.ipv6dev); + idev->stats.ipv6dev = NULL; +} + +static +int addrconf_extended_ipstats(struct ctl_table *ctl, int *valp, int val) +{ + struct inet6_dev *idev; + struct net *net; + + if (!rtnl_trylock()) + return restart_syscall(); + + net = (struct net *)ctl->extra2; + if (valp == &net->ipv6.devconf_dflt->extended_ipstats) { + *valp = val; + rtnl_unlock(); + return 0; + } + + if (valp == &net->ipv6.devconf_all->extended_ipstats) { + struct net_device *dev; + bool undo = 0; + +loop: + for_each_netdev(net, dev) { + idev = __in6_dev_get(dev); + if (!idev) + continue; + if (val && !idev->stats.ipv6dev) { + idev->stats.ipv6dev = kzalloc(sizeof(struct ipstats_mib_device), + GFP_KERNEL); + if (!idev->stats.ipv6dev) { + undo = 1; + val = 0; + goto loop; + } + } else if (!val && idev->stats.ipv6dev) { + call_rcu(&idev->rcu, free_ipv6dev_rcu); + } + } + if (undo) { + rtnl_unlock(); + return -ENOMEM; + } + } else { + idev = (struct inet6_dev *)ctl->extra1; + if (val && !idev->stats.ipv6dev) { + idev->stats.ipv6dev = kzalloc(sizeof(struct ipstats_mib_device), + GFP_KERNEL); + if (!idev->stats.ipv6dev) { + rtnl_unlock(); + return -ENOMEM; + } + } else if (!val && !idev->stats.ipv6dev) { + call_rcu(&idev->rcu, free_ipv6dev_rcu); + } + } + + *valp = val; + + rtnl_unlock(); + return 0; +} + +static +int addrconf_sysctl_extended_ipstats(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int *valp = ctl->data; + int val = *valp; + loff_t pos = *ppos; + struct ctl_table lctl; + int ret; + + lctl = *ctl; + lctl.data = &val; + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + + if (write && (*valp != val)) + ret = addrconf_extended_ipstats(ctl, valp, val); + + if (ret) + *ppos = pos; + + return ret; +} + +static +void free_icmpv6dev_rcu(struct rcu_head *head) +{ + struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu); + + kfree(idev->stats.icmpv6dev); + idev->stats.icmpv6dev = NULL; +} + +static +int addrconf_icmpstats(struct ctl_table *ctl, int *valp, int val) +{ + struct inet6_dev *idev; + struct net *net; + + if (!rtnl_trylock()) + return restart_syscall(); + + net = (struct net *)ctl->extra2; + if (valp == &net->ipv6.devconf_dflt->icmpstats) { + *valp = val; + rtnl_unlock(); + return 0; + } + + if (valp == &net->ipv6.devconf_all->icmpstats) { + struct net_device *dev; + bool undo = 0; + +loop: + for_each_netdev(net, dev) { + idev = __in6_dev_get(dev); + if (!idev) + continue; + if (val && !idev->stats.icmpv6dev) { + idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6dev) { + undo = 1; + val = 0; + goto loop; + } + } else if (!val && idev->stats.icmpv6dev) { + call_rcu(&idev->rcu, free_icmpv6dev_rcu); + } + } + if (undo) { + rtnl_unlock(); + return -ENOMEM; + } + } else { + idev = (struct inet6_dev *)ctl->extra1; + if (val && !idev->stats.icmpv6dev) { + idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6dev) { + rtnl_unlock(); + return -ENOMEM; + } + } else if (!val && idev->stats.icmpv6dev) { + call_rcu(&idev->rcu, free_icmpv6dev_rcu); + } + } + + *valp = val; + + rtnl_unlock(); + return 0; +} + +static +int addrconf_sysctl_icmpstats(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int *valp = ctl->data; + int val = *valp; + loff_t pos = *ppos; + struct ctl_table lctl; + int ret; + + lctl = *ctl; + lctl.data = &val; + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + + if (write && (*valp != val)) + ret = addrconf_icmpstats(ctl, valp, val); + + if (ret) + *ppos = pos; + + return ret; +} + +static +void free_icmpv6msgdev_rcu(struct rcu_head *head) +{ + struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu); + + kfree(idev->stats.icmpv6msgdev); + idev->stats.icmpv6msgdev = NULL; +} + +static +int addrconf_icmpmsgstats(struct ctl_table *ctl, int *valp, int val) +{ + struct inet6_dev *idev; + struct net *net; + + if (!rtnl_trylock()) + return restart_syscall(); + + net = (struct net *)ctl->extra2; + if (valp == &net->ipv6.devconf_dflt->icmpmsgstats) { + *valp = val; + rtnl_unlock(); + return 0; + } + + if (valp == &net->ipv6.devconf_all->icmpmsgstats) { + struct net_device *dev; + bool undo = 0; + +loop: + for_each_netdev(net, dev) { + idev = __in6_dev_get(dev); + if (!idev) + continue; + if (val && !idev->stats.icmpv6msgdev) { + idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6msgdev) { + undo = 1; + val = 0; + goto loop; + } + } else if (!val && idev->stats.icmpv6msgdev) { + call_rcu(&idev->rcu, free_icmpv6msgdev_rcu); + } + } + if (undo) { + rtnl_unlock(); + return -ENOMEM; + } + } else { + idev = (struct inet6_dev *)ctl->extra1; + if (val && !idev->stats.icmpv6msgdev) { + idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6msgdev) { + rtnl_unlock(); + return -ENOMEM; + } + } else if (!val && idev->stats.icmpv6msgdev) { + call_rcu(&idev->rcu, free_icmpv6msgdev_rcu); + } + } + + *valp = val; + + rtnl_unlock(); + return 0; +} + +static +int addrconf_sysctl_icmpmsgstats(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int *valp = ctl->data; + int val = *valp; + loff_t pos = *ppos; + struct ctl_table lctl; + int ret; + + lctl = *ctl; + lctl.data = &val; + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + + if (write && (*valp != val)) + ret = addrconf_icmpmsgstats(ctl, valp, val); + + if (ret) + *ppos = pos; + + return ret; +} + static int minus_one = -1; static const int zero = 0; static const int one = 1; @@ -6586,6 +6897,27 @@ static const struct ctl_table addrconf_sysctl[] = { .extra1 = (void *)&zero, .extra2 = (void *)&two_five_five, }, + { + .procname = "extended_ipstats", + .data = &ipv6_devconf.extended_ipstats, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = addrconf_sysctl_extended_ipstats, + }, + { + .procname = "icmpstats", + .data = &ipv6_devconf.icmpstats, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = addrconf_sysctl_icmpstats, + }, + { + .procname = "icmpmsgstats", + .data = &ipv6_devconf.icmpmsgstats, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = addrconf_sysctl_icmpmsgstats, + }, { /* sentinel */ } diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index 5cd0029d930e..f143d7e2264c 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -198,7 +198,8 @@ static void snmp6_free_dev(struct inet6_dev *idev) { kfree(idev->stats.icmpv6msgdev); kfree(idev->stats.icmpv6dev); - free_percpu(idev->stats.ipv6); + kfree(idev->stats.ipv6dev); + free_percpu(idev->stats.ipv6dev_fast); } static void in6_dev_finish_destroy_rcu(struct rcu_head *head) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 2356b4af7309..c641c05af1b3 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -91,6 +91,47 @@ static const struct snmp_mib snmp6_ipstats_list[] = { SNMP_MIB_SENTINEL }; +static const struct snmp_mib snmp6_ipstats_device_fast_list[] = { + SNMP_MIB_ITEM("Ip6InReceives", IPSTATS_MIB_INPKTS), + SNMP_MIB_ITEM("Ip6InOctets", IPSTATS_MIB_INOCTETS), + SNMP_MIB_ITEM("Ip6InDelivers", IPSTATS_MIB_INDELIVERS), + SNMP_MIB_ITEM("Ip6OutForwDatagrams", IPSTATS_MIB_OUTFORWDATAGRAMS), + SNMP_MIB_ITEM("Ip6OutRequests", IPSTATS_MIB_OUTPKTS), + SNMP_MIB_ITEM("Ip6OutOctets", IPSTATS_MIB_OUTOCTETS), + SNMP_MIB_SENTINEL +}; + +static const struct snmp_mib snmp6_ipstats_device_list[] = { + SNMP_MIB_ITEM("Ip6InHdrErrors", IPSTATS_MIB_INHDRERRORS), + SNMP_MIB_ITEM("Ip6InTooBigErrors", IPSTATS_MIB_INTOOBIGERRORS), + SNMP_MIB_ITEM("Ip6InNoRoutes", IPSTATS_MIB_INNOROUTES), + SNMP_MIB_ITEM("Ip6InAddrErrors", IPSTATS_MIB_INADDRERRORS), + SNMP_MIB_ITEM("Ip6InUnknownProtos", IPSTATS_MIB_INUNKNOWNPROTOS), + SNMP_MIB_ITEM("Ip6InTruncatedPkts", IPSTATS_MIB_INTRUNCATEDPKTS), + SNMP_MIB_ITEM("Ip6InDiscards", IPSTATS_MIB_INDISCARDS), + SNMP_MIB_ITEM("Ip6OutDiscards", IPSTATS_MIB_OUTDISCARDS), + SNMP_MIB_ITEM("Ip6OutNoRoutes", IPSTATS_MIB_OUTNOROUTES), + SNMP_MIB_ITEM("Ip6ReasmTimeout", IPSTATS_MIB_REASMTIMEOUT), + SNMP_MIB_ITEM("Ip6ReasmReqds", IPSTATS_MIB_REASMREQDS), + SNMP_MIB_ITEM("Ip6ReasmOKs", IPSTATS_MIB_REASMOKS), + SNMP_MIB_ITEM("Ip6ReasmFails", IPSTATS_MIB_REASMFAILS), + SNMP_MIB_ITEM("Ip6FragOKs", IPSTATS_MIB_FRAGOKS), + SNMP_MIB_ITEM("Ip6FragFails", IPSTATS_MIB_FRAGFAILS), + SNMP_MIB_ITEM("Ip6FragCreates", IPSTATS_MIB_FRAGCREATES), + SNMP_MIB_ITEM("Ip6InMcastPkts", IPSTATS_MIB_INMCASTPKTS), + SNMP_MIB_ITEM("Ip6OutMcastPkts", IPSTATS_MIB_OUTMCASTPKTS), + SNMP_MIB_ITEM("Ip6InMcastOctets", IPSTATS_MIB_INMCASTOCTETS), + SNMP_MIB_ITEM("Ip6OutMcastOctets", IPSTATS_MIB_OUTMCASTOCTETS), + SNMP_MIB_ITEM("Ip6InBcastOctets", IPSTATS_MIB_INBCASTOCTETS), + SNMP_MIB_ITEM("Ip6OutBcastOctets", IPSTATS_MIB_OUTBCASTOCTETS), + /* IPSTATS_MIB_CSUMERRORS is not relevant in IPv6 (no checksum) */ + SNMP_MIB_ITEM("Ip6InNoECTPkts", IPSTATS_MIB_NOECTPKTS), + SNMP_MIB_ITEM("Ip6InECT1Pkts", IPSTATS_MIB_ECT1PKTS), + SNMP_MIB_ITEM("Ip6InECT0Pkts", IPSTATS_MIB_ECT0PKTS), + SNMP_MIB_ITEM("Ip6InCEPkts", IPSTATS_MIB_CEPKTS), + SNMP_MIB_SENTINEL +}; + static const struct snmp_mib snmp6_icmp6_list[] = { /* icmpv6 mib according to RFC 2466 */ SNMP_MIB_ITEM("Icmp6InMsgs", ICMP6_MIB_INMSGS), @@ -235,11 +276,17 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v) struct inet6_dev *idev = (struct inet6_dev *)seq->private; seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); - snmp6_seq_show_item64(seq, idev->stats.ipv6, - snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); - snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, - snmp6_icmp6_list); - snmp6_seq_show_icmpv6msg(seq, idev->stats.icmpv6msgdev->mibs); + snmp6_seq_show_item64(seq, idev->stats.ipv6dev_fast, + snmp6_ipstats_device_fast_list, + offsetof(struct ipstats_mib_device_fast, syncp)); + if (idev->stats.ipv6dev) + snmp6_seq_show_item(seq, NULL, idev->stats.ipv6dev->mibs, + snmp6_ipstats_device_list); + if (idev->stats.icmpv6dev) + snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, + snmp6_icmp6_list); + if (idev->stats.icmpv6msgdev) + snmp6_seq_show_icmpv6msg(seq, idev->stats.icmpv6msgdev->mibs); return 0; } -- 2.17.1