struct ipv6_devstat {
struct proc_dir_entry *proc_dir_entry;
DEFINE_SNMP_STAT(struct ipstats_mib, ipv6);
- DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
- DEFINE_SNMP_STAT(struct icmpv6msg_mib, icmpv6msg);
+ DEFINE_SNMP_STAT_ATOMIC(struct icmpv6_mib_device, icmpv6dev);
+ DEFINE_SNMP_STAT_ATOMIC(struct icmpv6msg_mib_device, icmpv6msgdev);
};
struct inet6_dev {
SNMP_INC_STATS##modifier((net)->mib.statname##_statistics, (field));\
})
+/* per device counters are atomic_long_t */
+#define _DEVINCATOMIC(net, statname, modifier, idev, field) \
+({ \
+ struct inet6_dev *_idev = (idev); \
+ if (likely(_idev != NULL)) \
+ SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field)); \
+ SNMP_INC_STATS##modifier((net)->mib.statname##_statistics, (field));\
+})
+
#define _DEVADD(net, statname, modifier, idev, field, val) \
({ \
struct inet6_dev *_idev = (idev); \
#define IP6_UPD_PO_STATS_BH(net, idev,field,val) \
_DEVUPD(net, ipv6, 64_BH, idev, field, val)
#define ICMP6_INC_STATS(net, idev, field) \
- _DEVINC(net, icmpv6, , idev, field)
+ _DEVINCATOMIC(net, icmpv6, , idev, field)
#define ICMP6_INC_STATS_BH(net, idev, field) \
- _DEVINC(net, icmpv6, _BH, idev, field)
+ _DEVINCATOMIC(net, icmpv6, _BH, idev, field)
#define ICMP6MSGOUT_INC_STATS(net, idev, field) \
- _DEVINC(net, icmpv6msg, , idev, field +256)
+ _DEVINCATOMIC(net, icmpv6msg, , idev, field +256)
#define ICMP6MSGOUT_INC_STATS_BH(net, idev, field) \
- _DEVINC(net, icmpv6msg, _BH, idev, field +256)
+ _DEVINCATOMIC(net, icmpv6msg, _BH, idev, field +256)
#define ICMP6MSGIN_INC_STATS_BH(net, idev, field) \
- _DEVINC(net, icmpv6msg, _BH, idev, field)
+ _DEVINCATOMIC(net, icmpv6msg, _BH, idev, field)
struct ip6_ra_chain {
struct ip6_ra_chain *next;
/* ICMP6 (IPv6-ICMP) */
#define ICMP6_MIB_MAX __ICMP6_MIB_MAX
+/* per network ns counters */
struct icmpv6_mib {
unsigned long mibs[ICMP6_MIB_MAX];
};
+/* per device counters, (shared on all cpus) */
+struct icmpv6_mib_device {
+ atomic_long_t mibs[ICMP6_MIB_MAX];
+};
#define ICMP6MSG_MIB_MAX __ICMP6MSG_MIB_MAX
+/* per network ns counters */
struct icmpv6msg_mib {
unsigned long mibs[ICMP6MSG_MIB_MAX];
};
+/* per device counters, (shared on all cpus) */
+struct icmpv6msg_mib_device {
+ atomic_long_t mibs[ICMP6MSG_MIB_MAX];
+};
/* TCP */
*/
#define DEFINE_SNMP_STAT(type, name) \
__typeof__(type) __percpu *name[2]
+#define DEFINE_SNMP_STAT_ATOMIC(type, name) \
+ __typeof__(type) *name
#define DECLARE_SNMP_STAT(type, name) \
extern __typeof__(type) __percpu *name[2]
__this_cpu_inc(mib[0]->mibs[field])
#define SNMP_INC_STATS_USER(mib, field) \
this_cpu_inc(mib[1]->mibs[field])
+#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \
+ atomic_long_inc(&mib->mibs[field])
#define SNMP_INC_STATS(mib, field) \
this_cpu_inc(mib[!in_softirq()]->mibs[field])
#define SNMP_DEC_STATS(mib, field) \
sizeof(struct ipstats_mib),
__alignof__(struct ipstats_mib)) < 0)
goto err_ip;
- if (snmp_mib_init((void __percpu **)idev->stats.icmpv6,
- sizeof(struct icmpv6_mib),
- __alignof__(struct icmpv6_mib)) < 0)
+ idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),
+ GFP_KERNEL);
+ if (!idev->stats.icmpv6dev)
goto err_icmp;
- if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg,
- sizeof(struct icmpv6msg_mib),
- __alignof__(struct icmpv6msg_mib)) < 0)
+ idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device),
+ GFP_KERNEL);
+ if (!idev->stats.icmpv6msgdev)
goto err_icmpmsg;
return 0;
err_icmpmsg:
- snmp_mib_free((void __percpu **)idev->stats.icmpv6);
+ kfree(idev->stats.icmpv6dev);
err_icmp:
snmp_mib_free((void __percpu **)idev->stats.ipv6);
err_ip:
static void snmp6_free_dev(struct inet6_dev *idev)
{
- snmp_mib_free((void __percpu **)idev->stats.icmpv6msg);
- snmp_mib_free((void __percpu **)idev->stats.icmpv6);
+ kfree(idev->stats.icmpv6msgdev);
+ kfree(idev->stats.icmpv6dev);
snmp_mib_free((void __percpu **)idev->stats.ipv6);
}
+ nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */
}
-static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib,
+static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,
int items, int bytes)
{
int i;
/* Use put_unaligned() because stats may not be aligned for u64. */
put_unaligned(items, &stats[0]);
for (i = 1; i < items; i++)
- put_unaligned(snmp_fold_field(mib, i), &stats[i]);
+ put_unaligned(atomic_long_read(&mib[i]), &stats[i]);
memset(&stats[items], 0, pad);
}
IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));
break;
case IFLA_INET6_ICMP6STATS:
- __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
+ __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, ICMP6_MIB_MAX, bytes);
break;
}
}
SNMP_MIB_SENTINEL
};
-static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib)
+/* can be called either with percpu mib (pcpumib != NULL),
+ * or shared one (smib != NULL)
+ */
+static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **pcpumib,
+ atomic_long_t *smib)
{
char name[32];
int i;
snprintf(name, sizeof(name), "Icmp6%s%s",
i & 0x100 ? "Out" : "In", p);
seq_printf(seq, "%-32s\t%lu\n", name,
- snmp_fold_field(mib, i));
+ pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i));
}
/* print by number (nonzero only) - ICMPMsgStat format */
for (i = 0; i < ICMP6MSG_MIB_MAX; i++) {
unsigned long val;
- val = snmp_fold_field(mib, i);
+ val = pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i);
if (!val)
continue;
snprintf(name, sizeof(name), "Icmp6%sType%u",
}
}
-static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **mib,
+/* can be called either with percpu mib (pcpumib != NULL),
+ * or shared one (smib != NULL)
+ */
+static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
+ atomic_long_t *smib,
const struct snmp_mib *itemlist)
{
int i;
+ unsigned long val;
- for (i = 0; itemlist[i].name; i++)
- seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
- snmp_fold_field(mib, itemlist[i].entry));
+ for (i = 0; itemlist[i].name; i++) {
+ val = pcpumib ?
+ snmp_fold_field(pcpumib, itemlist[i].entry) :
+ atomic_long_read(smib + itemlist[i].entry);
+ seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, val);
+ }
}
static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib,
snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics,
snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics,
- snmp6_icmp6_list);
+ NULL, snmp6_icmp6_list);
snmp6_seq_show_icmpv6msg(seq,
- (void __percpu **)net->mib.icmpv6msg_statistics);
+ (void __percpu **)net->mib.icmpv6msg_statistics, NULL);
snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6,
- snmp6_udp6_list);
+ NULL, snmp6_udp6_list);
snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6,
- snmp6_udplite6_list);
+ NULL, snmp6_udplite6_list);
return 0;
}
struct inet6_dev *idev = (struct inet6_dev *)seq->private;
seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
- snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6,
+ snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, NULL,
snmp6_ipstats_list);
- snmp6_seq_show_item(seq, (void __percpu **)idev->stats.icmpv6,
+ snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs,
snmp6_icmp6_list);
- snmp6_seq_show_icmpv6msg(seq, (void __percpu **)idev->stats.icmpv6msg);
+ snmp6_seq_show_icmpv6msg(seq, NULL, idev->stats.icmpv6msgdev->mibs);
return 0;
}