/* SPDX-License-Identifier: GPL-2.0 */
|
/* Copyright (c) 2018 Mellanox Technologies. */
|
|
#include "en.h"
|
#include "monitor_stats.h"
|
#include "lib/eq.h"
|
|
/* Driver will set the following watch counters list:
|
* Ppcnt.802_3:
|
* a_in_range_length_errors Type: 0x0, Counter: 0x0, group_id = N/A
|
* a_out_of_range_length_field Type: 0x0, Counter: 0x1, group_id = N/A
|
* a_frame_too_long_errors Type: 0x0, Counter: 0x2, group_id = N/A
|
* a_frame_check_sequence_errors Type: 0x0, Counter: 0x3, group_id = N/A
|
* a_alignment_errors Type: 0x0, Counter: 0x4, group_id = N/A
|
* if_out_discards Type: 0x0, Counter: 0x5, group_id = N/A
|
* Q_Counters:
|
* Q[index].rx_out_of_buffer Type: 0x1, Counter: 0x4, group_id = counter_ix
|
*/
|
|
#define NUM_REQ_PPCNT_COUNTER_S1 MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1
|
#define NUM_REQ_Q_COUNTERS_S1 MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1
|
|
int mlx5e_monitor_counter_supported(struct mlx5e_priv *priv)
|
{
|
struct mlx5_core_dev *mdev = priv->mdev;
|
|
if (!MLX5_CAP_GEN(mdev, max_num_of_monitor_counters))
|
return false;
|
if (MLX5_CAP_PCAM_REG(mdev, ppcnt) &&
|
MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters) <
|
NUM_REQ_PPCNT_COUNTER_S1)
|
return false;
|
if (MLX5_CAP_GEN(mdev, num_q_monitor_counters) <
|
NUM_REQ_Q_COUNTERS_S1)
|
return false;
|
return true;
|
}
|
|
void mlx5e_monitor_counter_arm(struct mlx5e_priv *priv)
|
{
|
u32 in[MLX5_ST_SZ_DW(arm_monitor_counter_in)] = {};
|
|
MLX5_SET(arm_monitor_counter_in, in, opcode,
|
MLX5_CMD_OP_ARM_MONITOR_COUNTER);
|
mlx5_cmd_exec_in(priv->mdev, arm_monitor_counter, in);
|
}
|
|
static void mlx5e_monitor_counters_work(struct work_struct *work)
|
{
|
struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
|
monitor_counters_work);
|
|
mutex_lock(&priv->state_lock);
|
mlx5e_stats_update_ndo_stats(priv);
|
mutex_unlock(&priv->state_lock);
|
mlx5e_monitor_counter_arm(priv);
|
}
|
|
static int mlx5e_monitor_event_handler(struct notifier_block *nb,
|
unsigned long event, void *eqe)
|
{
|
struct mlx5e_priv *priv = mlx5_nb_cof(nb, struct mlx5e_priv,
|
monitor_counters_nb);
|
queue_work(priv->wq, &priv->monitor_counters_work);
|
return NOTIFY_OK;
|
}
|
|
static int fill_monitor_counter_ppcnt_set1(int cnt, u32 *in)
|
{
|
enum mlx5_monitor_counter_ppcnt ppcnt_cnt;
|
|
for (ppcnt_cnt = 0;
|
ppcnt_cnt < NUM_REQ_PPCNT_COUNTER_S1;
|
ppcnt_cnt++, cnt++) {
|
MLX5_SET(set_monitor_counter_in, in,
|
monitor_counter[cnt].type,
|
MLX5_QUERY_MONITOR_CNT_TYPE_PPCNT);
|
MLX5_SET(set_monitor_counter_in, in,
|
monitor_counter[cnt].counter,
|
ppcnt_cnt);
|
}
|
return ppcnt_cnt;
|
}
|
|
static int fill_monitor_counter_q_counter_set1(int cnt, int q_counter, u32 *in)
|
{
|
MLX5_SET(set_monitor_counter_in, in,
|
monitor_counter[cnt].type,
|
MLX5_QUERY_MONITOR_CNT_TYPE_Q_COUNTER);
|
MLX5_SET(set_monitor_counter_in, in,
|
monitor_counter[cnt].counter,
|
MLX5_QUERY_MONITOR_Q_COUNTER_RX_OUT_OF_BUFFER);
|
MLX5_SET(set_monitor_counter_in, in,
|
monitor_counter[cnt].counter_group_id,
|
q_counter);
|
return 1;
|
}
|
|
/* check if mlx5e_monitor_counter_supported before calling this function*/
|
static void mlx5e_set_monitor_counter(struct mlx5e_priv *priv)
|
{
|
struct mlx5_core_dev *mdev = priv->mdev;
|
int max_num_of_counters = MLX5_CAP_GEN(mdev, max_num_of_monitor_counters);
|
int num_q_counters = MLX5_CAP_GEN(mdev, num_q_monitor_counters);
|
int num_ppcnt_counters = !MLX5_CAP_PCAM_REG(mdev, ppcnt) ? 0 :
|
MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters);
|
u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
|
int q_counter = priv->q_counter;
|
int cnt = 0;
|
|
if (num_ppcnt_counters >= NUM_REQ_PPCNT_COUNTER_S1 &&
|
max_num_of_counters >= (NUM_REQ_PPCNT_COUNTER_S1 + cnt))
|
cnt += fill_monitor_counter_ppcnt_set1(cnt, in);
|
|
if (num_q_counters >= NUM_REQ_Q_COUNTERS_S1 &&
|
max_num_of_counters >= (NUM_REQ_Q_COUNTERS_S1 + cnt) &&
|
q_counter)
|
cnt += fill_monitor_counter_q_counter_set1(cnt, q_counter, in);
|
|
MLX5_SET(set_monitor_counter_in, in, num_of_counters, cnt);
|
MLX5_SET(set_monitor_counter_in, in, opcode,
|
MLX5_CMD_OP_SET_MONITOR_COUNTER);
|
|
mlx5_cmd_exec_in(mdev, set_monitor_counter, in);
|
}
|
|
/* check if mlx5e_monitor_counter_supported before calling this function*/
|
void mlx5e_monitor_counter_init(struct mlx5e_priv *priv)
|
{
|
INIT_WORK(&priv->monitor_counters_work, mlx5e_monitor_counters_work);
|
MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
|
MONITOR_COUNTER);
|
mlx5_eq_notifier_register(priv->mdev, &priv->monitor_counters_nb);
|
|
mlx5e_set_monitor_counter(priv);
|
mlx5e_monitor_counter_arm(priv);
|
queue_work(priv->wq, &priv->update_stats_work);
|
}
|
|
/* check if mlx5e_monitor_counter_supported before calling this function*/
|
void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
|
{
|
u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
|
|
MLX5_SET(set_monitor_counter_in, in, opcode,
|
MLX5_CMD_OP_SET_MONITOR_COUNTER);
|
|
mlx5_cmd_exec_in(priv->mdev, set_monitor_counter, in);
|
mlx5_eq_notifier_unregister(priv->mdev, &priv->monitor_counters_nb);
|
cancel_work_sync(&priv->monitor_counters_work);
|
}
|