1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/* Copyright (c) 2019 Mellanox Technologies. */
3
4#include "rsc_dump.h"
5#include "lib/mlx5.h"
6
7#define MLX5_SGMT_TYPE(SGMT) MLX5_SGMT_TYPE_##SGMT
8#define MLX5_SGMT_STR_ASSING(SGMT)[MLX5_SGMT_TYPE(SGMT)] = #SGMT
9static const char *const mlx5_rsc_sgmt_name[] = {
10	MLX5_SGMT_STR_ASSING(HW_CQPC),
11	MLX5_SGMT_STR_ASSING(HW_SQPC),
12	MLX5_SGMT_STR_ASSING(HW_RQPC),
13	MLX5_SGMT_STR_ASSING(FULL_SRQC),
14	MLX5_SGMT_STR_ASSING(FULL_CQC),
15	MLX5_SGMT_STR_ASSING(FULL_EQC),
16	MLX5_SGMT_STR_ASSING(FULL_QPC),
17	MLX5_SGMT_STR_ASSING(SND_BUFF),
18	MLX5_SGMT_STR_ASSING(RCV_BUFF),
19	MLX5_SGMT_STR_ASSING(SRQ_BUFF),
20	MLX5_SGMT_STR_ASSING(CQ_BUFF),
21	MLX5_SGMT_STR_ASSING(EQ_BUFF),
22	MLX5_SGMT_STR_ASSING(SX_SLICE),
23	MLX5_SGMT_STR_ASSING(SX_SLICE_ALL),
24	MLX5_SGMT_STR_ASSING(RDB),
25	MLX5_SGMT_STR_ASSING(RX_SLICE_ALL),
26	MLX5_SGMT_STR_ASSING(PRM_QUERY_QP),
27	MLX5_SGMT_STR_ASSING(PRM_QUERY_CQ),
28	MLX5_SGMT_STR_ASSING(PRM_QUERY_MKEY),
29};
30
31struct mlx5_rsc_dump {
32	u32 pdn;
33	u32 mkey;
34	u32 number_of_menu_items;
35	u16 fw_segment_type[MLX5_SGMT_TYPE_NUM];
36};
37
38struct mlx5_rsc_dump_cmd {
39	u64 mem_size;
40	u8 cmd[MLX5_ST_SZ_BYTES(resource_dump)];
41};
42
43static int mlx5_rsc_dump_sgmt_get_by_name(char *name)
44{
45	int i;
46
47	for (i = 0; i < ARRAY_SIZE(mlx5_rsc_sgmt_name); i++)
48		if (!strcmp(name, mlx5_rsc_sgmt_name[i]))
49			return i;
50
51	return -EINVAL;
52}
53
54#define MLX5_RSC_DUMP_MENU_HEADER_SIZE (MLX5_ST_SZ_BYTES(resource_dump_info_segment) + \
55					MLX5_ST_SZ_BYTES(resource_dump_command_segment) + \
56					MLX5_ST_SZ_BYTES(resource_dump_menu_segment))
57
58static int mlx5_rsc_dump_read_menu_sgmt(struct mlx5_rsc_dump *rsc_dump, struct page *page,
59					int read_size, int start_idx)
60{
61	void *data = page_address(page);
62	enum mlx5_sgmt_type sgmt_idx;
63	int num_of_items;
64	char *sgmt_name;
65	void *member;
66	int size = 0;
67	void *menu;
68	int i;
69
70	if (!start_idx) {
71		menu = MLX5_ADDR_OF(menu_resource_dump_response, data, menu);
72		rsc_dump->number_of_menu_items = MLX5_GET(resource_dump_menu_segment, menu,
73							  num_of_records);
74		size = MLX5_RSC_DUMP_MENU_HEADER_SIZE;
75		data += size;
76	}
77	num_of_items = rsc_dump->number_of_menu_items;
78
79	for (i = 0; start_idx + i < num_of_items; i++) {
80		size += MLX5_ST_SZ_BYTES(resource_dump_menu_record);
81		if (size >= read_size)
82			return start_idx + i;
83
84		member = data + MLX5_ST_SZ_BYTES(resource_dump_menu_record) * i;
85		sgmt_name =  MLX5_ADDR_OF(resource_dump_menu_record, member, segment_name);
86		sgmt_idx = mlx5_rsc_dump_sgmt_get_by_name(sgmt_name);
87		if (sgmt_idx == -EINVAL)
88			continue;
89		rsc_dump->fw_segment_type[sgmt_idx] = MLX5_GET(resource_dump_menu_record,
90							       member, segment_type);
91	}
92	return 0;
93}
94
95static int mlx5_rsc_dump_trigger(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd,
96				 struct page *page)
97{
98	struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump;
99	struct device *ddev = mlx5_core_dma_dev(dev);
100	u32 out_seq_num;
101	u32 in_seq_num;
102	dma_addr_t dma;
103	int err;
104
105	dma = dma_map_page(ddev, page, 0, cmd->mem_size, DMA_FROM_DEVICE);
106	if (unlikely(dma_mapping_error(ddev, dma)))
107		return -ENOMEM;
108
109	in_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num);
110	MLX5_SET(resource_dump, cmd->cmd, mkey, rsc_dump->mkey);
111	MLX5_SET64(resource_dump, cmd->cmd, address, dma);
112
113	err = mlx5_core_access_reg(dev, cmd->cmd, sizeof(cmd->cmd), cmd->cmd,
114				   sizeof(cmd->cmd), MLX5_REG_RESOURCE_DUMP, 0, 1);
115	if (err) {
116		mlx5_core_err(dev, "Resource dump: Failed to access err %d\n", err);
117		goto out;
118	}
119	out_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num);
120	if (out_seq_num && (in_seq_num + 1 != out_seq_num))
121		err = -EIO;
122out:
123	dma_unmap_page(ddev, dma, cmd->mem_size, DMA_FROM_DEVICE);
124	return err;
125}
126
127struct mlx5_rsc_dump_cmd *mlx5_rsc_dump_cmd_create(struct mlx5_core_dev *dev,
128						   struct mlx5_rsc_key *key)
129{
130	struct mlx5_rsc_dump_cmd *cmd;
131	int sgmt_type;
132
133	if (IS_ERR_OR_NULL(dev->rsc_dump))
134		return ERR_PTR(-EOPNOTSUPP);
135
136	sgmt_type = dev->rsc_dump->fw_segment_type[key->rsc];
137	if (!sgmt_type && key->rsc != MLX5_SGMT_TYPE_MENU)
138		return ERR_PTR(-EOPNOTSUPP);
139
140	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
141	if (!cmd) {
142		mlx5_core_err(dev, "Resource dump: Failed to allocate command\n");
143		return ERR_PTR(-ENOMEM);
144	}
145	MLX5_SET(resource_dump, cmd->cmd, segment_type, sgmt_type);
146	MLX5_SET(resource_dump, cmd->cmd, index1, key->index1);
147	MLX5_SET(resource_dump, cmd->cmd, index2, key->index2);
148	MLX5_SET(resource_dump, cmd->cmd, num_of_obj1, key->num_of_obj1);
149	MLX5_SET(resource_dump, cmd->cmd, num_of_obj2, key->num_of_obj2);
150	MLX5_SET(resource_dump, cmd->cmd, size, key->size);
151	cmd->mem_size = key->size;
152	return cmd;
153}
154EXPORT_SYMBOL(mlx5_rsc_dump_cmd_create);
155
156void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd)
157{
158	kfree(cmd);
159}
160EXPORT_SYMBOL(mlx5_rsc_dump_cmd_destroy);
161
162int mlx5_rsc_dump_next(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd,
163		       struct page *page, int *size)
164{
165	bool more_dump;
166	int err;
167
168	if (IS_ERR_OR_NULL(dev->rsc_dump))
169		return -EOPNOTSUPP;
170
171	err = mlx5_rsc_dump_trigger(dev, cmd, page);
172	if (err) {
173		mlx5_core_err(dev, "Resource dump: Failed to trigger dump, %d\n", err);
174		return err;
175	}
176	*size = MLX5_GET(resource_dump, cmd->cmd, size);
177	more_dump = MLX5_GET(resource_dump, cmd->cmd, more_dump);
178
179	return more_dump;
180}
181EXPORT_SYMBOL(mlx5_rsc_dump_next);
182
183#define MLX5_RSC_DUMP_MENU_SEGMENT 0xffff
184static int mlx5_rsc_dump_menu(struct mlx5_core_dev *dev)
185{
186	struct mlx5_rsc_dump_cmd *cmd = NULL;
187	struct mlx5_rsc_key key = {};
188	struct page *page;
189	int start_idx = 0;
190	int size;
191	int err;
192
193	page = alloc_page(GFP_KERNEL);
194	if (!page)
195		return -ENOMEM;
196
197	key.rsc = MLX5_SGMT_TYPE_MENU;
198	key.size = PAGE_SIZE;
199	cmd  = mlx5_rsc_dump_cmd_create(dev, &key);
200	if (IS_ERR(cmd)) {
201		err = PTR_ERR(cmd);
202		goto free_page;
203	}
204	MLX5_SET(resource_dump, cmd->cmd, segment_type, MLX5_RSC_DUMP_MENU_SEGMENT);
205
206	do {
207		err = mlx5_rsc_dump_next(dev, cmd, page, &size);
208		if (err < 0)
209			goto destroy_cmd;
210
211		start_idx = mlx5_rsc_dump_read_menu_sgmt(dev->rsc_dump, page, size, start_idx);
212
213	} while (err > 0);
214
215destroy_cmd:
216	mlx5_rsc_dump_cmd_destroy(cmd);
217free_page:
218	__free_page(page);
219
220	return err;
221}
222
223static int mlx5_rsc_dump_create_mkey(struct mlx5_core_dev *mdev, u32 pdn,
224				     u32 *mkey)
225{
226	int inlen = MLX5_ST_SZ_BYTES(create_mkey_in);
227	void *mkc;
228	u32 *in;
229	int err;
230
231	in = kvzalloc(inlen, GFP_KERNEL);
232	if (!in)
233		return -ENOMEM;
234
235	mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
236	MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
237	MLX5_SET(mkc, mkc, lw, 1);
238	MLX5_SET(mkc, mkc, lr, 1);
239
240	MLX5_SET(mkc, mkc, pd, pdn);
241	MLX5_SET(mkc, mkc, length64, 1);
242	MLX5_SET(mkc, mkc, qpn, 0xffffff);
243
244	err = mlx5_core_create_mkey(mdev, mkey, in, inlen);
245
246	kvfree(in);
247	return err;
248}
249
250struct mlx5_rsc_dump *mlx5_rsc_dump_create(struct mlx5_core_dev *dev)
251{
252	struct mlx5_rsc_dump *rsc_dump;
253
254	if (!MLX5_CAP_DEBUG(dev, resource_dump)) {
255		mlx5_core_dbg(dev, "Resource dump: capability not present\n");
256		return NULL;
257	}
258	rsc_dump = kzalloc(sizeof(*rsc_dump), GFP_KERNEL);
259	if (!rsc_dump)
260		return ERR_PTR(-ENOMEM);
261
262	return rsc_dump;
263}
264
265void mlx5_rsc_dump_destroy(struct mlx5_core_dev *dev)
266{
267	if (IS_ERR_OR_NULL(dev->rsc_dump))
268		return;
269	kfree(dev->rsc_dump);
270}
271
272int mlx5_rsc_dump_init(struct mlx5_core_dev *dev)
273{
274	struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump;
275	int err;
276
277	if (IS_ERR_OR_NULL(dev->rsc_dump))
278		return 0;
279
280	err = mlx5_core_alloc_pd(dev, &rsc_dump->pdn);
281	if (err) {
282		mlx5_core_warn(dev, "Resource dump: Failed to allocate PD %d\n", err);
283		return err;
284	}
285	err = mlx5_rsc_dump_create_mkey(dev, rsc_dump->pdn, &rsc_dump->mkey);
286	if (err) {
287		mlx5_core_err(dev, "Resource dump: Failed to create mkey, %d\n", err);
288		goto free_pd;
289	}
290	err = mlx5_rsc_dump_menu(dev);
291	if (err) {
292		mlx5_core_err(dev, "Resource dump: Failed to read menu, %d\n", err);
293		goto destroy_mkey;
294	}
295	return err;
296
297destroy_mkey:
298	mlx5_core_destroy_mkey(dev, rsc_dump->mkey);
299free_pd:
300	mlx5_core_dealloc_pd(dev, rsc_dump->pdn);
301	return err;
302}
303
304void mlx5_rsc_dump_cleanup(struct mlx5_core_dev *dev)
305{
306	if (IS_ERR_OR_NULL(dev->rsc_dump))
307		return;
308
309	mlx5_core_destroy_mkey(dev, dev->rsc_dump->mkey);
310	mlx5_core_dealloc_pd(dev, dev->rsc_dump->pdn);
311}
312