Index: include/uapi/linux/ethtool.h =================================================================== --- include/uapi/linux/ethtool.h +++ linux-imx/include/uapi/linux/ethtool.h @@ -1377,6 +1377,7 @@ enum ethtool_fec_config_bits { #define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ #define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ #define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ +#define ETHTOOL_CSTATS 0x00000052 /* Clear statistics */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET Index: net/core/ethtool.c =================================================================== --- net/core/ethtool.c +++ linux-imx/net/core/ethtool.c @@ -1938,6 +1938,15 @@ static int ethtool_get_stats(struct net_ return ret; } +static int ethtool_clear_stats(struct net_device *dev) +{ + if (!dev->ethtool_ops->clear_ethtool_stats) + return -EOPNOTSUPP; + + dev->ethtool_ops->clear_ethtool_stats(dev); + return 0; +} + static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) { struct ethtool_stats stats; @@ -2577,6 +2586,7 @@ int dev_ethtool(struct net *net, struct case ETHTOOL_GSSET_INFO: case ETHTOOL_GSTRINGS: case ETHTOOL_GSTATS: + case ETHTOOL_CSTATS: case ETHTOOL_GPHYSTATS: case ETHTOOL_GTSO: case ETHTOOL_GPERMADDR: @@ -2688,6 +2698,9 @@ int dev_ethtool(struct net *net, struct case ETHTOOL_GSTATS: rc = ethtool_get_stats(dev, useraddr); break; + case ETHTOOL_CSTATS: + rc = ethtool_clear_stats(dev); + break; case ETHTOOL_GPERMADDR: rc = ethtool_get_perm_addr(dev, useraddr); break; Index: include/linux/ethtool.h =================================================================== --- include/linux/ethtool.h +++ linux-imx/include/linux/ethtool.h @@ -347,6 +347,7 @@ struct ethtool_ops { int (*set_phys_id)(struct net_device *, enum ethtool_phys_id_state); void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); + void (*clear_ethtool_stats)(struct net_device *); int (*begin)(struct net_device *); void (*complete)(struct net_device *); u32 (*get_priv_flags)(struct net_device *); Index: net/dsa/slave.c =================================================================== --- net/dsa/slave.c +++ linux-imx/net/dsa/slave.c @@ -1019,6 +1019,30 @@ static int dsa_slave_set_rxnfc(struct ne return ds->ops->set_rxnfc(ds, p->dp->index, nfc); } +static void dsa_slave_clear_ethtool_stats(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->dp->ds; + struct pcpu_sw_netstats *s; + unsigned int start; + int i; + + memset(&dev->stats, 0, sizeof(struct net_device_stats)); + for_each_possible_cpu(i) { + s = per_cpu_ptr(p->stats64, i); + do { + start = u64_stats_fetch_begin_irq(&s->syncp); + s->tx_packets = 0L; + s->tx_bytes = 0L; + s->rx_packets = 0L; + s->rx_bytes = 0L; + } while (u64_stats_fetch_retry_irq(&s->syncp, start)); + } + + if (ds->ops->clear_ethtool_stats) + ds->ops->clear_ethtool_stats(ds, p->dp->index); +} + static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_drvinfo = dsa_slave_get_drvinfo, .get_regs_len = dsa_slave_get_regs_len, @@ -1039,6 +1063,7 @@ static const struct ethtool_ops dsa_slav .set_link_ksettings = dsa_slave_set_link_ksettings, .get_rxnfc = dsa_slave_get_rxnfc, .set_rxnfc = dsa_slave_set_rxnfc, + .clear_ethtool_stats = dsa_slave_clear_ethtool_stats, }; static const struct net_device_ops dsa_slave_netdev_ops = { Index: drivers/net/dsa/mv88e6xxx/chip.h =================================================================== --- drivers/net/dsa/mv88e6xxx/chip.h +++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.h @@ -330,6 +330,7 @@ struct mv88e6xxx_ops { void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, uint64_t *data); + void (*stats_clear_stats)(struct mv88e6xxx_chip *chip, int port); int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port); int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port); const struct mv88e6xxx_irq_ops *watchdog_ops; Index: include/net/dsa.h =================================================================== --- include/net/dsa.h +++ linux-imx/include/net/dsa.h @@ -320,6 +320,7 @@ struct dsa_switch_ops { void (*get_ethtool_stats)(struct dsa_switch *ds, int port, uint64_t *data); int (*get_sset_count)(struct dsa_switch *ds); + void (*clear_ethtool_stats)(struct dsa_switch *ds, int port); /* * ethtool Wake-on-LAN Index: drivers/net/dsa/mv88e6xxx/chip.c =================================================================== --- drivers/net/dsa/mv88e6xxx/chip.c +++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.c @@ -648,6 +648,46 @@ static void mv88e6xxx_get_ethtool_stats( mutex_unlock(&chip->reg_lock); } +static void mv88e6xxx_stats_clear_stats(struct mv88e6xxx_chip *chip, int port) +{ + u16 histogram = 0; + int err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STATS_OP, &histogram); + if (err) + return; + + histogram &= 0xC00; + /* Clear the statistics counters for given port */ + err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_STATS_OP, + MV88E6XXX_G1_STATS_OP_BUSY | + MV88E6XXX_G1_STATS_OP_FLUSH_PORT | + (port +1) << 5 | + histogram); +} + +static void mv88e6250_stats_clear_stats(struct mv88e6xxx_chip *chip, int port) +{ + mv88e6xxx_stats_clear_stats(chip, port); +} + +static void mv88e6xxx_clear_stats(struct mv88e6xxx_chip *chip, int port) +{ + if (chip->info->ops->stats_clear_stats) + chip->info->ops->stats_clear_stats(chip, port); +} + +static void mv88e6xxx_clear_ethtool_stats(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + mutex_lock(&chip->reg_lock); + + mv88e6xxx_clear_stats(chip, port); + + mutex_unlock(&chip->reg_lock); +} + static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip) { if (chip->info->ops->stats_set_histogram) @@ -2801,6 +2841,7 @@ static const struct mv88e6xxx_ops mv88e6 .stats_get_sset_count = mv88e6250_stats_get_sset_count, .stats_get_strings = mv88e6250_stats_get_strings, .stats_get_stats = mv88e6250_stats_get_stats, + .stats_clear_stats = mv88e6250_stats_clear_stats, .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6250_watchdog_ops, @@ -3837,6 +3878,7 @@ static const struct dsa_switch_ops mv88e .adjust_link = mv88e6xxx_adjust_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .clear_ethtool_stats = mv88e6xxx_clear_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, .port_enable = mv88e6xxx_port_enable, .port_disable = mv88e6xxx_port_disable, Index: net/8021q/vlan_dev.c =================================================================== --- net/8021q/vlan_dev.c +++ linux-imx/net/8021q/vlan_dev.c @@ -681,6 +681,26 @@ static int vlan_ethtool_get_ts_info(stru return 0; } +static void vlan_clear_ethtool_stats(struct net_device *dev) +{ + struct vlan_pcpu_stats *p; + unsigned int start; + int i; + + memset(&dev->stats, 0, sizeof(struct net_device_stats)); + for_each_possible_cpu(i) { + p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); + do { + start = u64_stats_fetch_begin_irq(&p->syncp); + p->rx_packets = 0L; + p->rx_bytes = 0L; + p->rx_multicast = 0L; + p->tx_packets = 0L; + p->tx_bytes = 0L; + } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + } +} + static void vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { @@ -771,6 +791,7 @@ static const struct ethtool_ops vlan_eth .get_drvinfo = vlan_ethtool_get_drvinfo, .get_link = ethtool_op_get_link, .get_ts_info = vlan_ethtool_get_ts_info, + .clear_ethtool_stats = vlan_clear_ethtool_stats, }; static const struct net_device_ops vlan_netdev_ops = {