diff options
author | Nogah Frankel <nogahf@mellanox.com> | 2016-09-16 09:05:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-18 22:33:42 -0400 |
commit | fc1bbb0f1831cc22326c86fb21d88cca44999b3e (patch) | |
tree | 9ac04fd87ecd80fd0a46b03b48748ba06c8075d7 | |
parent | 69ae6ad2ff37911903a90256e216d7e7ae460002 (diff) |
mlxsw: spectrum: Implement offload stats ndo and expose HW stats by default
Change the default statistics ndo to return HW statistics
(like the one returned by ethtool_ops).
The HW stats are collected to a cache by delayed work every 1 sec.
Implement the offload stat ndo.
Add a function to get SW statistics, to be called from this function.
Signed-off-by: Nogah Frankel <nogahf@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 129 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 5 |
2 files changed, 127 insertions, 7 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 27bbcaf9cfcd..171f8dd19efa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c | |||
@@ -819,9 +819,9 @@ err_span_port_mtu_update: | |||
819 | return err; | 819 | return err; |
820 | } | 820 | } |
821 | 821 | ||
822 | static struct rtnl_link_stats64 * | 822 | int |
823 | mlxsw_sp_port_get_stats64(struct net_device *dev, | 823 | mlxsw_sp_port_get_sw_stats64(const struct net_device *dev, |
824 | struct rtnl_link_stats64 *stats) | 824 | struct rtnl_link_stats64 *stats) |
825 | { | 825 | { |
826 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | 826 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
827 | struct mlxsw_sp_port_pcpu_stats *p; | 827 | struct mlxsw_sp_port_pcpu_stats *p; |
@@ -848,6 +848,107 @@ mlxsw_sp_port_get_stats64(struct net_device *dev, | |||
848 | tx_dropped += p->tx_dropped; | 848 | tx_dropped += p->tx_dropped; |
849 | } | 849 | } |
850 | stats->tx_dropped = tx_dropped; | 850 | stats->tx_dropped = tx_dropped; |
851 | return 0; | ||
852 | } | ||
853 | |||
854 | bool mlxsw_sp_port_has_offload_stats(int attr_id) | ||
855 | { | ||
856 | switch (attr_id) { | ||
857 | case IFLA_OFFLOAD_XSTATS_CPU_HIT: | ||
858 | return true; | ||
859 | } | ||
860 | |||
861 | return false; | ||
862 | } | ||
863 | |||
864 | int mlxsw_sp_port_get_offload_stats(int attr_id, const struct net_device *dev, | ||
865 | void *sp) | ||
866 | { | ||
867 | switch (attr_id) { | ||
868 | case IFLA_OFFLOAD_XSTATS_CPU_HIT: | ||
869 | return mlxsw_sp_port_get_sw_stats64(dev, sp); | ||
870 | } | ||
871 | |||
872 | return -EINVAL; | ||
873 | } | ||
874 | |||
875 | static int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp, | ||
876 | int prio, char *ppcnt_pl) | ||
877 | { | ||
878 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | ||
879 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | ||
880 | |||
881 | mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, grp, prio); | ||
882 | return mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); | ||
883 | } | ||
884 | |||
885 | static int mlxsw_sp_port_get_hw_stats(struct net_device *dev, | ||
886 | struct rtnl_link_stats64 *stats) | ||
887 | { | ||
888 | char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; | ||
889 | int err; | ||
890 | |||
891 | err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, | ||
892 | 0, ppcnt_pl); | ||
893 | if (err) | ||
894 | goto out; | ||
895 | |||
896 | stats->tx_packets = | ||
897 | mlxsw_reg_ppcnt_a_frames_transmitted_ok_get(ppcnt_pl); | ||
898 | stats->rx_packets = | ||
899 | mlxsw_reg_ppcnt_a_frames_received_ok_get(ppcnt_pl); | ||
900 | stats->tx_bytes = | ||
901 | mlxsw_reg_ppcnt_a_octets_transmitted_ok_get(ppcnt_pl); | ||
902 | stats->rx_bytes = | ||
903 | mlxsw_reg_ppcnt_a_octets_received_ok_get(ppcnt_pl); | ||
904 | stats->multicast = | ||
905 | mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get(ppcnt_pl); | ||
906 | |||
907 | stats->rx_crc_errors = | ||
908 | mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get(ppcnt_pl); | ||
909 | stats->rx_frame_errors = | ||
910 | mlxsw_reg_ppcnt_a_alignment_errors_get(ppcnt_pl); | ||
911 | |||
912 | stats->rx_length_errors = ( | ||
913 | mlxsw_reg_ppcnt_a_in_range_length_errors_get(ppcnt_pl) + | ||
914 | mlxsw_reg_ppcnt_a_out_of_range_length_field_get(ppcnt_pl) + | ||
915 | mlxsw_reg_ppcnt_a_frame_too_long_errors_get(ppcnt_pl)); | ||
916 | |||
917 | stats->rx_errors = (stats->rx_crc_errors + | ||
918 | stats->rx_frame_errors + stats->rx_length_errors); | ||
919 | |||
920 | out: | ||
921 | return err; | ||
922 | } | ||
923 | |||
924 | static void update_stats_cache(struct work_struct *work) | ||
925 | { | ||
926 | struct mlxsw_sp_port *mlxsw_sp_port = | ||
927 | container_of(work, struct mlxsw_sp_port, | ||
928 | hw_stats.update_dw.work); | ||
929 | |||
930 | if (!netif_carrier_ok(mlxsw_sp_port->dev)) | ||
931 | goto out; | ||
932 | |||
933 | mlxsw_sp_port_get_hw_stats(mlxsw_sp_port->dev, | ||
934 | mlxsw_sp_port->hw_stats.cache); | ||
935 | |||
936 | out: | ||
937 | mlxsw_core_schedule_dw(&mlxsw_sp_port->hw_stats.update_dw, | ||
938 | MLXSW_HW_STATS_UPDATE_TIME); | ||
939 | } | ||
940 | |||
941 | /* Return the stats from a cache that is updated periodically, | ||
942 | * as this function might get called in an atomic context. | ||
943 | */ | ||
944 | static struct rtnl_link_stats64 * | ||
945 | mlxsw_sp_port_get_stats64(struct net_device *dev, | ||
946 | struct rtnl_link_stats64 *stats) | ||
947 | { | ||
948 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | ||
949 | |||
950 | memcpy(stats, mlxsw_sp_port->hw_stats.cache, sizeof(*stats)); | ||
951 | |||
851 | return stats; | 952 | return stats; |
852 | } | 953 | } |
853 | 954 | ||
@@ -1209,6 +1310,8 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { | |||
1209 | .ndo_set_mac_address = mlxsw_sp_port_set_mac_address, | 1310 | .ndo_set_mac_address = mlxsw_sp_port_set_mac_address, |
1210 | .ndo_change_mtu = mlxsw_sp_port_change_mtu, | 1311 | .ndo_change_mtu = mlxsw_sp_port_change_mtu, |
1211 | .ndo_get_stats64 = mlxsw_sp_port_get_stats64, | 1312 | .ndo_get_stats64 = mlxsw_sp_port_get_stats64, |
1313 | .ndo_has_offload_stats = mlxsw_sp_port_has_offload_stats, | ||
1314 | .ndo_get_offload_stats = mlxsw_sp_port_get_offload_stats, | ||
1212 | .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, | 1315 | .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, |
1213 | .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, | 1316 | .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, |
1214 | .ndo_neigh_construct = mlxsw_sp_router_neigh_construct, | 1317 | .ndo_neigh_construct = mlxsw_sp_router_neigh_construct, |
@@ -1547,8 +1650,6 @@ static void __mlxsw_sp_port_get_stats(struct net_device *dev, | |||
1547 | enum mlxsw_reg_ppcnt_grp grp, int prio, | 1650 | enum mlxsw_reg_ppcnt_grp grp, int prio, |
1548 | u64 *data, int data_index) | 1651 | u64 *data, int data_index) |
1549 | { | 1652 | { |
1550 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | ||
1551 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | ||
1552 | struct mlxsw_sp_port_hw_stats *hw_stats; | 1653 | struct mlxsw_sp_port_hw_stats *hw_stats; |
1553 | char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; | 1654 | char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
1554 | int i, len; | 1655 | int i, len; |
@@ -1557,8 +1658,7 @@ static void __mlxsw_sp_port_get_stats(struct net_device *dev, | |||
1557 | err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp); | 1658 | err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp); |
1558 | if (err) | 1659 | if (err) |
1559 | return; | 1660 | return; |
1560 | mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, grp, prio); | 1661 | mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl); |
1561 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); | ||
1562 | for (i = 0; i < len; i++) | 1662 | for (i = 0; i < len; i++) |
1563 | data[data_index + i] = !err ? hw_stats[i].getter(ppcnt_pl) : 0; | 1663 | data[data_index + i] = !err ? hw_stats[i].getter(ppcnt_pl) : 0; |
1564 | } | 1664 | } |
@@ -2145,6 +2245,16 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, | |||
2145 | goto err_alloc_stats; | 2245 | goto err_alloc_stats; |
2146 | } | 2246 | } |
2147 | 2247 | ||
2248 | mlxsw_sp_port->hw_stats.cache = | ||
2249 | kzalloc(sizeof(*mlxsw_sp_port->hw_stats.cache), GFP_KERNEL); | ||
2250 | |||
2251 | if (!mlxsw_sp_port->hw_stats.cache) { | ||
2252 | err = -ENOMEM; | ||
2253 | goto err_alloc_hw_stats; | ||
2254 | } | ||
2255 | INIT_DELAYED_WORK(&mlxsw_sp_port->hw_stats.update_dw, | ||
2256 | &update_stats_cache); | ||
2257 | |||
2148 | dev->netdev_ops = &mlxsw_sp_port_netdev_ops; | 2258 | dev->netdev_ops = &mlxsw_sp_port_netdev_ops; |
2149 | dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; | 2259 | dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; |
2150 | 2260 | ||
@@ -2245,6 +2355,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, | |||
2245 | goto err_core_port_init; | 2355 | goto err_core_port_init; |
2246 | } | 2356 | } |
2247 | 2357 | ||
2358 | mlxsw_core_schedule_dw(&mlxsw_sp_port->hw_stats.update_dw, 0); | ||
2248 | return 0; | 2359 | return 0; |
2249 | 2360 | ||
2250 | err_core_port_init: | 2361 | err_core_port_init: |
@@ -2265,6 +2376,8 @@ err_port_system_port_mapping_set: | |||
2265 | err_dev_addr_init: | 2376 | err_dev_addr_init: |
2266 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); | 2377 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); |
2267 | err_port_swid_set: | 2378 | err_port_swid_set: |
2379 | kfree(mlxsw_sp_port->hw_stats.cache); | ||
2380 | err_alloc_hw_stats: | ||
2268 | free_percpu(mlxsw_sp_port->pcpu_stats); | 2381 | free_percpu(mlxsw_sp_port->pcpu_stats); |
2269 | err_alloc_stats: | 2382 | err_alloc_stats: |
2270 | kfree(mlxsw_sp_port->untagged_vlans); | 2383 | kfree(mlxsw_sp_port->untagged_vlans); |
@@ -2281,6 +2394,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) | |||
2281 | 2394 | ||
2282 | if (!mlxsw_sp_port) | 2395 | if (!mlxsw_sp_port) |
2283 | return; | 2396 | return; |
2397 | cancel_delayed_work_sync(&mlxsw_sp_port->hw_stats.update_dw); | ||
2284 | mlxsw_core_port_fini(&mlxsw_sp_port->core_port); | 2398 | mlxsw_core_port_fini(&mlxsw_sp_port->core_port); |
2285 | unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ | 2399 | unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ |
2286 | mlxsw_sp->ports[local_port] = NULL; | 2400 | mlxsw_sp->ports[local_port] = NULL; |
@@ -2290,6 +2404,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) | |||
2290 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); | 2404 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); |
2291 | mlxsw_sp_port_module_unmap(mlxsw_sp, mlxsw_sp_port->local_port); | 2405 | mlxsw_sp_port_module_unmap(mlxsw_sp, mlxsw_sp_port->local_port); |
2292 | free_percpu(mlxsw_sp_port->pcpu_stats); | 2406 | free_percpu(mlxsw_sp_port->pcpu_stats); |
2407 | kfree(mlxsw_sp_port->hw_stats.cache); | ||
2293 | kfree(mlxsw_sp_port->untagged_vlans); | 2408 | kfree(mlxsw_sp_port->untagged_vlans); |
2294 | kfree(mlxsw_sp_port->active_vlans); | 2409 | kfree(mlxsw_sp_port->active_vlans); |
2295 | WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vports_list)); | 2410 | WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vports_list)); |
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 969c250b3048..49f4cafce148 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h | |||
@@ -361,6 +361,11 @@ struct mlxsw_sp_port { | |||
361 | struct list_head vports_list; | 361 | struct list_head vports_list; |
362 | /* TC handles */ | 362 | /* TC handles */ |
363 | struct list_head mall_tc_list; | 363 | struct list_head mall_tc_list; |
364 | struct { | ||
365 | #define MLXSW_HW_STATS_UPDATE_TIME HZ | ||
366 | struct rtnl_link_stats64 *cache; | ||
367 | struct delayed_work update_dw; | ||
368 | } hw_stats; | ||
364 | }; | 369 | }; |
365 | 370 | ||
366 | struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev); | 371 | struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev); |