@@ -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;
};
@@ -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);
};
@@ -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));\
})
@@ -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
@@ -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
};
@@ -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 */
@@ -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 */
}
@@ -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)
@@ -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;
}
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 <ssuryaextr@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(-)