| /* 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); |
| } |