1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3
4#include "lag.h"
5
6static char *get_str_mode_type(struct mlx5_lag *ldev)
7{
8	switch (ldev->mode) {
9	case MLX5_LAG_MODE_ROCE: return "roce";
10	case MLX5_LAG_MODE_SRIOV: return "switchdev";
11	case MLX5_LAG_MODE_MULTIPATH: return "multipath";
12	case MLX5_LAG_MODE_MPESW: return "multiport_eswitch";
13	default: return "invalid";
14	}
15
16	return NULL;
17}
18
19static int type_show(struct seq_file *file, void *priv)
20{
21	struct mlx5_core_dev *dev = file->private;
22	struct mlx5_lag *ldev;
23	char *mode = NULL;
24
25	ldev = mlx5_lag_dev(dev);
26	mutex_lock(&ldev->lock);
27	if (__mlx5_lag_is_active(ldev))
28		mode = get_str_mode_type(ldev);
29	mutex_unlock(&ldev->lock);
30	if (!mode)
31		return -EINVAL;
32	seq_printf(file, "%s\n", mode);
33
34	return 0;
35}
36
37static int port_sel_mode_show(struct seq_file *file, void *priv)
38{
39	struct mlx5_core_dev *dev = file->private;
40	struct mlx5_lag *ldev;
41	int ret = 0;
42	char *mode;
43
44	ldev = mlx5_lag_dev(dev);
45	mutex_lock(&ldev->lock);
46	if (__mlx5_lag_is_active(ldev))
47		mode = mlx5_get_str_port_sel_mode(ldev->mode, ldev->mode_flags);
48	else
49		ret = -EINVAL;
50	mutex_unlock(&ldev->lock);
51	if (ret)
52		return ret;
53
54	seq_printf(file, "%s\n", mode);
55	return 0;
56}
57
58static int state_show(struct seq_file *file, void *priv)
59{
60	struct mlx5_core_dev *dev = file->private;
61	struct mlx5_lag *ldev;
62	bool active;
63
64	ldev = mlx5_lag_dev(dev);
65	mutex_lock(&ldev->lock);
66	active = __mlx5_lag_is_active(ldev);
67	mutex_unlock(&ldev->lock);
68	seq_printf(file, "%s\n", active ? "active" : "disabled");
69	return 0;
70}
71
72static int flags_show(struct seq_file *file, void *priv)
73{
74	struct mlx5_core_dev *dev = file->private;
75	bool fdb_sel_mode_native;
76	struct mlx5_lag *ldev;
77	bool shared_fdb;
78	bool lag_active;
79
80	ldev = mlx5_lag_dev(dev);
81	mutex_lock(&ldev->lock);
82	lag_active = __mlx5_lag_is_active(ldev);
83	if (!lag_active)
84		goto unlock;
85
86	shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags);
87	fdb_sel_mode_native = test_bit(MLX5_LAG_MODE_FLAG_FDB_SEL_MODE_NATIVE,
88				       &ldev->mode_flags);
89
90unlock:
91	mutex_unlock(&ldev->lock);
92	if (!lag_active)
93		return -EINVAL;
94
95	seq_printf(file, "%s:%s\n", "shared_fdb", shared_fdb ? "on" : "off");
96	seq_printf(file, "%s:%s\n", "fdb_selection_mode",
97		   fdb_sel_mode_native ? "native" : "affinity");
98	return 0;
99}
100
101static int mapping_show(struct seq_file *file, void *priv)
102{
103	struct mlx5_core_dev *dev = file->private;
104	u8 ports[MLX5_MAX_PORTS] = {};
105	struct mlx5_lag *ldev;
106	bool hash = false;
107	bool lag_active;
108	int num_ports;
109	int i;
110
111	ldev = mlx5_lag_dev(dev);
112	mutex_lock(&ldev->lock);
113	lag_active = __mlx5_lag_is_active(ldev);
114	if (lag_active) {
115		if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) {
116			mlx5_infer_tx_enabled(&ldev->tracker, ldev->ports, ports,
117					      &num_ports);
118			hash = true;
119		} else {
120			for (i = 0; i < ldev->ports; i++)
121				ports[i] = ldev->v2p_map[i];
122			num_ports = ldev->ports;
123		}
124	}
125	mutex_unlock(&ldev->lock);
126	if (!lag_active)
127		return -EINVAL;
128
129	for (i = 0; i < num_ports; i++) {
130		if (hash)
131			seq_printf(file, "%d\n", ports[i] + 1);
132		else
133			seq_printf(file, "%d:%d\n", i + 1, ports[i]);
134	}
135
136	return 0;
137}
138
139static int members_show(struct seq_file *file, void *priv)
140{
141	struct mlx5_core_dev *dev = file->private;
142	struct mlx5_lag *ldev;
143	int i;
144
145	ldev = mlx5_lag_dev(dev);
146	mutex_lock(&ldev->lock);
147	for (i = 0; i < ldev->ports; i++) {
148		if (!ldev->pf[i].dev)
149			continue;
150		seq_printf(file, "%s\n", dev_name(ldev->pf[i].dev->device));
151	}
152	mutex_unlock(&ldev->lock);
153
154	return 0;
155}
156
157DEFINE_SHOW_ATTRIBUTE(type);
158DEFINE_SHOW_ATTRIBUTE(port_sel_mode);
159DEFINE_SHOW_ATTRIBUTE(state);
160DEFINE_SHOW_ATTRIBUTE(flags);
161DEFINE_SHOW_ATTRIBUTE(mapping);
162DEFINE_SHOW_ATTRIBUTE(members);
163
164void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev)
165{
166	struct dentry *dbg;
167
168	dbg = debugfs_create_dir("lag", mlx5_debugfs_get_dev_root(dev));
169	dev->priv.dbg.lag_debugfs = dbg;
170
171	debugfs_create_file("type", 0444, dbg, dev, &type_fops);
172	debugfs_create_file("port_sel_mode", 0444, dbg, dev, &port_sel_mode_fops);
173	debugfs_create_file("state", 0444, dbg, dev, &state_fops);
174	debugfs_create_file("flags", 0444, dbg, dev, &flags_fops);
175	debugfs_create_file("mapping", 0444, dbg, dev, &mapping_fops);
176	debugfs_create_file("members", 0444, dbg, dev, &members_fops);
177}
178
179void mlx5_ldev_remove_debugfs(struct dentry *dbg)
180{
181	debugfs_remove_recursive(dbg);
182}
183