]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/mlx5/mlx5_en/mlx5_en_ethtool.c
MFC r292949:
[FreeBSD/stable/10.git] / sys / dev / mlx5 / mlx5_en / mlx5_en_ethtool.c
1 /*-
2  * Copyright (c) 2015 Mellanox Technologies. 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 "en.h"
29 #include <net/sff8472.h>
30
31 void
32 mlx5e_create_stats(struct sysctl_ctx_list *ctx,
33     struct sysctl_oid_list *parent, const char *buffer,
34     const char **desc, unsigned num, u64 * arg)
35 {
36         struct sysctl_oid *node;
37         unsigned x;
38
39         sysctl_ctx_init(ctx);
40
41         node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO,
42             buffer, CTLFLAG_RD, NULL, "Statistics");
43         if (node == NULL)
44                 return;
45         for (x = 0; x != num; x++) {
46                 SYSCTL_ADD_UQUAD(ctx, SYSCTL_CHILDREN(node), OID_AUTO,
47                     desc[2 * x], CTLFLAG_RD, arg + x, desc[2 * x + 1]);
48         }
49 }
50
51 static int
52 mlx5e_ethtool_handler(SYSCTL_HANDLER_ARGS)
53 {
54         struct mlx5e_priv *priv = arg1;
55         uint64_t value;
56         int was_opened;
57         int error;
58
59         PRIV_LOCK(priv);
60         value = priv->params_ethtool.arg[arg2];
61         if (req != NULL) {
62                 error = sysctl_handle_64(oidp, &value, 0, req);
63                 if (error || req->newptr == NULL ||
64                     value == priv->params_ethtool.arg[arg2])
65                         goto done;
66
67                 /* assign new value */
68                 priv->params_ethtool.arg[arg2] = value;
69         } else {
70                 error = 0;
71         }
72         /* check if device is gone */
73         if (priv->gone) {
74                 error = ENXIO;
75                 goto done;
76         }
77         /* import RX coal time */
78         if (priv->params_ethtool.rx_coalesce_usecs < 1)
79                 priv->params_ethtool.rx_coalesce_usecs = 0;
80         else if (priv->params_ethtool.rx_coalesce_usecs >
81             MLX5E_FLD_MAX(cqc, cq_period)) {
82                 priv->params_ethtool.rx_coalesce_usecs =
83                     MLX5E_FLD_MAX(cqc, cq_period);
84         }
85         priv->params.rx_cq_moderation_usec = priv->params_ethtool.rx_coalesce_usecs;
86
87         /* import RX coal pkts */
88         if (priv->params_ethtool.rx_coalesce_pkts < 1)
89                 priv->params_ethtool.rx_coalesce_pkts = 0;
90         else if (priv->params_ethtool.rx_coalesce_pkts >
91             MLX5E_FLD_MAX(cqc, cq_max_count)) {
92                 priv->params_ethtool.rx_coalesce_pkts =
93                     MLX5E_FLD_MAX(cqc, cq_max_count);
94         }
95         priv->params.rx_cq_moderation_pkts = priv->params_ethtool.rx_coalesce_pkts;
96
97         /* import TX coal time */
98         if (priv->params_ethtool.tx_coalesce_usecs < 1)
99                 priv->params_ethtool.tx_coalesce_usecs = 0;
100         else if (priv->params_ethtool.tx_coalesce_usecs >
101             MLX5E_FLD_MAX(cqc, cq_period)) {
102                 priv->params_ethtool.tx_coalesce_usecs =
103                     MLX5E_FLD_MAX(cqc, cq_period);
104         }
105         priv->params.tx_cq_moderation_usec = priv->params_ethtool.tx_coalesce_usecs;
106
107         /* import TX coal pkts */
108         if (priv->params_ethtool.tx_coalesce_pkts < 1)
109                 priv->params_ethtool.tx_coalesce_pkts = 0;
110         else if (priv->params_ethtool.tx_coalesce_pkts >
111             MLX5E_FLD_MAX(cqc, cq_max_count)) {
112                 priv->params_ethtool.tx_coalesce_pkts = MLX5E_FLD_MAX(cqc, cq_max_count);
113         }
114         priv->params.tx_cq_moderation_pkts = priv->params_ethtool.tx_coalesce_pkts;
115
116         if (&priv->params_ethtool.arg[arg2] == &priv->params_ethtool.rx_pauseframe_control ||
117             &priv->params_ethtool.arg[arg2] == &priv->params_ethtool.tx_pauseframe_control) {
118                 /* range check parameters */
119                 priv->params_ethtool.rx_pauseframe_control =
120                     priv->params_ethtool.rx_pauseframe_control ? 1 : 0;
121                 priv->params_ethtool.tx_pauseframe_control =
122                     priv->params_ethtool.tx_pauseframe_control ? 1 : 0;
123
124                 /* update firmware */
125                 error = -mlx5_set_port_pause(priv->mdev, 1,
126                     priv->params_ethtool.rx_pauseframe_control,
127                     priv->params_ethtool.tx_pauseframe_control);
128                 goto done;
129         }
130
131         was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
132         if (was_opened) {
133                 u64 *xarg = priv->params_ethtool.arg + arg2;
134
135                 if (xarg == &priv->params_ethtool.tx_coalesce_pkts ||
136                     xarg == &priv->params_ethtool.rx_coalesce_pkts ||
137                     xarg == &priv->params_ethtool.tx_coalesce_usecs ||
138                     xarg == &priv->params_ethtool.rx_coalesce_usecs) {
139                         /* avoid downing and upping the network interface */
140                         error = mlx5e_refresh_channel_params(priv);
141                         goto done;
142                 }
143                 mlx5e_close_locked(priv->ifp);
144         }
145         /* import TX queue size */
146         if (priv->params_ethtool.tx_queue_size <
147             (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)) {
148                 priv->params_ethtool.tx_queue_size =
149                     (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE);
150         } else if (priv->params_ethtool.tx_queue_size >
151             priv->params_ethtool.tx_queue_size_max) {
152                 priv->params_ethtool.tx_queue_size =
153                     priv->params_ethtool.tx_queue_size_max;
154         }
155         priv->params.log_sq_size =
156             order_base_2(priv->params_ethtool.tx_queue_size);
157
158         /* import RX queue size */
159         if (priv->params_ethtool.rx_queue_size <
160             (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) {
161                 priv->params_ethtool.rx_queue_size =
162                     (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE);
163         } else if (priv->params_ethtool.rx_queue_size >
164             priv->params_ethtool.rx_queue_size_max) {
165                 priv->params_ethtool.rx_queue_size =
166                     priv->params_ethtool.rx_queue_size_max;
167         }
168         priv->params.log_rq_size =
169             order_base_2(priv->params_ethtool.rx_queue_size);
170
171         priv->params.min_rx_wqes = min_t (u16,
172                   priv->params_ethtool.rx_queue_size - 1,
173                   MLX5E_PARAMS_DEFAULT_MIN_RX_WQES);
174
175         /* import number of channels */
176         if (priv->params_ethtool.channels < 1)
177                 priv->params_ethtool.channels = 1;
178         else if (priv->params_ethtool.channels >
179             (u64) priv->mdev->priv.eq_table.num_comp_vectors) {
180                 priv->params_ethtool.channels =
181                     (u64) priv->mdev->priv.eq_table.num_comp_vectors;
182         }
183         priv->params.num_channels = priv->params_ethtool.channels;
184
185         /* import RX mode */
186         if (priv->params_ethtool.rx_coalesce_mode != 0)
187                 priv->params_ethtool.rx_coalesce_mode = 1;
188         priv->params.rx_cq_moderation_mode = priv->params_ethtool.rx_coalesce_mode;
189
190         /* import TX mode */
191         if (priv->params_ethtool.tx_coalesce_mode != 0)
192                 priv->params_ethtool.tx_coalesce_mode = 1;
193         priv->params.tx_cq_moderation_mode = priv->params_ethtool.tx_coalesce_mode;
194
195         /* we always agree to turn off HW LRO - but not always to turn on */
196         if (priv->params_ethtool.hw_lro) {
197                 if (priv->params_ethtool.hw_lro != 1) {
198                         priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
199                         error = EINVAL;
200                         goto done;
201                 }
202                 if (priv->ifp->if_capenable & IFCAP_LRO)
203                         priv->params.hw_lro_en = !!MLX5_CAP_ETH(priv->mdev, lro_cap);
204                 else {
205                         /* set the correct (0) value to params_ethtool.hw_lro, issue a warning and return error */
206                         priv->params_ethtool.hw_lro = 0;
207                         error = EINVAL;
208                         if_printf(priv->ifp, "Can't set HW_LRO to a device with LRO turned off");
209                         goto done;
210                 }
211         } else {
212                 priv->params.hw_lro_en = false;
213         }
214
215         if (&priv->params_ethtool.arg[arg2] ==
216             &priv->params_ethtool.cqe_zipping) {
217                 if (priv->params_ethtool.cqe_zipping &&
218                     MLX5_CAP_GEN(priv->mdev, cqe_compression)) {
219                         priv->params.cqe_zipping_en = true;
220                         priv->params_ethtool.cqe_zipping = 1;
221                 } else {
222                         priv->params.cqe_zipping_en = false;
223                         priv->params_ethtool.cqe_zipping = 0;
224                 }
225         }
226
227         if (was_opened)
228                 mlx5e_open_locked(priv->ifp);
229 done:
230         PRIV_UNLOCK(priv);
231         return (error);
232 }
233
234 /*
235  * Read the first three bytes of the eeprom in order to get the needed info
236  * for the whole reading.
237  * Byte 0 - Identifier byte
238  * Byte 1 - Revision byte
239  * Byte 2 - Status byte
240  */
241 static int
242 mlx5e_get_eeprom_info(struct mlx5e_priv *priv, struct mlx5e_eeprom *eeprom)
243 {
244         struct mlx5_core_dev *dev = priv->mdev;
245         u32 data = 0;
246         int size_read = 0;
247         int ret;
248
249         ret = mlx5_query_module_num(dev, &eeprom->module_num);
250         if (ret) {
251                 if_printf(priv->ifp, "%s:%d: Failed query module error=%d\n",
252                     __func__, __LINE__, ret);
253                 return (ret);
254         }
255
256         /* Read the first three bytes to get Identifier, Revision and Status */
257         ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num,
258             eeprom->device_addr, MLX5E_EEPROM_INFO_BYTES, eeprom->module_num, &data,
259             &size_read);
260         if (ret) {
261                 if_printf(priv->ifp, "%s:%d: Failed query eeprom module error=0x%x\n",
262                     __func__, __LINE__, ret);
263                 return (ret);
264         }
265
266         switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) {
267         case SFF_8024_ID_QSFP:
268                 eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
269                 eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
270                 break;
271         case SFF_8024_ID_QSFPPLUS:
272         case SFF_8024_ID_QSFP28:
273                 if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 ||
274                     ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) {
275                         eeprom->type = MLX5E_ETH_MODULE_SFF_8636;
276                         eeprom->len = MLX5E_ETH_MODULE_SFF_8636_LEN;
277                 } else {
278                         eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
279                         eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
280                 }
281                 if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0)
282                         eeprom->page_valid = 1;
283                 break;
284         case SFF_8024_ID_SFP:
285                 eeprom->type = MLX5E_ETH_MODULE_SFF_8472;
286                 eeprom->len = MLX5E_ETH_MODULE_SFF_8472_LEN;
287                 break;
288         default:
289                 if_printf(priv->ifp, "%s:%d: Not recognized cable type = 0x%x(%s)\n",
290                     __func__, __LINE__, data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK,
291                     sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]);
292                 return (EINVAL);
293         }
294         return (0);
295 }
296
297 /* Read both low and high pages of the eeprom */
298 static int
299 mlx5e_get_eeprom(struct mlx5e_priv *priv, struct mlx5e_eeprom *ee)
300 {
301         struct mlx5_core_dev *dev = priv->mdev;
302         int size_read = 0;
303         int ret;
304
305         if (ee->len == 0)
306                 return (EINVAL);
307
308         /* Read low page of the eeprom */
309         while (ee->device_addr < ee->len) {
310                 ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr,
311                     ee->len - ee->device_addr, ee->module_num,
312                     ee->data + (ee->device_addr / 4), &size_read);
313                 if (ret) {
314                         if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
315                             "error = 0x%02x\n", __func__, __LINE__, ret);
316                         return (ret);
317                 }
318                 ee->device_addr += size_read;
319         }
320
321         /* Read high page of the eeprom */
322         if (ee->page_valid) {
323                 ee->device_addr = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
324                 ee->page_num = MLX5E_EEPROM_HIGH_PAGE;
325                 size_read = 0;
326                 while (ee->device_addr < MLX5E_EEPROM_PAGE_LENGTH) {
327                         ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num,
328                             ee->device_addr, MLX5E_EEPROM_PAGE_LENGTH - ee->device_addr,
329                             ee->module_num, ee->data + (ee->len / 4) +
330                             ((ee->device_addr - MLX5E_EEPROM_HIGH_PAGE_OFFSET) / 4),
331                             &size_read);
332                         if (ret) {
333                                 if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
334                                     "error = 0x%02x\n", __func__, __LINE__, ret);
335                                 return (ret);
336                         }
337                         ee->device_addr += size_read;
338                 }
339         }
340         return (0);
341 }
342
343 static void
344 mlx5e_print_eeprom(struct mlx5e_eeprom *eeprom)
345 {
346         int row;
347         int index_in_row;
348         int byte_to_write = 0;
349         int line_length = 16;
350
351         printf("\nOffset\t\tValues\n");
352         printf("------\t\t------");
353         while (byte_to_write < eeprom->len) {
354                 printf("\n0x%04X\t\t", byte_to_write);
355                 for (index_in_row = 0; index_in_row < line_length; index_in_row++) {
356                         printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]);
357                         byte_to_write++;
358                 }
359         }
360
361         if (eeprom->page_valid) {
362                 row = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
363                 printf("\n\nUpper Page 0x03\n");
364                 printf("\nOffset\t\tValues\n");
365                 printf("------\t\t------");
366                 while (row < MLX5E_EEPROM_PAGE_LENGTH) {
367                         printf("\n0x%04X\t\t", row);
368                         for (index_in_row = 0; index_in_row < line_length; index_in_row++) {
369                                 printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]);
370                                 byte_to_write++;
371                                 row++;
372                         }
373                 }
374         }
375 }
376
377 /*
378  * Read cable EEPROM module information by first inspecting the first
379  * three bytes to get the initial information for a whole reading.
380  * Information will be printed to dmesg.
381  */
382 static int
383 mlx5e_read_eeprom(SYSCTL_HANDLER_ARGS)
384 {
385         struct mlx5e_priv *priv = arg1;
386         struct mlx5e_eeprom eeprom;
387         int error;
388         int result = 0;
389
390         PRIV_LOCK(priv);
391         error = sysctl_handle_int(oidp, &result, 0, req);
392         if (error || !req->newptr)
393                 goto done;
394
395         /* Check if device is gone */
396         if (priv->gone) {
397                 error = ENXIO;
398                 goto done;
399         }
400
401         if (result == 1) {
402                 eeprom.i2c_addr = MLX5E_I2C_ADDR_LOW;
403                 eeprom.device_addr = 0;
404                 eeprom.page_num = MLX5E_EEPROM_LOW_PAGE;
405                 eeprom.page_valid = 0;
406
407                 /* Read three first bytes to get important info */
408                 error = mlx5e_get_eeprom_info(priv, &eeprom);
409                 if (error) {
410                         if_printf(priv->ifp, "%s:%d: Failed reading eeprom's "
411                             "initial information\n", __func__, __LINE__);
412                         error = 0;
413                         goto done;
414                 }
415                 /*
416                  * Allocate needed length buffer and additional space for
417                  * page 0x03
418                  */
419                 eeprom.data = malloc(eeprom.len + MLX5E_EEPROM_PAGE_LENGTH,
420                     M_MLX5EN, M_WAITOK | M_ZERO);
421
422                 /* Read the whole eeprom information */
423                 error = mlx5e_get_eeprom(priv, &eeprom);
424                 if (error) {
425                         if_printf(priv->ifp, "%s:%d: Failed reading eeprom\n",
426                             __func__, __LINE__);
427                         error = 0;
428                         /*
429                          * Continue printing partial information in case of
430                          * an error
431                          */
432                 }
433                 mlx5e_print_eeprom(&eeprom);
434                 free(eeprom.data, M_MLX5EN);
435         }
436 done:
437         PRIV_UNLOCK(priv);
438         return (error);
439 }
440
441 static const char *mlx5e_params_desc[] = {
442         MLX5E_PARAMS(MLX5E_STATS_DESC)
443 };
444
445 static const char *mlx5e_port_stats_debug_desc[] = {
446         MLX5E_PORT_STATS_DEBUG(MLX5E_STATS_DESC)
447 };
448
449 static int
450 mlx5e_ethtool_debug_stats(SYSCTL_HANDLER_ARGS)
451 {
452         struct mlx5e_priv *priv = arg1;
453         int error;
454         int sys_debug;
455
456         sys_debug = priv->sysctl_debug;
457         error = sysctl_handle_int(oidp, &priv->sysctl_debug, 0, req);
458         if (error || !req->newptr)
459                 return (error);
460         priv->sysctl_debug = !!priv->sysctl_debug;
461         if (sys_debug == priv->sysctl_debug)
462                 return (error);
463         if (priv->sysctl_debug)
464                 mlx5e_create_stats(&priv->stats.port_stats_debug.ctx,
465                     SYSCTL_CHILDREN(priv->sysctl_ifnet), "debug_stats",
466                     mlx5e_port_stats_debug_desc, MLX5E_PORT_STATS_DEBUG_NUM,
467                     priv->stats.port_stats_debug.arg);
468         else
469                 sysctl_ctx_free(&priv->stats.port_stats_debug.ctx);
470         return (error);
471 }
472
473 void
474 mlx5e_create_ethtool(struct mlx5e_priv *priv)
475 {
476         struct sysctl_oid *node;
477         const char *pnameunit;
478         unsigned x;
479
480         /* set some defaults */
481         priv->params_ethtool.tx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE;
482         priv->params_ethtool.rx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
483         priv->params_ethtool.tx_queue_size = 1 << priv->params.log_sq_size;
484         priv->params_ethtool.rx_queue_size = 1 << priv->params.log_rq_size;
485         priv->params_ethtool.channels = priv->params.num_channels;
486         priv->params_ethtool.coalesce_pkts_max = MLX5E_FLD_MAX(cqc, cq_max_count);
487         priv->params_ethtool.coalesce_usecs_max = MLX5E_FLD_MAX(cqc, cq_period);
488         priv->params_ethtool.rx_coalesce_mode = priv->params.rx_cq_moderation_mode;
489         priv->params_ethtool.rx_coalesce_usecs = priv->params.rx_cq_moderation_usec;
490         priv->params_ethtool.rx_coalesce_pkts = priv->params.rx_cq_moderation_pkts;
491         priv->params_ethtool.tx_coalesce_mode = priv->params.tx_cq_moderation_mode;
492         priv->params_ethtool.tx_coalesce_usecs = priv->params.tx_cq_moderation_usec;
493         priv->params_ethtool.tx_coalesce_pkts = priv->params.tx_cq_moderation_pkts;
494         priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
495         priv->params_ethtool.cqe_zipping = priv->params.cqe_zipping_en;
496
497         /* create root node */
498         node = SYSCTL_ADD_NODE(&priv->sysctl_ctx,
499             SYSCTL_CHILDREN(priv->sysctl_ifnet), OID_AUTO,
500             "conf", CTLFLAG_RW, NULL, "Configuration");
501         if (node == NULL)
502                 return;
503         for (x = 0; x != MLX5E_PARAMS_NUM; x++) {
504                 /* check for read-only parameter */
505                 if (strstr(mlx5e_params_desc[2 * x], "_max") != NULL) {
506                         SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
507                             mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RD |
508                             CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
509                             mlx5e_params_desc[2 * x + 1]);
510                 } else {
511 #if (__FreeBSD_version < 1100000)
512                         char path[64];
513 #endif
514                         /*
515                          * NOTE: In FreeBSD-11 and newer the
516                          * CTLFLAG_RWTUN flag will take care of
517                          * loading default sysctl value from the
518                          * kernel environment, if any:
519                          */
520                         SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
521                             mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RWTUN |
522                             CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
523                             mlx5e_params_desc[2 * x + 1]);
524
525 #if (__FreeBSD_version < 1100000)
526                         /* compute path for sysctl */
527                         snprintf(path, sizeof(path), "dev.mce.%d.conf.%s",
528                             device_get_unit(priv->mdev->pdev->dev.bsddev),
529                             mlx5e_params_desc[2 * x]);
530
531                         /* try to fetch tunable, if any */
532                         if (TUNABLE_QUAD_FETCH(path, &priv->params_ethtool.arg[x]))
533                                 mlx5e_ethtool_handler(NULL, priv, x, NULL);
534 #endif
535                 }
536         }
537
538         SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
539             "debug_stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv,
540             0, &mlx5e_ethtool_debug_stats, "I", "Extended debug statistics");
541
542         pnameunit = device_get_nameunit(priv->mdev->pdev->dev.bsddev);
543
544         SYSCTL_ADD_STRING(&priv->sysctl_ctx, SYSCTL_CHILDREN(node),
545             OID_AUTO, "device_name", CTLFLAG_RD,
546             __DECONST(void *, pnameunit), 0,
547             "PCI device name");
548
549         /* EEPROM support */
550         SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, "eeprom_info",
551             CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv, 0,
552             mlx5e_read_eeprom, "I", "EEPROM information");
553 }