/*- * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_rss.h" #include "opt_ratelimit.h" #include #include #include #include #include const struct mlx5_core_diagnostics_entry mlx5_core_pci_diagnostics_table[ MLX5_CORE_PCI_DIAGNOSTICS_NUM] = { MLX5_CORE_PCI_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY) }; const struct mlx5_core_diagnostics_entry mlx5_core_general_diagnostics_table[ MLX5_CORE_GENERAL_DIAGNOSTICS_NUM] = { MLX5_CORE_GENERAL_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY) }; static int mlx5_core_get_index_of_diag_counter( const struct mlx5_core_diagnostics_entry *entry, int size, u16 counter_id) { int x; /* check for invalid counter ID */ if (counter_id == 0) return -1; /* lookup counter ID in table */ for (x = 0; x != size; x++) { if (entry[x].counter_id == counter_id) return x; } return -1; } static void mlx5_core_put_diag_counter( const struct mlx5_core_diagnostics_entry *entry, u64 *array, int size, u16 counter_id, u64 value) { int x; /* check for invalid counter ID */ if (counter_id == 0) return; /* lookup counter ID in table */ for (x = 0; x != size; x++) { if (entry[x].counter_id == counter_id) { array[x] = value; break; } } } int mlx5_core_set_diagnostics_full(struct mlx5_core_dev *dev, u8 enable_pci, u8 enable_general) { void *diag_params_ctx; void *in; int numcounters; int inlen; int err; int x; int y; if (MLX5_CAP_GEN(dev, debug) == 0) return 0; numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters); if (numcounters == 0) return 0; inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) + MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters; in = mlx5_vzalloc(inlen); if (in == NULL) return -ENOMEM; diag_params_ctx = MLX5_ADDR_OF(set_diagnostic_params_in, in, diagnostic_params_ctx); MLX5_SET(diagnostic_params_context, diag_params_ctx, enable, enable_pci || enable_general); MLX5_SET(diagnostic_params_context, diag_params_ctx, single, 1); MLX5_SET(diagnostic_params_context, diag_params_ctx, on_demand, 1); /* collect the counters we want to enable */ for (x = y = 0; x != numcounters; x++) { u16 counter_id = MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id); int index = -1; if (index < 0 && enable_pci != 0) { /* check if counter ID exists in local table */ index = mlx5_core_get_index_of_diag_counter( mlx5_core_pci_diagnostics_table, MLX5_CORE_PCI_DIAGNOSTICS_NUM, counter_id); } if (index < 0 && enable_general != 0) { /* check if counter ID exists in local table */ index = mlx5_core_get_index_of_diag_counter( mlx5_core_general_diagnostics_table, MLX5_CORE_GENERAL_DIAGNOSTICS_NUM, counter_id); } if (index < 0) continue; MLX5_SET(diagnostic_params_context, diag_params_ctx, counter_id[y].counter_id, counter_id); y++; } /* recompute input length */ inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) + MLX5_ST_SZ_BYTES(diagnostic_counter) * y; /* set number of counters */ MLX5_SET(diagnostic_params_context, diag_params_ctx, num_of_counters, y); /* execute firmware command */ err = mlx5_set_diagnostic_params(dev, in, inlen); kvfree(in); return err; } int mlx5_core_get_diagnostics_full(struct mlx5_core_dev *dev, union mlx5_core_pci_diagnostics *pdiag, union mlx5_core_general_diagnostics *pgen) { void *out; void *in; int numcounters; int outlen; int inlen; int err; int x; if (MLX5_CAP_GEN(dev, debug) == 0) return 0; numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters); if (numcounters == 0) return 0; outlen = MLX5_ST_SZ_BYTES(query_diagnostic_counters_out) + MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters; out = mlx5_vzalloc(outlen); if (out == NULL) return -ENOMEM; err = mlx5_query_diagnostic_counters(dev, 1, 0, out, outlen); if (err == 0) { for (x = 0; x != numcounters; x++) { u16 counter_id = MLX5_GET( query_diagnostic_counters_out, out, diag_counter[x].counter_id); u64 counter_value = MLX5_GET64( query_diagnostic_counters_out, out, diag_counter[x].counter_value_h); if (pdiag != NULL) { mlx5_core_put_diag_counter( mlx5_core_pci_diagnostics_table, pdiag->array, MLX5_CORE_PCI_DIAGNOSTICS_NUM, counter_id, counter_value); } if (pgen != NULL) { mlx5_core_put_diag_counter( mlx5_core_general_diagnostics_table, pgen->array, MLX5_CORE_GENERAL_DIAGNOSTICS_NUM, counter_id, counter_value); } } } kvfree(out); if (pdiag != NULL) { inlen = MLX5_ST_SZ_BYTES(mpcnt_reg); outlen = MLX5_ST_SZ_BYTES(mpcnt_reg); in = mlx5_vzalloc(inlen); if (in == NULL) return -ENOMEM; out = mlx5_vzalloc(outlen); if (out == NULL) { kvfree(in); return -ENOMEM; } MLX5_SET(mpcnt_reg, in, grp, MLX5_PCIE_PERFORMANCE_COUNTERS_GROUP); err = mlx5_core_access_reg(dev, in, inlen, out, outlen, MLX5_REG_MPCNT, 0, 0); if (err == 0) { void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out, counter_set.pcie_perf_counters); pdiag->counter.rx_pci_errors = MLX5_GET(pcie_perf_counters, pcounters, rx_errors); pdiag->counter.tx_pci_errors = MLX5_GET(pcie_perf_counters, pcounters, tx_errors); } MLX5_SET(mpcnt_reg, in, grp, MLX5_PCIE_TIMERS_AND_STATES_COUNTERS_GROUP); err = mlx5_core_access_reg(dev, in, inlen, out, outlen, MLX5_REG_MPCNT, 0, 0); if (err == 0) { void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out, counter_set.pcie_timers_states); pdiag->counter.tx_pci_non_fatal_errors = MLX5_GET(pcie_timers_states, pcounters, non_fatal_err_msg_sent); pdiag->counter.tx_pci_fatal_errors = MLX5_GET(pcie_timers_states, pcounters, fatal_err_msg_sent); } kvfree(in); kvfree(out); } return 0; } int mlx5_core_supports_diagnostics(struct mlx5_core_dev *dev, u16 counter_id) { int numcounters; int x; if (MLX5_CAP_GEN(dev, debug) == 0) return 0; /* check for any counter */ if (counter_id == 0) return 1; numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters); /* check if counter ID exists in debug capability */ for (x = 0; x != numcounters; x++) { if (MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id) == counter_id) return 1; } return 0; /* not supported counter */ } /* * Read the first three bytes of the eeprom in order to get the needed info * for the whole reading. * Byte 0 - Identifier byte * Byte 1 - Revision byte * Byte 2 - Status byte */ int mlx5_get_eeprom_info(struct mlx5_core_dev *dev, struct mlx5_eeprom *eeprom) { u32 data = 0; int size_read = 0; int ret; ret = mlx5_query_module_num(dev, &eeprom->module_num); if (ret) { mlx5_core_err(dev, "Failed query module error=%d\n", ret); return (-ret); } /* Read the first three bytes to get Identifier, Revision and Status */ ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num, eeprom->device_addr, MLX5_EEPROM_INFO_BYTES, eeprom->module_num, &data, &size_read); if (ret) { mlx5_core_err(dev, "Failed query EEPROM module error=0x%x\n", ret); return (-ret); } switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) { case SFF_8024_ID_QSFP: eeprom->type = MLX5_ETH_MODULE_SFF_8436; eeprom->len = MLX5_ETH_MODULE_SFF_8436_LEN; break; case SFF_8024_ID_QSFPPLUS: case SFF_8024_ID_QSFP28: if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 || ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) { eeprom->type = MLX5_ETH_MODULE_SFF_8636; eeprom->len = MLX5_ETH_MODULE_SFF_8636_LEN; } else { eeprom->type = MLX5_ETH_MODULE_SFF_8436; eeprom->len = MLX5_ETH_MODULE_SFF_8436_LEN; } if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0) eeprom->page_valid = 1; break; case SFF_8024_ID_SFP: eeprom->type = MLX5_ETH_MODULE_SFF_8472; eeprom->len = MLX5_ETH_MODULE_SFF_8472_LEN; break; default: mlx5_core_err(dev, "Not recognized cable type = 0x%x(%s)\n", data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK, sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]); return (EINVAL); } return (0); } /* Read both low and high pages of the eeprom */ int mlx5_get_eeprom(struct mlx5_core_dev *dev, struct mlx5_eeprom *ee) { int size_read = 0; int ret; if (ee->len == 0) return (EINVAL); /* Read low page of the eeprom */ while (ee->device_addr < ee->len) { ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr, ee->len - ee->device_addr, ee->module_num, ee->data + (ee->device_addr / 4), &size_read); if (ret) { mlx5_core_err(dev, "Failed reading EEPROM, error = 0x%02x\n", ret); return (-ret); } ee->device_addr += size_read; } /* Read high page of the eeprom */ if (ee->page_valid == 1) { ee->device_addr = MLX5_EEPROM_HIGH_PAGE_OFFSET; ee->page_num = MLX5_EEPROM_HIGH_PAGE; size_read = 0; while (ee->device_addr < MLX5_EEPROM_PAGE_LENGTH) { ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr, MLX5_EEPROM_PAGE_LENGTH - ee->device_addr, ee->module_num, ee->data + (ee->len / 4) + ((ee->device_addr - MLX5_EEPROM_HIGH_PAGE_OFFSET) / 4), &size_read); if (ret) { mlx5_core_err(dev, "Failed reading EEPROM, error = 0x%02x\n", ret); return (-ret); } ee->device_addr += size_read; } } return (0); } /* * Read cable EEPROM module information by first inspecting the first * three bytes to get the initial information for a whole reading. * Information will be printed to dmesg. */ int mlx5_read_eeprom(struct mlx5_core_dev *dev, struct mlx5_eeprom *eeprom) { int error; eeprom->i2c_addr = MLX5_I2C_ADDR_LOW; eeprom->device_addr = 0; eeprom->page_num = MLX5_EEPROM_LOW_PAGE; eeprom->page_valid = 0; /* Read three first bytes to get important info */ error = mlx5_get_eeprom_info(dev, eeprom); if (error) { mlx5_core_err(dev, "Failed reading EEPROM initial information\n"); return (error); } /* * Allocate needed length buffer and additional space for * page 0x03 */ eeprom->data = malloc(eeprom->len + MLX5_EEPROM_PAGE_LENGTH, M_MLX5_EEPROM, M_WAITOK | M_ZERO); /* Read the whole eeprom information */ error = mlx5_get_eeprom(dev, eeprom); if (error) { mlx5_core_err(dev, "Failed reading EEPROM\n"); error = 0; /* * Continue printing partial information in case of * an error */ } free(eeprom->data, M_MLX5_EEPROM); return (error); }