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 
23 static 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 
37 int 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(pos))
44 			return false;
45 	return true;
46 }
47 
48 static 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 
57 static 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(&priv->state_lock);
67 	mlx5_sd_for_each_dev(i, priv->mdev, pos)
68 		mlx5e_monitor_counter_arm(pos);
69 }
70 
71 static 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(priv->wq, &priv->monitor_counters_work);
77 	return NOTIFY_OK;
78 }
79 
80 static 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 
97 static 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*/
112 static 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*/
138 void 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(pos, &priv->monitor_counters_nb);
148 		mlx5e_set_monitor_counter(pos, priv->q_counter[i]);
149 		mlx5e_monitor_counter_arm(pos);
150 	}
151 	queue_work(priv->wq, &priv->update_stats_work);
152 }
153 
154 /* check if mlx5e_monitor_counter_supported before calling this function*/
155 void 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(pos, &priv->monitor_counters_nb);
167 	}
168 	cancel_work_sync(&priv->monitor_counters_work);
169 }
170