FILE
events.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/events.c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2// Copyright (c) 2018 Mellanox Technologies 3 4#include <linux/mlx5/driver.h> 5 6#include "mlx5_core.h" 7#include "lib/eq.h" 8#include "lib/events.h" 9#include "hwmon.h" 10 11struct mlx5_event_nb { 12 struct mlx5_nb nb; 13 void *ctx; 14}; 15 16/* General events handlers for the low level mlx5_core driver 17 * 18 * Other Major feature specific events such as 19 * clock/eswitch/fpga/FW trace and many others, are handled elsewhere, with 20 * separate notifiers callbacks, specifically by those mlx5 components. 21 */ 22static int any_notifier(struct notifier_block *, unsigned long, void *); 23static int temp_warn(struct notifier_block *, unsigned long, void *); 24static int port_module(struct notifier_block *, unsigned long, void *); 25static int pcie_core(struct notifier_block *, unsigned long, void *); 26 27/* handler which forwards the event to events->fw_nh, driver notifiers */ 28static int forward_event(struct notifier_block *, unsigned long, void *); 29 30static struct mlx5_nb events_nbs_ref[] = { 31 /* Events to be processed by mlx5_core */ 32 {.nb.notifier_call = any_notifier, .event_type = MLX5_EVENT_TYPE_NOTIFY_ANY }, 33 {.nb.notifier_call = temp_warn, .event_type = MLX5_EVENT_TYPE_TEMP_WARN_EVENT }, 34 {.nb.notifier_call = port_module, .event_type = MLX5_EVENT_TYPE_PORT_MODULE_EVENT }, 35 {.nb.notifier_call = pcie_core, .event_type = MLX5_EVENT_TYPE_GENERAL_EVENT }, 36 37 /* Events to be forwarded (as is) to mlx5 core interfaces (mlx5e/mlx5_ib) */ 38 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_PORT_CHANGE }, 39 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_GENERAL_EVENT }, 40 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_OBJECT_CHANGE }, 41 /* QP/WQ resource events to forward */ 42 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_DCT_DRAINED }, 43 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_PATH_MIG }, 44 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_COMM_EST }, 45 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_SQ_DRAINED }, 46 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_SRQ_LAST_WQE }, 47 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_WQ_CATAS_ERROR }, 48 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_PATH_MIG_FAILED }, 49 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR }, 50 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_WQ_ACCESS_ERROR }, 51 /* SRQ events */ 52 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_SRQ_CATAS_ERROR }, 53 {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_SRQ_RQ_LIMIT }, 54}; 55 56struct mlx5_events { 57 struct mlx5_core_dev *dev; 58 struct workqueue_struct *wq; 59 struct mlx5_event_nb notifiers[ARRAY_SIZE(events_nbs_ref)]; 60 /* driver notifier chain for fw events */ 61 struct atomic_notifier_head fw_nh; 62 /* port module events stats */ 63 struct mlx5_pme_stats pme_stats; 64 /*pcie_core*/ 65 struct work_struct pcie_core_work; 66 /* driver notifier chain for sw events */ 67 struct blocking_notifier_head sw_nh; 68}; 69 70static const char *eqe_type_str(u8 type) 71{ 72 switch (type) { 73 case MLX5_EVENT_TYPE_COMP: 74 return "MLX5_EVENT_TYPE_COMP"; 75 case MLX5_EVENT_TYPE_PATH_MIG: 76 return "MLX5_EVENT_TYPE_PATH_MIG"; 77 case MLX5_EVENT_TYPE_COMM_EST: 78 return "MLX5_EVENT_TYPE_COMM_EST"; 79 case MLX5_EVENT_TYPE_SQ_DRAINED: 80 return "MLX5_EVENT_TYPE_SQ_DRAINED"; 81 case MLX5_EVENT_TYPE_SRQ_LAST_WQE: 82 return "MLX5_EVENT_TYPE_SRQ_LAST_WQE"; 83 case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT: 84 return "MLX5_EVENT_TYPE_SRQ_RQ_LIMIT"; 85 case MLX5_EVENT_TYPE_CQ_ERROR: 86 return "MLX5_EVENT_TYPE_CQ_ERROR"; 87 case MLX5_EVENT_TYPE_WQ_CATAS_ERROR: 88 return "MLX5_EVENT_TYPE_WQ_CATAS_ERROR"; 89 case MLX5_EVENT_TYPE_PATH_MIG_FAILED: 90 return "MLX5_EVENT_TYPE_PATH_MIG_FAILED"; 91 case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR: 92 return "MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR"; 93 case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR: 94 return "MLX5_EVENT_TYPE_WQ_ACCESS_ERROR"; 95 case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR: 96 return "MLX5_EVENT_TYPE_SRQ_CATAS_ERROR"; 97 case MLX5_EVENT_TYPE_INTERNAL_ERROR: 98 return "MLX5_EVENT_TYPE_INTERNAL_ERROR"; 99 case MLX5_EVENT_TYPE_PORT_CHANGE: 100 return "MLX5_EVENT_TYPE_PORT_CHANGE"; 101 case MLX5_EVENT_TYPE_GPIO_EVENT: 102 return "MLX5_EVENT_TYPE_GPIO_EVENT"; 103 case MLX5_EVENT_TYPE_PORT_MODULE_EVENT: 104 return "MLX5_EVENT_TYPE_PORT_MODULE_EVENT"; 105 case MLX5_EVENT_TYPE_TEMP_WARN_EVENT: 106 return "MLX5_EVENT_TYPE_TEMP_WARN_EVENT"; 107 case MLX5_EVENT_TYPE_REMOTE_CONFIG: 108 return "MLX5_EVENT_TYPE_REMOTE_CONFIG"; 109 case MLX5_EVENT_TYPE_DB_BF_CONGESTION: 110 return "MLX5_EVENT_TYPE_DB_BF_CONGESTION"; 111 case MLX5_EVENT_TYPE_STALL_EVENT: 112 return "MLX5_EVENT_TYPE_STALL_EVENT"; 113 case MLX5_EVENT_TYPE_CMD: 114 return "MLX5_EVENT_TYPE_CMD"; 115 case MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED: 116 return "MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED"; 117 case MLX5_EVENT_TYPE_VHCA_STATE_CHANGE: 118 return "MLX5_EVENT_TYPE_VHCA_STATE_CHANGE"; 119 case MLX5_EVENT_TYPE_PAGE_REQUEST: 120 return "MLX5_EVENT_TYPE_PAGE_REQUEST"; 121 case MLX5_EVENT_TYPE_PAGE_FAULT: 122 return "MLX5_EVENT_TYPE_PAGE_FAULT"; 123 case MLX5_EVENT_TYPE_PPS_EVENT: 124 return "MLX5_EVENT_TYPE_PPS_EVENT"; 125 case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE: 126 return "MLX5_EVENT_TYPE_NIC_VPORT_CHANGE"; 127 case MLX5_EVENT_TYPE_FPGA_ERROR: 128 return "MLX5_EVENT_TYPE_FPGA_ERROR"; 129 case MLX5_EVENT_TYPE_FPGA_QP_ERROR: 130 return "MLX5_EVENT_TYPE_FPGA_QP_ERROR"; 131 case MLX5_EVENT_TYPE_GENERAL_EVENT: 132 return "MLX5_EVENT_TYPE_GENERAL_EVENT"; 133 case MLX5_EVENT_TYPE_MONITOR_COUNTER: 134 return "MLX5_EVENT_TYPE_MONITOR_COUNTER"; 135 case MLX5_EVENT_TYPE_DEVICE_TRACER: 136 return "MLX5_EVENT_TYPE_DEVICE_TRACER"; 137 case MLX5_EVENT_TYPE_OBJECT_CHANGE: 138 return "MLX5_EVENT_TYPE_OBJECT_CHANGE"; 139 default: 140 return "Unrecognized event"; 141 } 142} 143 144/* handles all FW events, type == eqe->type */ 145static int any_notifier(struct notifier_block *nb, 146 unsigned long type, void *data) 147{ 148 struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb); 149 struct mlx5_events *events = event_nb->ctx; 150 struct mlx5_eqe *eqe = data; 151 152 mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d)\n", 153 eqe_type_str(eqe->type), eqe->sub_type); 154 return NOTIFY_OK; 155} 156 157#if IS_ENABLED(CONFIG_HWMON) 158static void print_sensor_names_in_bit_set(struct mlx5_core_dev *dev, struct mlx5_hwmon *hwmon, 159 u64 bit_set, int bit_set_offset) 160{ 161 unsigned long *bit_set_ptr = (unsigned long *)&bit_set; 162 int num_bits = sizeof(bit_set) * BITS_PER_BYTE; 163 int i; 164 165 for_each_set_bit(i, bit_set_ptr, num_bits) { 166 const char *sensor_name = hwmon_get_sensor_name(hwmon, i + bit_set_offset); 167 168 mlx5_core_warn(dev, "Sensor name[%d]: %s\n", i + bit_set_offset, sensor_name); 169 } 170} 171#endif /* CONFIG_HWMON */ 172 173/* type == MLX5_EVENT_TYPE_TEMP_WARN_EVENT */ 174static int temp_warn(struct notifier_block *nb, unsigned long type, void *data) 175{ 176 struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb); 177 struct mlx5_events *events = event_nb->ctx; 178 struct mlx5_core_dev *dev = events->dev; 179 struct mlx5_eqe *eqe = data; 180 u64 value_lsb; 181 u64 value_msb; 182 183 value_lsb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_lsb); 184 /* bit 1-63 are not supported for NICs, 185 * hence read only bit 0 (asic) from lsb. 186 */ 187 value_lsb &= 0x1; 188 value_msb = be64_to_cpu(eqe->data.temp_warning.sensor_warning_msb); 189 190 if (net_ratelimit()) { 191 mlx5_core_warn(dev, "High temperature on sensors with bit set %#llx %#llx.\n", 192 value_msb, value_lsb); 193#if IS_ENABLED(CONFIG_HWMON) 194 if (dev->hwmon) { 195 print_sensor_names_in_bit_set(dev, dev->hwmon, value_lsb, 0); 196 print_sensor_names_in_bit_set(dev, dev->hwmon, value_msb, 197 sizeof(value_lsb) * BITS_PER_BYTE); 198 } 199#endif 200 } 201 202 return NOTIFY_OK; 203} 204 205/* MLX5_EVENT_TYPE_PORT_MODULE_EVENT */ 206static const char *mlx5_pme_status_to_string(enum port_module_event_status_type status) 207{ 208 switch (status) { 209 case MLX5_MODULE_STATUS_PLUGGED: 210 return "Cable plugged"; 211 case MLX5_MODULE_STATUS_UNPLUGGED: 212 return "Cable unplugged"; 213 case MLX5_MODULE_STATUS_ERROR: 214 return "Cable error"; 215 case MLX5_MODULE_STATUS_DISABLED: 216 return "Cable disabled"; 217 default: 218 return "Unknown status"; 219 } 220} 221 222static const char *mlx5_pme_error_to_string(enum port_module_event_error_type error) 223{ 224 switch (error) { 225 case MLX5_MODULE_EVENT_ERROR_POWER_BUDGET_EXCEEDED: 226 return "Power budget exceeded"; 227 case MLX5_MODULE_EVENT_ERROR_LONG_RANGE_FOR_NON_MLNX: 228 return "Long Range for non MLNX cable"; 229 case MLX5_MODULE_EVENT_ERROR_BUS_STUCK: 230 return "Bus stuck (I2C or data shorted)"; 231 case MLX5_MODULE_EVENT_ERROR_NO_EEPROM_RETRY_TIMEOUT: 232 return "No EEPROM/retry timeout"; 233 case MLX5_MODULE_EVENT_ERROR_ENFORCE_PART_NUMBER_LIST: 234 return "Enforce part number list"; 235 case MLX5_MODULE_EVENT_ERROR_UNKNOWN_IDENTIFIER: 236 return "Unknown identifier"; 237 case MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE: 238 return "High Temperature"; 239 case MLX5_MODULE_EVENT_ERROR_BAD_CABLE: 240 return "Bad or shorted cable/module"; 241 case MLX5_MODULE_EVENT_ERROR_PCIE_POWER_SLOT_EXCEEDED: 242 return "One or more network ports have been powered down due to insufficient/unadvertised power on the PCIe slot"; 243 default: 244 return "Unknown error"; 245 } 246} 247 248/* type == MLX5_EVENT_TYPE_PORT_MODULE_EVENT */ 249static int port_module(struct notifier_block *nb, unsigned long type, void *data) 250{ 251 struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb); 252 struct mlx5_events *events = event_nb->ctx; 253 struct mlx5_eqe *eqe = data; 254 255 enum port_module_event_status_type module_status; 256 enum port_module_event_error_type error_type; 257 struct mlx5_eqe_port_module *module_event_eqe; 258 const char *status_str; 259 u8 module_num; 260 261 module_event_eqe = &eqe->data.port_module; 262 module_status = module_event_eqe->module_status & 263 PORT_MODULE_EVENT_MODULE_STATUS_MASK; 264 error_type = module_event_eqe->error_type & 265 PORT_MODULE_EVENT_ERROR_TYPE_MASK; 266 267 if (module_status < MLX5_MODULE_STATUS_NUM) 268 events->pme_stats.status_counters[module_status]++; 269 270 if (module_status == MLX5_MODULE_STATUS_ERROR) 271 if (error_type < MLX5_MODULE_EVENT_ERROR_NUM) 272 events->pme_stats.error_counters[error_type]++; 273 274 if (!printk_ratelimit()) 275 return NOTIFY_OK; 276 277 module_num = module_event_eqe->module; 278 status_str = mlx5_pme_status_to_string(module_status); 279 if (module_status == MLX5_MODULE_STATUS_ERROR) { 280 const char *error_str = mlx5_pme_error_to_string(error_type); 281 282 mlx5_core_err(events->dev, 283 "Port module event[error]: module %u, %s, %s\n", 284 module_num, status_str, error_str); 285 } else { 286 mlx5_core_info(events->dev, 287 "Port module event: module %u, %s\n", 288 module_num, status_str); 289 } 290 291 return NOTIFY_OK; 292} 293 294enum { 295 MLX5_PCI_POWER_COULD_NOT_BE_READ = 0x0, 296 MLX5_PCI_POWER_SUFFICIENT_REPORTED = 0x1, 297 MLX5_PCI_POWER_INSUFFICIENT_REPORTED = 0x2, 298}; 299 300static void mlx5_pcie_event(struct work_struct *work) 301{ 302 u32 out[MLX5_ST_SZ_DW(mpein_reg)] = {0}; 303 u32 in[MLX5_ST_SZ_DW(mpein_reg)] = {0}; 304 struct mlx5_events *events; 305 struct mlx5_core_dev *dev; 306 u8 power_status; 307 u16 pci_power; 308 309 events = container_of(work, struct mlx5_events, pcie_core_work); 310 dev = events->dev; 311 312 if (!MLX5_CAP_MCAM_FEATURE(dev, pci_status_and_power)) 313 return; 314 315 mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), 316 MLX5_REG_MPEIN, 0, 0); 317 power_status = MLX5_GET(mpein_reg, out, pwr_status); 318 pci_power = MLX5_GET(mpein_reg, out, pci_power); 319 320 switch (power_status) { 321 case MLX5_PCI_POWER_COULD_NOT_BE_READ: 322 mlx5_core_info_rl(dev, 323 "PCIe slot power capability was not advertised.\n"); 324 break; 325 case MLX5_PCI_POWER_INSUFFICIENT_REPORTED: 326 mlx5_core_warn_rl(dev, 327 "Detected insufficient power on the PCIe slot (%uW).\n", 328 pci_power); 329 break; 330 case MLX5_PCI_POWER_SUFFICIENT_REPORTED: 331 mlx5_core_info_rl(dev, 332 "PCIe slot advertised sufficient power (%uW).\n", 333 pci_power); 334 break; 335 } 336} 337 338static int pcie_core(struct notifier_block *nb, unsigned long type, void *data) 339{ 340 struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, 341 struct mlx5_event_nb, 342 nb); 343 struct mlx5_events *events = event_nb->ctx; 344 struct mlx5_eqe *eqe = data; 345 346 switch (eqe->sub_type) { 347 case MLX5_GENERAL_SUBTYPE_PCI_POWER_CHANGE_EVENT: 348 queue_work(events->wq, &events->pcie_core_work); 349 break; 350 default: 351 return NOTIFY_DONE; 352 } 353 354 return NOTIFY_OK; 355} 356 357void mlx5_get_pme_stats(struct mlx5_core_dev *dev, struct mlx5_pme_stats *stats) 358{ 359 *stats = dev->priv.events->pme_stats; 360} 361 362/* forward event as is to registered interfaces (mlx5e/mlx5_ib) */ 363static int forward_event(struct notifier_block *nb, unsigned long event, void *data) 364{ 365 struct mlx5_event_nb *event_nb = mlx5_nb_cof(nb, struct mlx5_event_nb, nb); 366 struct mlx5_events *events = event_nb->ctx; 367 struct mlx5_eqe *eqe = data; 368 369 mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d) forward to interfaces\n", 370 eqe_type_str(eqe->type), eqe->sub_type); 371 atomic_notifier_call_chain(&events->fw_nh, event, data); 372 return NOTIFY_OK; 373} 374 375int mlx5_events_init(struct mlx5_core_dev *dev) 376{ 377 struct mlx5_events *events = kzalloc(sizeof(*events), GFP_KERNEL); 378 379 if (!events) 380 return -ENOMEM; 381 382 ATOMIC_INIT_NOTIFIER_HEAD(&events->fw_nh); 383 events->dev = dev; 384 dev->priv.events = events; 385 events->wq = create_singlethread_workqueue("mlx5_events"); 386 if (!events->wq) { 387 kfree(events); 388 return -ENOMEM; 389 } 390 INIT_WORK(&events->pcie_core_work, mlx5_pcie_event); 391 BLOCKING_INIT_NOTIFIER_HEAD(&events->sw_nh); 392 393 return 0; 394} 395 396void mlx5_events_cleanup(struct mlx5_core_dev *dev) 397{ 398 destroy_workqueue(dev->priv.events->wq); 399 kvfree(dev->priv.events); 400} 401 402void mlx5_events_start(struct mlx5_core_dev *dev) 403{ 404 struct mlx5_events *events = dev->priv.events; 405 int i; 406 407 for (i = 0; i < ARRAY_SIZE(events_nbs_ref); i++) { 408 events->notifiers[i].nb = events_nbs_ref[i]; 409 events->notifiers[i].ctx = events; 410 mlx5_eq_notifier_register(dev, &events->notifiers[i].nb); 411 } 412} 413 414void mlx5_events_stop(struct mlx5_core_dev *dev) 415{ 416 struct mlx5_events *events = dev->priv.events; 417 int i; 418 419 for (i = ARRAY_SIZE(events_nbs_ref) - 1; i >= 0 ; i--) 420 mlx5_eq_notifier_unregister(dev, &events->notifiers[i].nb); 421 flush_workqueue(events->wq); 422} 423 424/* This API is used only for processing and forwarding firmware 425 * events to mlx5 consumer. 426 */ 427int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) 428{ 429 struct mlx5_events *events = dev->priv.events; 430 431 return atomic_notifier_chain_register(&events->fw_nh, nb); 432} 433EXPORT_SYMBOL(mlx5_notifier_register); 434 435int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb) 436{ 437 struct mlx5_events *events = dev->priv.events; 438 439 return atomic_notifier_chain_unregister(&events->fw_nh, nb); 440} 441EXPORT_SYMBOL(mlx5_notifier_unregister); 442 443int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data) 444{ 445 return atomic_notifier_call_chain(&events->fw_nh, event, data); 446} 447 448/* This API is used only for processing and forwarding driver-specific 449 * events to mlx5 consumers. 450 */ 451int mlx5_blocking_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) 452{ 453 struct mlx5_events *events = dev->priv.events; 454 455 return blocking_notifier_chain_register(&events->sw_nh, nb); 456} 457EXPORT_SYMBOL(mlx5_blocking_notifier_register); 458 459int mlx5_blocking_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb) 460{ 461 struct mlx5_events *events = dev->priv.events; 462 463 return blocking_notifier_chain_unregister(&events->sw_nh, nb); 464} 465EXPORT_SYMBOL(mlx5_blocking_notifier_unregister); 466 467int mlx5_blocking_notifier_call_chain(struct mlx5_core_dev *dev, unsigned int event, 468 void *data) 469{ 470 struct mlx5_events *events = dev->priv.events; 471 472 return blocking_notifier_call_chain(&events->sw_nh, event, data); 473}
~/
Read-only file preview.