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