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 9 struct mlx5e_hv_vhca_per_ring_stats { 10 u64 rx_packets; 11 u64 rx_bytes; 12 u64 tx_packets; 13 u64 tx_bytes; 14 }; 15 16 static void 17 mlx5e_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 33 static 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 51 static 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 57 static 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 88 enum { 89 MLX5_HV_VHCA_STATS_VERSION = 1, 90 MLX5_HV_VHCA_STATS_UPDATE_ONCE = 0xFFFF, 91 }; 92 93 static 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 116 static 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 123 int 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 154 void 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