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
9 static 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 
31 struct 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 
38 struct mlx5_rsc_dump_cmd {
39 	u64 mem_size;
40 	u8 cmd[MLX5_ST_SZ_BYTES(resource_dump)];
41 };
42 
mlx5_rsc_dump_sgmt_get_by_name(char * name)43 static 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 
mlx5_rsc_dump_read_menu_sgmt(struct mlx5_rsc_dump * rsc_dump,struct page * page,int read_size,int start_idx)58 static 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 
mlx5_rsc_dump_trigger(struct mlx5_core_dev * dev,struct mlx5_rsc_dump_cmd * cmd,struct page * page)95 static 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;
122 out:
123 	dma_unmap_page(ddev, dma, cmd->mem_size, DMA_FROM_DEVICE);
124 	return err;
125 }
126 
mlx5_rsc_dump_cmd_create(struct mlx5_core_dev * dev,struct mlx5_rsc_key * key)127 struct 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 }
154 EXPORT_SYMBOL(mlx5_rsc_dump_cmd_create);
155 
mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd * cmd)156 void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd)
157 {
158 	kfree(cmd);
159 }
160 EXPORT_SYMBOL(mlx5_rsc_dump_cmd_destroy);
161 
mlx5_rsc_dump_next(struct mlx5_core_dev * dev,struct mlx5_rsc_dump_cmd * cmd,struct page * page,int * size)162 int 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 }
181 EXPORT_SYMBOL(mlx5_rsc_dump_next);
182 
183 #define MLX5_RSC_DUMP_MENU_SEGMENT 0xffff
mlx5_rsc_dump_menu(struct mlx5_core_dev * dev)184 static 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 
215 destroy_cmd:
216 	mlx5_rsc_dump_cmd_destroy(cmd);
217 free_page:
218 	__free_page(page);
219 
220 	return err;
221 }
222 
mlx5_rsc_dump_create_mkey(struct mlx5_core_dev * mdev,u32 pdn,u32 * mkey)223 static 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 
mlx5_rsc_dump_create(struct mlx5_core_dev * dev)250 struct 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 
mlx5_rsc_dump_destroy(struct mlx5_core_dev * dev)265 void 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 
mlx5_rsc_dump_init(struct mlx5_core_dev * dev)272 int 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 
297 destroy_mkey:
298 	mlx5_core_destroy_mkey(dev, rsc_dump->mkey);
299 free_pd:
300 	mlx5_core_dealloc_pd(dev, rsc_dump->pdn);
301 	return err;
302 }
303 
mlx5_rsc_dump_cleanup(struct mlx5_core_dev * dev)304 void 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