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(mdev: 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(lock: &priv->state_lock); |
67 | mlx5_sd_for_each_dev(i, priv->mdev, pos) |
68 | mlx5e_monitor_counter_arm(mdev: 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(wq: priv->wq, work: &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(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*/ |
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(dev: pos, nb: &priv->monitor_counters_nb); |
167 | } |
168 | cancel_work_sync(work: &priv->monitor_counters_work); |
169 | } |
170 | |