1 /*-
2  * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #include <dev/mlx5/driver.h>
29 #include <dev/mlx5/diagnostics.h>
30 
31 const struct mlx5_core_diagnostics_entry
32 	mlx5_core_pci_diagnostics_table[
33 		MLX5_CORE_PCI_DIAGNOSTICS_NUM] = {
34 	MLX5_CORE_PCI_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY)
35 };
36 
37 const struct mlx5_core_diagnostics_entry
38 	mlx5_core_general_diagnostics_table[
39 		MLX5_CORE_GENERAL_DIAGNOSTICS_NUM] = {
40 	MLX5_CORE_GENERAL_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY)
41 };
42 
43 static int mlx5_core_get_index_of_diag_counter(
44 	const struct mlx5_core_diagnostics_entry *entry,
45 	int size, u16 counter_id)
46 {
47 	int x;
48 
49 	/* check for invalid counter ID */
50 	if (counter_id == 0)
51 		return -1;
52 
53 	/* lookup counter ID in table */
54 	for (x = 0; x != size; x++) {
55 		if (entry[x].counter_id == counter_id)
56 			return x;
57 	}
58 	return -1;
59 }
60 
61 static void mlx5_core_put_diag_counter(
62 	const struct mlx5_core_diagnostics_entry *entry,
63 	u64 *array, int size, u16 counter_id, u64 value)
64 {
65 	int x;
66 
67 	/* check for invalid counter ID */
68 	if (counter_id == 0)
69 		return;
70 
71 	/* lookup counter ID in table */
72 	for (x = 0; x != size; x++) {
73 		if (entry[x].counter_id == counter_id) {
74 			array[x] = value;
75 			break;
76 		}
77 	}
78 }
79 
80 int mlx5_core_set_diagnostics_full(struct mlx5_core_dev *dev,
81 				   u8 enable_pci, u8 enable_general)
82 {
83 	void *diag_params_ctx;
84 	void *in;
85 	int numcounters;
86 	int inlen;
87 	int err;
88 	int x;
89 	int y;
90 
91 	if (MLX5_CAP_GEN(dev, debug) == 0)
92 		return 0;
93 
94 	numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
95 	if (numcounters == 0)
96 		return 0;
97 
98 	inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) +
99 	    MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters;
100 	in = mlx5_vzalloc(inlen);
101 	if (in == NULL)
102 		return -ENOMEM;
103 
104 	diag_params_ctx = MLX5_ADDR_OF(set_diagnostic_params_in, in,
105 				       diagnostic_params_ctx);
106 
107 	MLX5_SET(diagnostic_params_context, diag_params_ctx,
108 		 enable, enable_pci || enable_general);
109 	MLX5_SET(diagnostic_params_context, diag_params_ctx,
110 		 single, 1);
111 	MLX5_SET(diagnostic_params_context, diag_params_ctx,
112 		 on_demand, 1);
113 
114 	/* collect the counters we want to enable */
115 	for (x = y = 0; x != numcounters; x++) {
116 		u16 counter_id =
117 			MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id);
118 		int index = -1;
119 
120 		if (index < 0 && enable_pci != 0) {
121 			/* check if counter ID exists in local table */
122 			index = mlx5_core_get_index_of_diag_counter(
123 			    mlx5_core_pci_diagnostics_table,
124 			    MLX5_CORE_PCI_DIAGNOSTICS_NUM,
125 			    counter_id);
126 		}
127 		if (index < 0 && enable_general != 0) {
128 			/* check if counter ID exists in local table */
129 			index = mlx5_core_get_index_of_diag_counter(
130 			    mlx5_core_general_diagnostics_table,
131 			    MLX5_CORE_GENERAL_DIAGNOSTICS_NUM,
132 			    counter_id);
133 		}
134 		if (index < 0)
135 			continue;
136 
137 		MLX5_SET(diagnostic_params_context,
138 			 diag_params_ctx,
139 			 counter_id[y].counter_id,
140 			 counter_id);
141 		y++;
142 	}
143 
144 	/* recompute input length */
145 	inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) +
146 	    MLX5_ST_SZ_BYTES(diagnostic_counter) * y;
147 
148 	/* set number of counters */
149 	MLX5_SET(diagnostic_params_context, diag_params_ctx,
150 		 num_of_counters, y);
151 
152 	/* execute firmware command */
153 	err = mlx5_set_diagnostic_params(dev, in, inlen);
154 
155 	kvfree(in);
156 
157 	return err;
158 }
159 
160 int mlx5_core_get_diagnostics_full(struct mlx5_core_dev *dev,
161 				   union mlx5_core_pci_diagnostics *pdiag,
162 				   union mlx5_core_general_diagnostics *pgen)
163 {
164 	void *out;
165 	void *in;
166 	int numcounters;
167 	int outlen;
168 	int inlen;
169 	int err;
170 	int x;
171 
172 	if (MLX5_CAP_GEN(dev, debug) == 0)
173 		return 0;
174 
175 	numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
176 	if (numcounters == 0)
177 		return 0;
178 
179 	outlen = MLX5_ST_SZ_BYTES(query_diagnostic_counters_out) +
180 	    MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters;
181 
182 	out = mlx5_vzalloc(outlen);
183 	if (out == NULL)
184 		return -ENOMEM;
185 
186 	err = mlx5_query_diagnostic_counters(dev, 1, 0, out, outlen);
187 	if (err == 0) {
188 		for (x = 0; x != numcounters; x++) {
189 			u16 counter_id = MLX5_GET(
190 			    query_diagnostic_counters_out,
191 			    out, diag_counter[x].counter_id);
192 			u64 counter_value = MLX5_GET64(
193 			    query_diagnostic_counters_out,
194 			    out, diag_counter[x].counter_value_h);
195 
196 			if (pdiag != NULL) {
197 				mlx5_core_put_diag_counter(
198 				    mlx5_core_pci_diagnostics_table,
199 				    pdiag->array,
200 				    MLX5_CORE_PCI_DIAGNOSTICS_NUM,
201 				    counter_id, counter_value);
202 			}
203 			if (pgen != NULL) {
204 				mlx5_core_put_diag_counter(
205 				    mlx5_core_general_diagnostics_table,
206 				    pgen->array,
207 				    MLX5_CORE_GENERAL_DIAGNOSTICS_NUM,
208 				    counter_id, counter_value);
209 			}
210 		}
211 	}
212 	kvfree(out);
213 
214 	if (pdiag != NULL) {
215 		inlen = MLX5_ST_SZ_BYTES(mpcnt_reg);
216 		outlen = MLX5_ST_SZ_BYTES(mpcnt_reg);
217 
218 		in = mlx5_vzalloc(inlen);
219 		if (in == NULL)
220 			return -ENOMEM;
221 
222 		out = mlx5_vzalloc(outlen);
223 		if (out == NULL) {
224 			kvfree(in);
225 			return -ENOMEM;
226 		}
227 		MLX5_SET(mpcnt_reg, in, grp,
228 			 MLX5_PCIE_PERFORMANCE_COUNTERS_GROUP);
229 
230 		err = mlx5_core_access_reg(dev, in, inlen, out, outlen,
231 					   MLX5_REG_MPCNT, 0, 0);
232 		if (err == 0) {
233 			void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out,
234 			    counter_set.pcie_perf_counters);
235 
236 			pdiag->counter.rx_pci_errors =
237 			    MLX5_GET(pcie_perf_counters,
238 				     pcounters, rx_errors);
239 			pdiag->counter.tx_pci_errors =
240 			    MLX5_GET(pcie_perf_counters,
241 				     pcounters, tx_errors);
242 		}
243 		MLX5_SET(mpcnt_reg, in, grp,
244 			 MLX5_PCIE_TIMERS_AND_STATES_COUNTERS_GROUP);
245 
246 		err = mlx5_core_access_reg(dev, in, inlen, out, outlen,
247 		    MLX5_REG_MPCNT, 0, 0);
248 		if (err == 0) {
249 			void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out,
250 			    counter_set.pcie_timers_states);
251 
252 			pdiag->counter.tx_pci_non_fatal_errors =
253 			    MLX5_GET(pcie_timers_states,
254 				     pcounters, non_fatal_err_msg_sent);
255 			pdiag->counter.tx_pci_fatal_errors =
256 			    MLX5_GET(pcie_timers_states,
257 				     pcounters, fatal_err_msg_sent);
258 		}
259 		kvfree(in);
260 		kvfree(out);
261 	}
262 	return 0;
263 }
264 
265 int mlx5_core_supports_diagnostics(struct mlx5_core_dev *dev, u16 counter_id)
266 {
267 	int numcounters;
268 	int x;
269 
270 	if (MLX5_CAP_GEN(dev, debug) == 0)
271 		return 0;
272 
273 	/* check for any counter */
274 	if (counter_id == 0)
275 		return 1;
276 
277 	numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
278 
279 	/* check if counter ID exists in debug capability */
280 	for (x = 0; x != numcounters; x++) {
281 		if (MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id) ==
282 		    counter_id)
283 			return 1;
284 	}
285 	return 0;			/* not supported counter */
286 }
287