| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ |
| |
| #include "lag.h" |
| |
| static char *get_str_mode_type(struct mlx5_lag *ldev) |
| { |
| switch (ldev->mode) { |
| case MLX5_LAG_MODE_ROCE: return "roce"; |
| case MLX5_LAG_MODE_SRIOV: return "switchdev"; |
| case MLX5_LAG_MODE_MULTIPATH: return "multipath"; |
| case MLX5_LAG_MODE_MPESW: return "multiport_eswitch"; |
| default: return "invalid"; |
| } |
| |
| return NULL; |
| } |
| |
| static int type_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| struct mlx5_lag *ldev; |
| char *mode = NULL; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| if (__mlx5_lag_is_active(ldev)) |
| mode = get_str_mode_type(ldev); |
| mutex_unlock(&ldev->lock); |
| if (!mode) |
| return -EINVAL; |
| seq_printf(file, "%s\n", mode); |
| |
| return 0; |
| } |
| |
| static int port_sel_mode_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| struct mlx5_lag *ldev; |
| int ret = 0; |
| char *mode; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| if (__mlx5_lag_is_active(ldev)) |
| mode = mlx5_get_str_port_sel_mode(ldev->mode, ldev->mode_flags); |
| else |
| ret = -EINVAL; |
| mutex_unlock(&ldev->lock); |
| if (ret) |
| return ret; |
| |
| seq_printf(file, "%s\n", mode); |
| return 0; |
| } |
| |
| static int state_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| struct mlx5_lag *ldev; |
| bool active; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| active = __mlx5_lag_is_active(ldev); |
| mutex_unlock(&ldev->lock); |
| seq_printf(file, "%s\n", active ? "active" : "disabled"); |
| return 0; |
| } |
| |
| static int flags_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| bool fdb_sel_mode_native; |
| struct mlx5_lag *ldev; |
| bool shared_fdb; |
| bool lag_active; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| lag_active = __mlx5_lag_is_active(ldev); |
| if (!lag_active) |
| goto unlock; |
| |
| shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); |
| fdb_sel_mode_native = test_bit(MLX5_LAG_MODE_FLAG_FDB_SEL_MODE_NATIVE, |
| &ldev->mode_flags); |
| |
| unlock: |
| mutex_unlock(&ldev->lock); |
| if (!lag_active) |
| return -EINVAL; |
| |
| seq_printf(file, "%s:%s\n", "shared_fdb", shared_fdb ? "on" : "off"); |
| seq_printf(file, "%s:%s\n", "fdb_selection_mode", |
| fdb_sel_mode_native ? "native" : "affinity"); |
| return 0; |
| } |
| |
| static int mapping_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| u8 ports[MLX5_MAX_PORTS] = {}; |
| struct mlx5_lag *ldev; |
| bool hash = false; |
| bool lag_active; |
| int num_ports; |
| int i; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| lag_active = __mlx5_lag_is_active(ldev); |
| if (lag_active) { |
| if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) { |
| mlx5_infer_tx_enabled(&ldev->tracker, ldev->ports, ports, |
| &num_ports); |
| hash = true; |
| } else { |
| for (i = 0; i < ldev->ports; i++) |
| ports[i] = ldev->v2p_map[i]; |
| num_ports = ldev->ports; |
| } |
| } |
| mutex_unlock(&ldev->lock); |
| if (!lag_active) |
| return -EINVAL; |
| |
| for (i = 0; i < num_ports; i++) { |
| if (hash) |
| seq_printf(file, "%d\n", ports[i] + 1); |
| else |
| seq_printf(file, "%d:%d\n", i + 1, ports[i]); |
| } |
| |
| return 0; |
| } |
| |
| static int members_show(struct seq_file *file, void *priv) |
| { |
| struct mlx5_core_dev *dev = file->private; |
| struct mlx5_lag *ldev; |
| int i; |
| |
| ldev = mlx5_lag_dev(dev); |
| mutex_lock(&ldev->lock); |
| for (i = 0; i < ldev->ports; i++) { |
| if (!ldev->pf[i].dev) |
| continue; |
| seq_printf(file, "%s\n", dev_name(ldev->pf[i].dev->device)); |
| } |
| mutex_unlock(&ldev->lock); |
| |
| return 0; |
| } |
| |
| DEFINE_SHOW_ATTRIBUTE(type); |
| DEFINE_SHOW_ATTRIBUTE(port_sel_mode); |
| DEFINE_SHOW_ATTRIBUTE(state); |
| DEFINE_SHOW_ATTRIBUTE(flags); |
| DEFINE_SHOW_ATTRIBUTE(mapping); |
| DEFINE_SHOW_ATTRIBUTE(members); |
| |
| void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev) |
| { |
| struct dentry *dbg; |
| |
| dbg = debugfs_create_dir("lag", mlx5_debugfs_get_dev_root(dev)); |
| dev->priv.dbg.lag_debugfs = dbg; |
| |
| debugfs_create_file("type", 0444, dbg, dev, &type_fops); |
| debugfs_create_file("port_sel_mode", 0444, dbg, dev, &port_sel_mode_fops); |
| debugfs_create_file("state", 0444, dbg, dev, &state_fops); |
| debugfs_create_file("flags", 0444, dbg, dev, &flags_fops); |
| debugfs_create_file("mapping", 0444, dbg, dev, &mapping_fops); |
| debugfs_create_file("members", 0444, dbg, dev, &members_fops); |
| } |
| |
| void mlx5_ldev_remove_debugfs(struct dentry *dbg) |
| { |
| debugfs_remove_recursive(dbg); |
| } |