net/mlx5e: Add mlx5e HV VHCA stats agent
authorEran Ben Elisha <eranbe@mellanox.com>
Thu, 22 Aug 2019 05:06:00 +0000 (05:06 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 22 Aug 2019 07:25:12 +0000 (00:25 -0700)
HV VHCA stats agent is responsible on running a preiodic rx/tx
packets/bytes stats update. Currently the supported format is version
MLX5_HV_VHCA_STATS_VERSION. Block ID 1 is dedicated for statistics data
transfer from the VF to the PF.

The reporter fetch the statistics data from all opened channels, fill it
in a buffer and send it to mlx5_hv_vhca_write_agent.

As the stats layer should include some metadata per block (sequence and
offset), the HV VHCA layer shall modify the buffer before actually send it
over block 1.

Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h

index 8d443fc..f4de9cc 100644 (file)
@@ -36,6 +36,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
 mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \
                                        lib/geneve.o en/tc_tun_vxlan.o en/tc_tun_gre.o \
                                        en/tc_tun_geneve.o diag/en_tc_tracepoint.o
+mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o
 
 #
 # Core extra
index 7316571..4467927 100644 (file)
@@ -54,6 +54,7 @@
 #include "mlx5_core.h"
 #include "en_stats.h"
 #include "en/fs.h"
+#include "lib/hv_vhca.h"
 
 extern const struct net_device_ops mlx5e_netdev_ops;
 struct page_pool;
@@ -782,6 +783,15 @@ struct mlx5e_modify_sq_param {
        int rl_index;
 };
 
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+struct mlx5e_hv_vhca_stats_agent {
+       struct mlx5_hv_vhca_agent *agent;
+       struct delayed_work        work;
+       u16                        delay;
+       void                      *buf;
+};
+#endif
+
 struct mlx5e_xsk {
        /* UMEMs are stored separately from channels, because we don't want to
         * lose them when channels are recreated. The kernel also stores UMEMs,
@@ -853,6 +863,9 @@ struct mlx5e_priv {
        struct devlink_health_reporter *tx_reporter;
        struct devlink_health_reporter *rx_reporter;
        struct mlx5e_xsk           xsk;
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+       struct mlx5e_hv_vhca_stats_agent stats_agent;
+#endif
 };
 
 struct mlx5e_profile {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
new file mode 100644 (file)
index 0000000..c37b4ac
--- /dev/null
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2018 Mellanox Technologies
+
+#include "en.h"
+#include "en/hv_vhca_stats.h"
+#include "lib/hv_vhca.h"
+#include "lib/hv.h"
+
+struct mlx5e_hv_vhca_per_ring_stats {
+       u64     rx_packets;
+       u64     rx_bytes;
+       u64     tx_packets;
+       u64     tx_bytes;
+};
+
+static void
+mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
+                             struct mlx5e_hv_vhca_per_ring_stats *data)
+{
+       struct mlx5e_channel_stats *stats;
+       int tc;
+
+       stats = &priv->channel_stats[ch];
+       data->rx_packets = stats->rq.packets;
+       data->rx_bytes   = stats->rq.bytes;
+
+       for (tc = 0; tc < priv->max_opened_tc; tc++) {
+               data->tx_packets += stats->sq[tc].packets;
+               data->tx_bytes   += stats->sq[tc].bytes;
+       }
+}
+
+static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, u64 *data,
+                                    int buf_len)
+{
+       int ch, i = 0;
+
+       for (ch = 0; ch < priv->max_nch; ch++) {
+               u64 *buf = data + i;
+
+               if (WARN_ON_ONCE(buf +
+                                sizeof(struct mlx5e_hv_vhca_per_ring_stats) >
+                                data + buf_len))
+                       return;
+
+               mlx5e_hv_vhca_fill_ring_stats(priv, ch,
+                                             (struct mlx5e_hv_vhca_per_ring_stats *)buf);
+               i += sizeof(struct mlx5e_hv_vhca_per_ring_stats) / sizeof(u64);
+       }
+}
+
+static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
+{
+       return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
+               priv->max_nch);
+}
+
+static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
+{
+       struct mlx5e_hv_vhca_stats_agent *sagent;
+       struct mlx5_hv_vhca_agent *agent;
+       struct delayed_work *dwork;
+       struct mlx5e_priv *priv;
+       int buf_len, rc;
+       void *buf;
+
+       dwork = to_delayed_work(work);
+       sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
+       priv = container_of(sagent, struct mlx5e_priv, stats_agent);
+       buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+       agent = sagent->agent;
+       buf = sagent->buf;
+
+       memset(buf, 0, buf_len);
+       mlx5e_hv_vhca_fill_stats(priv, buf, buf_len);
+
+       rc = mlx5_hv_vhca_agent_write(agent, buf, buf_len);
+       if (rc) {
+               mlx5_core_err(priv->mdev,
+                             "%s: Failed to write stats, err = %d\n",
+                             __func__, rc);
+               return;
+       }
+
+       if (sagent->delay)
+               queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
+}
+
+enum {
+       MLX5_HV_VHCA_STATS_VERSION     = 1,
+       MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF,
+};
+
+static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
+                                       struct mlx5_hv_vhca_control_block *block)
+{
+       struct mlx5e_hv_vhca_stats_agent *sagent;
+       struct mlx5e_priv *priv;
+
+       priv = mlx5_hv_vhca_agent_priv(agent);
+       sagent = &priv->stats_agent;
+
+       block->version = MLX5_HV_VHCA_STATS_VERSION;
+       block->rings   = priv->max_nch;
+
+       if (!block->command) {
+               cancel_delayed_work_sync(&priv->stats_agent.work);
+               return;
+       }
+
+       sagent->delay = block->command == MLX5_HV_VHCA_STATS_UPDATE_ONCE ? 0 :
+                       msecs_to_jiffies(block->command * 100);
+
+       queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
+}
+
+static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
+{
+       struct mlx5e_priv *priv = mlx5_hv_vhca_agent_priv(agent);
+
+       cancel_delayed_work_sync(&priv->stats_agent.work);
+}
+
+int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
+{
+       int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+       struct mlx5_hv_vhca_agent *agent;
+
+       priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
+       if (!priv->stats_agent.buf)
+               return -ENOMEM;
+
+       agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
+                                         MLX5_HV_VHCA_AGENT_STATS,
+                                         mlx5e_hv_vhca_stats_control, NULL,
+                                         mlx5e_hv_vhca_stats_cleanup,
+                                         priv);
+
+       if (IS_ERR_OR_NULL(agent)) {
+               if (IS_ERR(agent))
+                       netdev_warn(priv->netdev,
+                                   "Failed to create hv vhca stats agent, err = %ld\n",
+                                   PTR_ERR(agent));
+
+               kfree(priv->stats_agent.buf);
+               return IS_ERR_OR_NULL(agent);
+       }
+
+       priv->stats_agent.agent = agent;
+       INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
+
+       return 0;
+}
+
+void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
+{
+       if (IS_ERR_OR_NULL(priv->stats_agent.agent))
+               return;
+
+       mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
+       kfree(priv->stats_agent.buf);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h
new file mode 100644 (file)
index 0000000..664463f
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#ifndef __MLX5_EN_STATS_VHCA_H__
+#define __MLX5_EN_STATS_VHCA_H__
+#include "en.h"
+
+#if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
+
+int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv);
+void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv);
+
+#else
+
+static inline int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
+{
+       return 0;
+}
+
+static inline void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
+{
+}
+#endif
+
+#endif /* __MLX5_EN_STATS_VHCA_H__ */
index 7fdea64..fa4bf2d 100644 (file)
@@ -62,6 +62,7 @@
 #include "en/xsk/setup.h"
 #include "en/xsk/rx.h"
 #include "en/xsk/tx.h"
+#include "en/hv_vhca_stats.h"
 
 
 bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
@@ -5109,6 +5110,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
        if (mlx5e_monitor_counter_supported(priv))
                mlx5e_monitor_counter_init(priv);
 
+       mlx5e_hv_vhca_stats_create(priv);
        if (netdev->reg_state != NETREG_REGISTERED)
                return;
 #ifdef CONFIG_MLX5_CORE_EN_DCB
@@ -5141,6 +5143,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
 
        queue_work(priv->wq, &priv->set_rx_mode_work);
 
+       mlx5e_hv_vhca_stats_destroy(priv);
        if (mlx5e_monitor_counter_supported(priv))
                mlx5e_monitor_counter_cleanup(priv);
 
index 984e7ad..4bad6a5 100644 (file)
@@ -13,6 +13,7 @@ struct mlx5_hv_vhca_control_block;
 
 enum mlx5_hv_vhca_agent_type {
        MLX5_HV_VHCA_AGENT_CONTROL = 0,
+       MLX5_HV_VHCA_AGENT_STATS   = 1,
        MLX5_HV_VHCA_AGENT_MAX = 32,
 };