| 1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| 2 | /* Copyright (c) 2024 NVIDIA Corporation & Affiliates */ |
| 3 | |
| 4 | #include <linux/debugfs.h> |
| 5 | #include <linux/kernel.h> |
| 6 | #include <linux/seq_file.h> |
| 7 | #include <linux/version.h> |
| 8 | #include "internal.h" |
| 9 | |
| 10 | static int |
| 11 | hws_debug_dump_matcher_template_definer(struct seq_file *f, |
| 12 | void *parent_obj, |
| 13 | struct mlx5hws_definer *definer, |
| 14 | enum mlx5hws_debug_res_type type) |
| 15 | { |
| 16 | int i; |
| 17 | |
| 18 | if (!definer) |
| 19 | return 0; |
| 20 | |
| 21 | seq_printf(m: f, fmt: "%d,0x%llx,0x%llx,%d,%d," , |
| 22 | type, |
| 23 | HWS_PTR_TO_ID(definer), |
| 24 | HWS_PTR_TO_ID(parent_obj), |
| 25 | definer->obj_id, |
| 26 | definer->type); |
| 27 | |
| 28 | for (i = 0; i < DW_SELECTORS; i++) |
| 29 | seq_printf(m: f, fmt: "0x%x%s" , definer->dw_selector[i], |
| 30 | (i == DW_SELECTORS - 1) ? "," : "-" ); |
| 31 | |
| 32 | for (i = 0; i < BYTE_SELECTORS; i++) |
| 33 | seq_printf(m: f, fmt: "0x%x%s" , definer->byte_selector[i], |
| 34 | (i == BYTE_SELECTORS - 1) ? "," : "-" ); |
| 35 | |
| 36 | for (i = 0; i < MLX5HWS_JUMBO_TAG_SZ; i++) |
| 37 | seq_printf(m: f, fmt: "%02x" , definer->mask.jumbo[i]); |
| 38 | |
| 39 | seq_puts(m: f, s: "\n" ); |
| 40 | |
| 41 | return 0; |
| 42 | } |
| 43 | |
| 44 | static int |
| 45 | hws_debug_dump_matcher_match_template(struct seq_file *f, struct mlx5hws_matcher *matcher) |
| 46 | { |
| 47 | enum mlx5hws_debug_res_type type; |
| 48 | int i, ret; |
| 49 | |
| 50 | for (i = 0; i < matcher->num_of_mt; i++) { |
| 51 | struct mlx5hws_match_template *mt = &matcher->mt[i]; |
| 52 | |
| 53 | seq_printf(m: f, fmt: "%d,0x%llx,0x%llx,%d,%d,%d\n" , |
| 54 | MLX5HWS_DEBUG_RES_TYPE_MATCHER_MATCH_TEMPLATE, |
| 55 | HWS_PTR_TO_ID(mt), |
| 56 | HWS_PTR_TO_ID(matcher), |
| 57 | mt->fc_sz, |
| 58 | 0, 0); |
| 59 | |
| 60 | type = MLX5HWS_DEBUG_RES_TYPE_MATCHER_TEMPLATE_MATCH_DEFINER; |
| 61 | ret = hws_debug_dump_matcher_template_definer(f, parent_obj: mt, definer: mt->definer, type); |
| 62 | if (ret) |
| 63 | return ret; |
| 64 | } |
| 65 | |
| 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | static int |
| 70 | hws_debug_dump_matcher_action_template(struct seq_file *f, struct mlx5hws_matcher *matcher) |
| 71 | { |
| 72 | enum mlx5hws_action_type action_type; |
| 73 | int i, j; |
| 74 | |
| 75 | for (i = 0; i < matcher->num_of_at; i++) { |
| 76 | struct mlx5hws_action_template *at = &matcher->at[i]; |
| 77 | |
| 78 | seq_printf(m: f, fmt: "%d,0x%llx,0x%llx,%d,%d,%d" , |
| 79 | MLX5HWS_DEBUG_RES_TYPE_MATCHER_ACTION_TEMPLATE, |
| 80 | HWS_PTR_TO_ID(at), |
| 81 | HWS_PTR_TO_ID(matcher), |
| 82 | at->only_term, |
| 83 | at->num_of_action_stes, |
| 84 | at->num_actions); |
| 85 | |
| 86 | for (j = 0; j < at->num_actions; j++) { |
| 87 | action_type = at->action_type_arr[j]; |
| 88 | seq_printf(m: f, fmt: ",%s" , mlx5hws_action_type_to_str(action_type)); |
| 89 | } |
| 90 | |
| 91 | seq_puts(m: f, s: "\n" ); |
| 92 | } |
| 93 | |
| 94 | return 0; |
| 95 | } |
| 96 | |
| 97 | static int |
| 98 | hws_debug_dump_matcher_attr(struct seq_file *f, struct mlx5hws_matcher *matcher) |
| 99 | { |
| 100 | struct mlx5hws_matcher_attr *attr = &matcher->attr; |
| 101 | |
| 102 | seq_printf(m: f, fmt: "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d,-1,-1,%d,%d\n" , |
| 103 | MLX5HWS_DEBUG_RES_TYPE_MATCHER_ATTR, |
| 104 | HWS_PTR_TO_ID(matcher), |
| 105 | attr->priority, |
| 106 | attr->mode, |
| 107 | attr->size[MLX5HWS_MATCHER_SIZE_TYPE_RX].table.sz_row_log, |
| 108 | attr->size[MLX5HWS_MATCHER_SIZE_TYPE_RX].table.sz_col_log, |
| 109 | attr->optimize_using_rule_idx, |
| 110 | attr->optimize_flow_src, |
| 111 | attr->insert_mode, |
| 112 | attr->distribute_mode, |
| 113 | attr->size[MLX5HWS_MATCHER_SIZE_TYPE_TX].table.sz_row_log, |
| 114 | attr->size[MLX5HWS_MATCHER_SIZE_TYPE_TX].table.sz_col_log); |
| 115 | |
| 116 | return 0; |
| 117 | } |
| 118 | |
| 119 | static int hws_debug_dump_matcher(struct seq_file *f, struct mlx5hws_matcher *matcher) |
| 120 | { |
| 121 | enum mlx5hws_table_type tbl_type = matcher->tbl->type; |
| 122 | struct mlx5hws_cmd_ft_query_attr ft_attr = {0}; |
| 123 | u64 icm_addr_0 = 0; |
| 124 | u64 icm_addr_1 = 0; |
| 125 | u32 ste_0_id = -1; |
| 126 | u32 ste_1_id = -1; |
| 127 | int ret; |
| 128 | |
| 129 | seq_printf(m: f, fmt: "%d,0x%llx,0x%llx,%d,%d,0x%llx" , |
| 130 | MLX5HWS_DEBUG_RES_TYPE_MATCHER, |
| 131 | HWS_PTR_TO_ID(matcher), |
| 132 | HWS_PTR_TO_ID(matcher->tbl), |
| 133 | matcher->num_of_mt, |
| 134 | matcher->end_ft_id, |
| 135 | matcher->col_matcher ? HWS_PTR_TO_ID(matcher->col_matcher) : 0); |
| 136 | |
| 137 | ste_0_id = matcher->match_ste.ste_0_base; |
| 138 | if (tbl_type == MLX5HWS_TABLE_TYPE_FDB) |
| 139 | ste_1_id = matcher->match_ste.ste_1_base; |
| 140 | |
| 141 | seq_printf(m: f, fmt: ",%d,%d,%d,%d" , |
| 142 | matcher->match_ste.rtc_0_id, |
| 143 | (int)ste_0_id, |
| 144 | matcher->match_ste.rtc_1_id, |
| 145 | (int)ste_1_id); |
| 146 | |
| 147 | ft_attr.type = matcher->tbl->fw_ft_type; |
| 148 | ret = mlx5hws_cmd_flow_table_query(mdev: matcher->tbl->ctx->mdev, |
| 149 | obj_id: matcher->end_ft_id, |
| 150 | ft_attr: &ft_attr, |
| 151 | icm_addr_0: &icm_addr_0, |
| 152 | icm_addr_1: &icm_addr_1); |
| 153 | if (ret) |
| 154 | return ret; |
| 155 | |
| 156 | seq_printf(m: f, fmt: ",-1,-1,-1,-1,0,0x%llx,0x%llx\n" , |
| 157 | mlx5hws_debug_icm_to_idx(icm_addr: icm_addr_0), |
| 158 | mlx5hws_debug_icm_to_idx(icm_addr: icm_addr_1)); |
| 159 | |
| 160 | ret = hws_debug_dump_matcher_attr(f, matcher); |
| 161 | if (ret) |
| 162 | return ret; |
| 163 | |
| 164 | ret = hws_debug_dump_matcher_match_template(f, matcher); |
| 165 | if (ret) |
| 166 | return ret; |
| 167 | |
| 168 | ret = hws_debug_dump_matcher_action_template(f, matcher); |
| 169 | if (ret) |
| 170 | return ret; |
| 171 | |
| 172 | return 0; |
| 173 | } |
| 174 | |
| 175 | static int hws_debug_dump_table(struct seq_file *f, struct mlx5hws_table *tbl) |
| 176 | { |
| 177 | struct mlx5hws_cmd_ft_query_attr ft_attr = {0}; |
| 178 | struct mlx5hws_matcher *matcher; |
| 179 | u64 local_icm_addr_0 = 0; |
| 180 | u64 local_icm_addr_1 = 0; |
| 181 | u64 icm_addr_0 = 0; |
| 182 | u64 icm_addr_1 = 0; |
| 183 | int ret; |
| 184 | |
| 185 | seq_printf(m: f, fmt: "%d,0x%llx,0x%llx,%d,%d,%d,%d,%d" , |
| 186 | MLX5HWS_DEBUG_RES_TYPE_TABLE, |
| 187 | HWS_PTR_TO_ID(tbl), |
| 188 | HWS_PTR_TO_ID(tbl->ctx), |
| 189 | tbl->ft_id, |
| 190 | MLX5HWS_TABLE_TYPE_BASE + tbl->type, |
| 191 | tbl->fw_ft_type, |
| 192 | tbl->level, |
| 193 | 0); |
| 194 | |
| 195 | ft_attr.type = tbl->fw_ft_type; |
| 196 | ret = mlx5hws_cmd_flow_table_query(mdev: tbl->ctx->mdev, |
| 197 | obj_id: tbl->ft_id, |
| 198 | ft_attr: &ft_attr, |
| 199 | icm_addr_0: &icm_addr_0, |
| 200 | icm_addr_1: &icm_addr_1); |
| 201 | if (ret) |
| 202 | return ret; |
| 203 | |
| 204 | seq_printf(m: f, fmt: ",0x%llx,0x%llx,0x%llx,0x%llx,0x%llx\n" , |
| 205 | mlx5hws_debug_icm_to_idx(icm_addr: icm_addr_0), |
| 206 | mlx5hws_debug_icm_to_idx(icm_addr: icm_addr_1), |
| 207 | mlx5hws_debug_icm_to_idx(icm_addr: local_icm_addr_0), |
| 208 | mlx5hws_debug_icm_to_idx(icm_addr: local_icm_addr_1), |
| 209 | HWS_PTR_TO_ID(tbl->default_miss.miss_tbl)); |
| 210 | |
| 211 | list_for_each_entry(matcher, &tbl->matchers_list, list_node) { |
| 212 | ret = hws_debug_dump_matcher(f, matcher); |
| 213 | if (ret) |
| 214 | return ret; |
| 215 | } |
| 216 | |
| 217 | return 0; |
| 218 | } |
| 219 | |
| 220 | static int |
| 221 | hws_debug_dump_context_send_engine(struct seq_file *f, struct mlx5hws_context *ctx) |
| 222 | { |
| 223 | struct mlx5hws_send_engine *send_queue; |
| 224 | struct mlx5hws_send_ring *send_ring; |
| 225 | struct mlx5hws_send_ring_cq *cq; |
| 226 | struct mlx5hws_send_ring_sq *sq; |
| 227 | int i; |
| 228 | |
| 229 | for (i = 0; i < (int)ctx->queues; i++) { |
| 230 | send_queue = &ctx->send_queue[i]; |
| 231 | seq_printf(m: f, fmt: "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d,%d\n" , |
| 232 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_ENGINE, |
| 233 | HWS_PTR_TO_ID(ctx), |
| 234 | i, |
| 235 | send_queue->used_entries, |
| 236 | send_queue->num_entries, |
| 237 | 1, /* one send ring per queue */ |
| 238 | send_queue->num_entries, |
| 239 | send_queue->err, |
| 240 | send_queue->completed.ci, |
| 241 | send_queue->completed.pi, |
| 242 | send_queue->completed.mask); |
| 243 | |
| 244 | send_ring = &send_queue->send_ring; |
| 245 | cq = &send_ring->send_cq; |
| 246 | sq = &send_ring->send_sq; |
| 247 | |
| 248 | seq_printf(m: f, fmt: "%d,0x%llx,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n" , |
| 249 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT_SEND_RING, |
| 250 | HWS_PTR_TO_ID(ctx), |
| 251 | 0, /* one send ring per send queue */ |
| 252 | i, |
| 253 | cq->mcq.cqn, |
| 254 | 0, |
| 255 | 0, |
| 256 | 0, |
| 257 | 0, |
| 258 | 0, |
| 259 | 0, |
| 260 | cq->mcq.cqe_sz, |
| 261 | sq->sqn, |
| 262 | 0, |
| 263 | 0, |
| 264 | 0); |
| 265 | } |
| 266 | |
| 267 | return 0; |
| 268 | } |
| 269 | |
| 270 | static int hws_debug_dump_context_caps(struct seq_file *f, struct mlx5hws_context *ctx) |
| 271 | { |
| 272 | struct mlx5hws_cmd_query_caps *caps = ctx->caps; |
| 273 | |
| 274 | seq_printf(m: f, fmt: "%d,0x%llx,%s,%d,%d,%d,%d," , |
| 275 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT_CAPS, |
| 276 | HWS_PTR_TO_ID(ctx), |
| 277 | caps->fw_ver, |
| 278 | caps->wqe_based_update, |
| 279 | caps->ste_format, |
| 280 | caps->ste_alloc_log_max, |
| 281 | caps->log_header_modify_argument_max_alloc); |
| 282 | |
| 283 | seq_printf(m: f, fmt: "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%s\n" , |
| 284 | caps->flex_protocols, |
| 285 | caps->rtc_reparse_mode, |
| 286 | caps->rtc_index_mode, |
| 287 | caps->ste_alloc_log_gran, |
| 288 | caps->stc_alloc_log_max, |
| 289 | caps->stc_alloc_log_gran, |
| 290 | caps->rtc_log_depth_max, |
| 291 | caps->format_select_gtpu_dw_0, |
| 292 | caps->format_select_gtpu_dw_1, |
| 293 | caps->format_select_gtpu_dw_2, |
| 294 | caps->format_select_gtpu_ext_dw_0, |
| 295 | caps->nic_ft.max_level, |
| 296 | caps->nic_ft.reparse, |
| 297 | caps->fdb_ft.max_level, |
| 298 | caps->fdb_ft.reparse, |
| 299 | caps->log_header_modify_argument_granularity, |
| 300 | caps->linear_match_definer, |
| 301 | "regc_3" ); |
| 302 | |
| 303 | return 0; |
| 304 | } |
| 305 | |
| 306 | static int hws_debug_dump_context_attr(struct seq_file *f, struct mlx5hws_context *ctx) |
| 307 | { |
| 308 | seq_printf(m: f, fmt: "%u,0x%llx,%d,%zu,%d,%s,%d,%d\n" , |
| 309 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT_ATTR, |
| 310 | HWS_PTR_TO_ID(ctx), |
| 311 | ctx->pd_num, |
| 312 | ctx->queues, |
| 313 | ctx->send_queue->num_entries, |
| 314 | "None" , /* no shared gvmi */ |
| 315 | ctx->caps->vhca_id, |
| 316 | 0xffff); /* no shared gvmi */ |
| 317 | |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | static int hws_debug_dump_context_info(struct seq_file *f, struct mlx5hws_context *ctx) |
| 322 | { |
| 323 | struct mlx5_core_dev *dev = ctx->mdev; |
| 324 | int ret; |
| 325 | |
| 326 | seq_printf(m: f, fmt: "%d,0x%llx,%d,%s,%s.KERNEL_%u_%u_%u\n" , |
| 327 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT, |
| 328 | HWS_PTR_TO_ID(ctx), |
| 329 | ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT, |
| 330 | pci_name(pdev: dev->pdev), |
| 331 | HWS_DEBUG_FORMAT_VERSION, |
| 332 | LINUX_VERSION_MAJOR, |
| 333 | LINUX_VERSION_PATCHLEVEL, |
| 334 | LINUX_VERSION_SUBLEVEL); |
| 335 | |
| 336 | ret = hws_debug_dump_context_attr(f, ctx); |
| 337 | if (ret) |
| 338 | return ret; |
| 339 | |
| 340 | ret = hws_debug_dump_context_caps(f, ctx); |
| 341 | if (ret) |
| 342 | return ret; |
| 343 | |
| 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | static int hws_debug_dump_context_stc_resource(struct seq_file *f, |
| 348 | struct mlx5hws_context *ctx, |
| 349 | struct mlx5hws_pool_resource *resource) |
| 350 | { |
| 351 | u32 tbl_type = MLX5HWS_TABLE_TYPE_BASE + MLX5HWS_TABLE_TYPE_FDB; |
| 352 | |
| 353 | seq_printf(m: f, fmt: "%d,0x%llx,%u,%u\n" , |
| 354 | MLX5HWS_DEBUG_RES_TYPE_CONTEXT_STC, |
| 355 | HWS_PTR_TO_ID(ctx), |
| 356 | tbl_type, |
| 357 | resource->base_id); |
| 358 | |
| 359 | return 0; |
| 360 | } |
| 361 | |
| 362 | static int hws_debug_dump_context_stc(struct seq_file *f, struct mlx5hws_context *ctx) |
| 363 | { |
| 364 | struct mlx5hws_pool *stc_pool = ctx->stc_pool; |
| 365 | int ret; |
| 366 | |
| 367 | if (!stc_pool) |
| 368 | return 0; |
| 369 | |
| 370 | if (stc_pool->resource) { |
| 371 | ret = hws_debug_dump_context_stc_resource(f, ctx, |
| 372 | resource: stc_pool->resource); |
| 373 | if (ret) |
| 374 | return ret; |
| 375 | } |
| 376 | |
| 377 | if (stc_pool->mirror_resource) { |
| 378 | struct mlx5hws_pool_resource *res = stc_pool->mirror_resource; |
| 379 | |
| 380 | ret = hws_debug_dump_context_stc_resource(f, ctx, resource: res); |
| 381 | if (ret) |
| 382 | return ret; |
| 383 | } |
| 384 | |
| 385 | return 0; |
| 386 | } |
| 387 | |
| 388 | static void |
| 389 | hws_debug_dump_action_ste_table(struct seq_file *f, |
| 390 | struct mlx5hws_action_ste_table *action_tbl) |
| 391 | { |
| 392 | int ste_0_id = mlx5hws_pool_get_base_id(pool: action_tbl->pool); |
| 393 | int ste_1_id = mlx5hws_pool_get_base_mirror_id(pool: action_tbl->pool); |
| 394 | |
| 395 | seq_printf(m: f, fmt: "%d,0x%llx,%d,%d,%d,%d\n" , |
| 396 | MLX5HWS_DEBUG_RES_TYPE_ACTION_STE_TABLE, |
| 397 | HWS_PTR_TO_ID(action_tbl), |
| 398 | action_tbl->rtc_0_id, ste_0_id, |
| 399 | action_tbl->rtc_1_id, ste_1_id); |
| 400 | } |
| 401 | |
| 402 | static void hws_debug_dump_action_ste_pool(struct seq_file *f, |
| 403 | struct mlx5hws_action_ste_pool *pool) |
| 404 | { |
| 405 | struct mlx5hws_action_ste_table *action_tbl; |
| 406 | enum mlx5hws_pool_optimize opt; |
| 407 | |
| 408 | mutex_lock(&pool->lock); |
| 409 | for (opt = MLX5HWS_POOL_OPTIMIZE_NONE; opt < MLX5HWS_POOL_OPTIMIZE_MAX; |
| 410 | opt++) { |
| 411 | list_for_each_entry(action_tbl, &pool->elems[opt].available, |
| 412 | list_node) { |
| 413 | hws_debug_dump_action_ste_table(f, action_tbl); |
| 414 | } |
| 415 | } |
| 416 | mutex_unlock(lock: &pool->lock); |
| 417 | } |
| 418 | |
| 419 | static int hws_debug_dump_context(struct seq_file *f, struct mlx5hws_context *ctx) |
| 420 | { |
| 421 | struct mlx5hws_table *tbl; |
| 422 | int ret, i; |
| 423 | |
| 424 | ret = hws_debug_dump_context_info(f, ctx); |
| 425 | if (ret) |
| 426 | return ret; |
| 427 | |
| 428 | ret = hws_debug_dump_context_send_engine(f, ctx); |
| 429 | if (ret) |
| 430 | return ret; |
| 431 | |
| 432 | ret = hws_debug_dump_context_stc(f, ctx); |
| 433 | if (ret) |
| 434 | return ret; |
| 435 | |
| 436 | list_for_each_entry(tbl, &ctx->tbl_list, tbl_list_node) { |
| 437 | ret = hws_debug_dump_table(f, tbl); |
| 438 | if (ret) |
| 439 | return ret; |
| 440 | } |
| 441 | |
| 442 | for (i = 0; i < ctx->queues; i++) |
| 443 | hws_debug_dump_action_ste_pool(f, pool: &ctx->action_ste_pool[i]); |
| 444 | |
| 445 | return 0; |
| 446 | } |
| 447 | |
| 448 | static int |
| 449 | hws_debug_dump(struct seq_file *f, struct mlx5hws_context *ctx) |
| 450 | { |
| 451 | int ret; |
| 452 | |
| 453 | if (!f || !ctx) |
| 454 | return -EINVAL; |
| 455 | |
| 456 | mutex_lock(&ctx->ctrl_lock); |
| 457 | ret = hws_debug_dump_context(f, ctx); |
| 458 | mutex_unlock(lock: &ctx->ctrl_lock); |
| 459 | |
| 460 | return ret; |
| 461 | } |
| 462 | |
| 463 | static int hws_dump_show(struct seq_file *file, void *priv) |
| 464 | { |
| 465 | return hws_debug_dump(f: file, ctx: file->private); |
| 466 | } |
| 467 | DEFINE_SHOW_ATTRIBUTE(hws_dump); |
| 468 | |
| 469 | void mlx5hws_debug_init_dump(struct mlx5hws_context *ctx) |
| 470 | { |
| 471 | struct mlx5_core_dev *dev = ctx->mdev; |
| 472 | char file_name[128]; |
| 473 | |
| 474 | ctx->debug_info.steering_debugfs = |
| 475 | debugfs_create_dir(name: "steering" , parent: mlx5_debugfs_get_dev_root(dev)); |
| 476 | ctx->debug_info.fdb_debugfs = |
| 477 | debugfs_create_dir(name: "fdb" , parent: ctx->debug_info.steering_debugfs); |
| 478 | |
| 479 | sprintf(buf: file_name, fmt: "ctx_%p" , ctx); |
| 480 | debugfs_create_file(file_name, 0444, ctx->debug_info.fdb_debugfs, |
| 481 | ctx, &hws_dump_fops); |
| 482 | } |
| 483 | |
| 484 | void mlx5hws_debug_uninit_dump(struct mlx5hws_context *ctx) |
| 485 | { |
| 486 | debugfs_remove_recursive(dentry: ctx->debug_info.steering_debugfs); |
| 487 | } |
| 488 | |