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;
 }