1/* SPDX-License-Identifier: GPL-2.0 */
2/* Copyright (c) 2018 Mellanox Technologies. */
3
4#include "en.h"
5#include "monitor_stats.h"
6#include "lib/eq.h"
7
8/* Driver will set the following watch counters list:
9 * Ppcnt.802_3:
10 * a_in_range_length_errors Type: 0x0, Counter: 0x0, group_id = N/A
11 * a_out_of_range_length_field Type: 0x0, Counter: 0x1, group_id = N/A
12 * a_frame_too_long_errors Type: 0x0, Counter: 0x2, group_id = N/A
13 * a_frame_check_sequence_errors Type: 0x0, Counter: 0x3, group_id = N/A
14 * a_alignment_errors Type: 0x0, Counter: 0x4, group_id = N/A
15 * if_out_discards Type: 0x0, Counter: 0x5, group_id = N/A
16 * Q_Counters:
17 * Q[index].rx_out_of_buffer Type: 0x1, Counter: 0x4, group_id = counter_ix
18 */
19
20#define NUM_REQ_PPCNT_COUNTER_S1 MLX5_CMD_SET_MONITOR_NUM_PPCNT_COUNTER_SET1
21#define NUM_REQ_Q_COUNTERS_S1 MLX5_CMD_SET_MONITOR_NUM_Q_COUNTERS_SET1
22
23static int mlx5e_monitor_counter_cap(struct mlx5_core_dev *mdev)
24{
25 if (!MLX5_CAP_GEN(mdev, max_num_of_monitor_counters))
26 return false;
27 if (MLX5_CAP_PCAM_REG(mdev, ppcnt) &&
28 MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters) <
29 NUM_REQ_PPCNT_COUNTER_S1)
30 return false;
31 if (MLX5_CAP_GEN(mdev, num_q_monitor_counters) <
32 NUM_REQ_Q_COUNTERS_S1)
33 return false;
34 return true;
35}
36
37int mlx5e_monitor_counter_supported(struct mlx5e_priv *priv)
38{
39 struct mlx5_core_dev *pos;
40 int i;
41
42 mlx5_sd_for_each_dev(i, priv->mdev, pos)
43 if (!mlx5e_monitor_counter_cap(mdev: pos))
44 return false;
45 return true;
46}
47
48static void mlx5e_monitor_counter_arm(struct mlx5_core_dev *mdev)
49{
50 u32 in[MLX5_ST_SZ_DW(arm_monitor_counter_in)] = {};
51
52 MLX5_SET(arm_monitor_counter_in, in, opcode,
53 MLX5_CMD_OP_ARM_MONITOR_COUNTER);
54 mlx5_cmd_exec_in(mdev, arm_monitor_counter, in);
55}
56
57static void mlx5e_monitor_counters_work(struct work_struct *work)
58{
59 struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
60 monitor_counters_work);
61 struct mlx5_core_dev *pos;
62 int i;
63
64 mutex_lock(&priv->state_lock);
65 mlx5e_stats_update_ndo_stats(priv);
66 mutex_unlock(lock: &priv->state_lock);
67 mlx5_sd_for_each_dev(i, priv->mdev, pos)
68 mlx5e_monitor_counter_arm(mdev: pos);
69}
70
71static int mlx5e_monitor_event_handler(struct notifier_block *nb,
72 unsigned long event, void *eqe)
73{
74 struct mlx5e_priv *priv = mlx5_nb_cof(nb, struct mlx5e_priv,
75 monitor_counters_nb);
76 queue_work(wq: priv->wq, work: &priv->monitor_counters_work);
77 return NOTIFY_OK;
78}
79
80static int fill_monitor_counter_ppcnt_set1(int cnt, u32 *in)
81{
82 enum mlx5_monitor_counter_ppcnt ppcnt_cnt;
83
84 for (ppcnt_cnt = 0;
85 ppcnt_cnt < NUM_REQ_PPCNT_COUNTER_S1;
86 ppcnt_cnt++, cnt++) {
87 MLX5_SET(set_monitor_counter_in, in,
88 monitor_counter[cnt].type,
89 MLX5_QUERY_MONITOR_CNT_TYPE_PPCNT);
90 MLX5_SET(set_monitor_counter_in, in,
91 monitor_counter[cnt].counter,
92 ppcnt_cnt);
93 }
94 return ppcnt_cnt;
95}
96
97static int fill_monitor_counter_q_counter_set1(int cnt, int q_counter, u32 *in)
98{
99 MLX5_SET(set_monitor_counter_in, in,
100 monitor_counter[cnt].type,
101 MLX5_QUERY_MONITOR_CNT_TYPE_Q_COUNTER);
102 MLX5_SET(set_monitor_counter_in, in,
103 monitor_counter[cnt].counter,
104 MLX5_QUERY_MONITOR_Q_COUNTER_RX_OUT_OF_BUFFER);
105 MLX5_SET(set_monitor_counter_in, in,
106 monitor_counter[cnt].counter_group_id,
107 q_counter);
108 return 1;
109}
110
111/* check if mlx5e_monitor_counter_supported before calling this function*/
112static void mlx5e_set_monitor_counter(struct mlx5_core_dev *mdev, int q_counter)
113{
114 int max_num_of_counters = MLX5_CAP_GEN(mdev, max_num_of_monitor_counters);
115 int num_q_counters = MLX5_CAP_GEN(mdev, num_q_monitor_counters);
116 int num_ppcnt_counters = !MLX5_CAP_PCAM_REG(mdev, ppcnt) ? 0 :
117 MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters);
118 u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
119 int cnt = 0;
120
121 if (num_ppcnt_counters >= NUM_REQ_PPCNT_COUNTER_S1 &&
122 max_num_of_counters >= (NUM_REQ_PPCNT_COUNTER_S1 + cnt))
123 cnt += fill_monitor_counter_ppcnt_set1(cnt, in);
124
125 if (num_q_counters >= NUM_REQ_Q_COUNTERS_S1 &&
126 max_num_of_counters >= (NUM_REQ_Q_COUNTERS_S1 + cnt) &&
127 q_counter)
128 cnt += fill_monitor_counter_q_counter_set1(cnt, q_counter, in);
129
130 MLX5_SET(set_monitor_counter_in, in, num_of_counters, cnt);
131 MLX5_SET(set_monitor_counter_in, in, opcode,
132 MLX5_CMD_OP_SET_MONITOR_COUNTER);
133
134 mlx5_cmd_exec_in(mdev, set_monitor_counter, in);
135}
136
137/* check if mlx5e_monitor_counter_supported before calling this function*/
138void mlx5e_monitor_counter_init(struct mlx5e_priv *priv)
139{
140 struct mlx5_core_dev *pos;
141 int i;
142
143 INIT_WORK(&priv->monitor_counters_work, mlx5e_monitor_counters_work);
144 MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
145 MONITOR_COUNTER);
146 mlx5_sd_for_each_dev(i, priv->mdev, pos) {
147 mlx5_eq_notifier_register(dev: pos, nb: &priv->monitor_counters_nb);
148 mlx5e_set_monitor_counter(mdev: pos, q_counter: priv->q_counter[i]);
149 mlx5e_monitor_counter_arm(mdev: pos);
150 }
151 queue_work(wq: priv->wq, work: &priv->update_stats_work);
152}
153
154/* check if mlx5e_monitor_counter_supported before calling this function*/
155void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
156{
157 u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
158 struct mlx5_core_dev *pos;
159 int i;
160
161 MLX5_SET(set_monitor_counter_in, in, opcode,
162 MLX5_CMD_OP_SET_MONITOR_COUNTER);
163
164 mlx5_sd_for_each_dev(i, priv->mdev, pos) {
165 mlx5_cmd_exec_in(pos, set_monitor_counter, in);
166 mlx5_eq_notifier_unregister(dev: pos, nb: &priv->monitor_counters_nb);
167 }
168 cancel_work_sync(work: &priv->monitor_counters_work);
169}
170

source code of linux/drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c