This patch adds support for clearing interface statistics using the ethtool interface adding a new 0x23 command. It adds a clear_stats function pointer to the net_device struct, and then impliments local functions in the driver much the say get_stats works. The ethtool funtion pointer points to the same functions. The driver-local functions are currently only implimented in the skge driver. Signed-off-by: Phil Dibowitz --- diff -puN include/linux/netdevice.h~interface_stats_clear include/linux/netdevice.h --- linux-2.6.17-rc3-git2/include/linux/netdevice.h~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700 +++ linux-2.6.17-rc3-git2-phil/include/linux/netdevice.h 2006-04-29 19:44:41.000000000 -0700 @@ -319,6 +319,7 @@ struct net_device struct net_device_stats* (*get_stats)(struct net_device *dev); + void (*clear_stats)(struct net_device *dev); struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); /* List of functions to handle Wireless Extensions (instead of ioctl). diff -puN drivers/net/skge.c~interface_stats_clear drivers/net/skge.c --- linux-2.6.17-rc3-git2/drivers/net/skge.c~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700 +++ linux-2.6.17-rc3-git2-phil/drivers/net/skge.c 2006-04-29 19:44:41.000000000 -0700 @@ -44,7 +44,7 @@ #include "skge.h" #define DRV_NAME "skge" -#define DRV_VERSION "1.5" +#define DRV_VERSION "1.6" #define PFX DRV_NAME " " #define DEFAULT_TX_RING_SIZE 128 @@ -97,6 +97,8 @@ static int xm_phy_write(struct skge_hw * static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val); static void genesis_get_stats(struct skge_port *skge, u64 *data); static void yukon_get_stats(struct skge_port *skge, u64 *data); +static void genesis_clear_stats(struct skge_port *skge); +static void yukon_clear_stats(struct skge_port *skge); static void yukon_init(struct skge_hw *hw, int port); static void genesis_mac_init(struct skge_hw *hw, int port); static void genesis_link_up(struct skge_port *skge); @@ -366,6 +368,15 @@ static struct net_device_stats *skge_get return &skge->net_stats; } +static void skge_clear_stats(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + if (skge->hw->chip_id == CHIP_ID_GENESIS) + genesis_clear_stats(skge); + else + yukon_clear_stats(skge); +} + static void skge_get_strings(struct net_device *dev, u32 stringset, u8 *data) { int i; @@ -722,6 +733,7 @@ static struct ethtool_ops skge_ethtool_o .phys_id = skge_phys_id, .get_stats_count = skge_get_stats_count, .get_ethtool_stats = skge_get_ethtool_stats, + .clear_ethtool_stats = skge_clear_stats, .get_perm_addr = ethtool_op_get_perm_addr, }; @@ -1383,6 +1395,20 @@ static void genesis_get_stats(struct skg data[i] = xm_read32(hw, port, skge_stats[i].xmac_offset); } +static void genesis_clear_stats(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + + /* + * This is based on reading other parts of the driver + * and is not yet tested. + */ + + xm_write16(hw, port, XM_STAT_CMD, 0 | XM_SC_CLR_RXC + | XM_SC_CLR_TXC); +} + static void genesis_mac_intr(struct skge_hw *hw, int port) { struct skge_port *skge = netdev_priv(hw->dev[port]); @@ -1871,6 +1897,21 @@ static void yukon_get_stats(struct skge_ skge_stats[i].gma_offset); } +static void yukon_clear_stats(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + u16 reg; + int i; + + reg = gma_read16(hw, port, GM_PHY_ADDR); + /* this read is important, or we sometimes get no effect */ + gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR); + for (i = 0; i < GM_MIB_CNT_SIZE; i++) + gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i); + gma_write16(hw, port, GM_PHY_ADDR, reg); +} + static void yukon_mac_intr(struct skge_hw *hw, int port) { struct net_device *dev = hw->dev[port]; @@ -3183,6 +3224,7 @@ static struct net_device *skge_devinit(s dev->do_ioctl = skge_ioctl; dev->hard_start_xmit = skge_xmit_frame; dev->get_stats = skge_get_stats; + dev->clear_stats = skge_clear_stats; if (hw->chip_id == CHIP_ID_GENESIS) dev->set_multicast_list = genesis_set_multicast; else diff -puN include/linux/ethtool.h~interface_stats_clear include/linux/ethtool.h --- linux-2.6.17-rc3-git2/include/linux/ethtool.h~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700 +++ linux-2.6.17-rc3-git2-phil/include/linux/ethtool.h 2006-04-29 19:44:41.000000000 -0700 @@ -365,6 +365,7 @@ struct ethtool_ops { int (*phys_id)(struct net_device *, u32); int (*get_stats_count)(struct net_device *); void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); + void (*clear_ethtool_stats)(struct net_device *); int (*get_perm_addr)(struct net_device *, struct ethtool_perm_addr *, u8 *); int (*begin)(struct net_device *); void (*complete)(struct net_device *); @@ -408,6 +409,7 @@ struct ethtool_ops { #define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ +#define ETHTOOL_CSTATS 0x00000023 /* Clear NIC-specific statistics */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET diff -puN net/core/ethtool.c~interface_stats_clear net/core/ethtool.c --- linux-2.6.17-rc3-git2/net/core/ethtool.c~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700 +++ linux-2.6.17-rc3-git2-phil/net/core/ethtool.c 2006-04-29 19:44:41.000000000 -0700 @@ -741,6 +741,17 @@ static int ethtool_get_stats(struct net_ return ret; } +static int ethtool_clear_stats(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_ops *ops = dev->ethtool_ops; + if (!ops->clear_ethtool_stats) + return -EOPNOTSUPP; + + ops->clear_ethtool_stats(dev); + + return 0; +} + static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) { struct ethtool_perm_addr epaddr; @@ -906,6 +917,9 @@ int dev_ethtool(struct ifreq *ifr) case ETHTOOL_SUFO: rc = ethtool_set_ufo(dev, useraddr); break; + case ETHTOOL_CSTATS: + rc = ethtool_clear_stats(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } _