linux/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2// Copyright (c) 2018 Mellanox Technologies
   3
   4#include "en.h"
   5#include "en/hv_vhca_stats.h"
   6#include "lib/hv_vhca.h"
   7#include "lib/hv.h"
   8
   9struct mlx5e_hv_vhca_per_ring_stats {
  10        u64     rx_packets;
  11        u64     rx_bytes;
  12        u64     tx_packets;
  13        u64     tx_bytes;
  14};
  15
  16static void
  17mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
  18                              struct mlx5e_hv_vhca_per_ring_stats *data)
  19{
  20        struct mlx5e_channel_stats *stats;
  21        int tc;
  22
  23        stats = &priv->channel_stats[ch];
  24        data->rx_packets = stats->rq.packets;
  25        data->rx_bytes   = stats->rq.bytes;
  26
  27        for (tc = 0; tc < priv->max_opened_tc; tc++) {
  28                data->tx_packets += stats->sq[tc].packets;
  29                data->tx_bytes   += stats->sq[tc].bytes;
  30        }
  31}
  32
  33static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
  34                                     int buf_len)
  35{
  36        int ch, i = 0;
  37
  38        for (ch = 0; ch < priv->stats_nch; ch++) {
  39                void *buf = data + i;
  40
  41                if (WARN_ON_ONCE(buf +
  42                                 sizeof(struct mlx5e_hv_vhca_per_ring_stats) >
  43                                 data + buf_len))
  44                        return;
  45
  46                mlx5e_hv_vhca_fill_ring_stats(priv, ch, buf);
  47                i += sizeof(struct mlx5e_hv_vhca_per_ring_stats);
  48        }
  49}
  50
  51static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
  52{
  53        return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
  54                priv->stats_nch);
  55}
  56
  57static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
  58{
  59        struct mlx5e_hv_vhca_stats_agent *sagent;
  60        struct mlx5_hv_vhca_agent *agent;
  61        struct delayed_work *dwork;
  62        struct mlx5e_priv *priv;
  63        int buf_len, rc;
  64        void *buf;
  65
  66        dwork = to_delayed_work(work);
  67        sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
  68        priv = container_of(sagent, struct mlx5e_priv, stats_agent);
  69        buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
  70        agent = sagent->agent;
  71        buf = sagent->buf;
  72
  73        memset(buf, 0, buf_len);
  74        mlx5e_hv_vhca_fill_stats(priv, buf, buf_len);
  75
  76        rc = mlx5_hv_vhca_agent_write(agent, buf, buf_len);
  77        if (rc) {
  78                mlx5_core_err(priv->mdev,
  79                              "%s: Failed to write stats, err = %d\n",
  80                              __func__, rc);
  81                return;
  82        }
  83
  84        if (sagent->delay)
  85                queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
  86}
  87
  88enum {
  89        MLX5_HV_VHCA_STATS_VERSION     = 1,
  90        MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF,
  91};
  92
  93static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
  94                                        struct mlx5_hv_vhca_control_block *block)
  95{
  96        struct mlx5e_hv_vhca_stats_agent *sagent;
  97        struct mlx5e_priv *priv;
  98
  99        priv = mlx5_hv_vhca_agent_priv(agent);
 100        sagent = &priv->stats_agent;
 101
 102        block->version = MLX5_HV_VHCA_STATS_VERSION;
 103        block->rings   = priv->stats_nch;
 104
 105        if (!block->command) {
 106                cancel_delayed_work_sync(&priv->stats_agent.work);
 107                return;
 108        }
 109
 110        sagent->delay = block->command == MLX5_HV_VHCA_STATS_UPDATE_ONCE ? 0 :
 111                        msecs_to_jiffies(block->command * 100);
 112
 113        queue_delayed_work(priv->wq, &sagent->work, sagent->delay);
 114}
 115
 116static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
 117{
 118        struct mlx5e_priv *priv = mlx5_hv_vhca_agent_priv(agent);
 119
 120        cancel_delayed_work_sync(&priv->stats_agent.work);
 121}
 122
 123int mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
 124{
 125        int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
 126        struct mlx5_hv_vhca_agent *agent;
 127
 128        priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
 129        if (!priv->stats_agent.buf)
 130                return -ENOMEM;
 131
 132        agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
 133                                          MLX5_HV_VHCA_AGENT_STATS,
 134                                          mlx5e_hv_vhca_stats_control, NULL,
 135                                          mlx5e_hv_vhca_stats_cleanup,
 136                                          priv);
 137
 138        if (IS_ERR_OR_NULL(agent)) {
 139                if (IS_ERR(agent))
 140                        netdev_warn(priv->netdev,
 141                                    "Failed to create hv vhca stats agent, err = %ld\n",
 142                                    PTR_ERR(agent));
 143
 144                kvfree(priv->stats_agent.buf);
 145                return IS_ERR_OR_NULL(agent);
 146        }
 147
 148        priv->stats_agent.agent = agent;
 149        INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
 150
 151        return 0;
 152}
 153
 154void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
 155{
 156        if (IS_ERR_OR_NULL(priv->stats_agent.agent))
 157                return;
 158
 159        mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
 160        kvfree(priv->stats_agent.buf);
 161}
 162