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