1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Huawei HiNIC PCI Express Linux driver |
3 | * Copyright(c) 2017 Huawei Technologies Co., Ltd |
4 | */ |
5 | |
6 | #include <linux/debugfs.h> |
7 | #include <linux/device.h> |
8 | |
9 | #include "hinic_debugfs.h" |
10 | |
11 | static struct dentry *hinic_dbgfs_root; |
12 | |
13 | enum sq_dbg_info { |
14 | GLB_SQ_ID, |
15 | SQ_PI, |
16 | SQ_CI, |
17 | SQ_FI, |
18 | SQ_MSIX_ENTRY, |
19 | }; |
20 | |
21 | static char *sq_fields[] = {"glb_sq_id" , "sq_pi" , "sq_ci" , "sq_fi" , "sq_msix_entry" }; |
22 | |
23 | static u64 hinic_dbg_get_sq_info(struct hinic_dev *nic_dev, struct hinic_sq *sq, int idx) |
24 | { |
25 | struct hinic_wq *wq = sq->wq; |
26 | |
27 | switch (idx) { |
28 | case GLB_SQ_ID: |
29 | return nic_dev->hwdev->func_to_io.global_qpn + sq->qid; |
30 | case SQ_PI: |
31 | return atomic_read(v: &wq->prod_idx) & wq->mask; |
32 | case SQ_CI: |
33 | return atomic_read(v: &wq->cons_idx) & wq->mask; |
34 | case SQ_FI: |
35 | return be16_to_cpu(*(__be16 *)(sq->hw_ci_addr)) & wq->mask; |
36 | case SQ_MSIX_ENTRY: |
37 | return sq->msix_entry; |
38 | } |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | enum rq_dbg_info { |
44 | GLB_RQ_ID, |
45 | RQ_HW_PI, |
46 | RQ_SW_CI, |
47 | RQ_SW_PI, |
48 | RQ_MSIX_ENTRY, |
49 | }; |
50 | |
51 | static char *rq_fields[] = {"glb_rq_id" , "rq_hw_pi" , "rq_sw_ci" , "rq_sw_pi" , "rq_msix_entry" }; |
52 | |
53 | static u64 hinic_dbg_get_rq_info(struct hinic_dev *nic_dev, struct hinic_rq *rq, int idx) |
54 | { |
55 | struct hinic_wq *wq = rq->wq; |
56 | |
57 | switch (idx) { |
58 | case GLB_RQ_ID: |
59 | return nic_dev->hwdev->func_to_io.global_qpn + rq->qid; |
60 | case RQ_HW_PI: |
61 | return be16_to_cpu(*(__be16 *)(rq->pi_virt_addr)) & wq->mask; |
62 | case RQ_SW_CI: |
63 | return atomic_read(v: &wq->cons_idx) & wq->mask; |
64 | case RQ_SW_PI: |
65 | return atomic_read(v: &wq->prod_idx) & wq->mask; |
66 | case RQ_MSIX_ENTRY: |
67 | return rq->msix_entry; |
68 | } |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | enum func_tbl_info { |
74 | VALID, |
75 | RX_MODE, |
76 | MTU, |
77 | RQ_DEPTH, |
78 | QUEUE_NUM, |
79 | }; |
80 | |
81 | static char *func_table_fields[] = {"valid" , "rx_mode" , "mtu" , "rq_depth" , "cfg_q_num" }; |
82 | |
83 | static int hinic_dbg_get_func_table(struct hinic_dev *nic_dev, int idx) |
84 | { |
85 | struct tag_sml_funcfg_tbl *funcfg_table_elem; |
86 | struct hinic_cmd_lt_rd *read_data; |
87 | u16 out_size = sizeof(*read_data); |
88 | int ret = ~0; |
89 | int err; |
90 | |
91 | read_data = kzalloc(size: sizeof(*read_data), GFP_KERNEL); |
92 | if (!read_data) |
93 | return ~0; |
94 | |
95 | read_data->node = TBL_ID_FUNC_CFG_SM_NODE; |
96 | read_data->inst = TBL_ID_FUNC_CFG_SM_INST; |
97 | read_data->entry_size = HINIC_FUNCTION_CONFIGURE_TABLE_SIZE; |
98 | read_data->lt_index = HINIC_HWIF_FUNC_IDX(nic_dev->hwdev->hwif); |
99 | read_data->len = HINIC_FUNCTION_CONFIGURE_TABLE_SIZE; |
100 | |
101 | err = hinic_port_msg_cmd(hwdev: nic_dev->hwdev, cmd: HINIC_PORT_CMD_RD_LINE_TBL, buf_in: read_data, |
102 | in_size: sizeof(*read_data), buf_out: read_data, out_size: &out_size); |
103 | if (err || out_size != sizeof(*read_data) || read_data->status) { |
104 | netif_err(nic_dev, drv, nic_dev->netdev, |
105 | "Failed to get func table, err: %d, status: 0x%x, out size: 0x%x\n" , |
106 | err, read_data->status, out_size); |
107 | kfree(objp: read_data); |
108 | return ~0; |
109 | } |
110 | |
111 | funcfg_table_elem = (struct tag_sml_funcfg_tbl *)read_data->data; |
112 | |
113 | switch (idx) { |
114 | case VALID: |
115 | ret = funcfg_table_elem->dw0.bs.valid; |
116 | break; |
117 | case RX_MODE: |
118 | ret = funcfg_table_elem->dw0.bs.nic_rx_mode; |
119 | break; |
120 | case MTU: |
121 | ret = funcfg_table_elem->dw1.bs.mtu; |
122 | break; |
123 | case RQ_DEPTH: |
124 | ret = funcfg_table_elem->dw13.bs.cfg_rq_depth; |
125 | break; |
126 | case QUEUE_NUM: |
127 | ret = funcfg_table_elem->dw13.bs.cfg_q_num; |
128 | break; |
129 | } |
130 | |
131 | kfree(objp: read_data); |
132 | |
133 | return ret; |
134 | } |
135 | |
136 | static ssize_t hinic_dbg_cmd_read(struct file *filp, char __user *buffer, size_t count, |
137 | loff_t *ppos) |
138 | { |
139 | struct hinic_debug_priv *dbg; |
140 | char ret_buf[20]; |
141 | int *desc; |
142 | u64 out; |
143 | int ret; |
144 | |
145 | desc = filp->private_data; |
146 | dbg = container_of(desc, struct hinic_debug_priv, field_id[*desc]); |
147 | |
148 | switch (dbg->type) { |
149 | case HINIC_DBG_SQ_INFO: |
150 | out = hinic_dbg_get_sq_info(nic_dev: dbg->dev, sq: dbg->object, idx: *desc); |
151 | break; |
152 | |
153 | case HINIC_DBG_RQ_INFO: |
154 | out = hinic_dbg_get_rq_info(nic_dev: dbg->dev, rq: dbg->object, idx: *desc); |
155 | break; |
156 | |
157 | case HINIC_DBG_FUNC_TABLE: |
158 | out = hinic_dbg_get_func_table(nic_dev: dbg->dev, idx: *desc); |
159 | break; |
160 | |
161 | default: |
162 | netif_warn(dbg->dev, drv, dbg->dev->netdev, "Invalid hinic debug cmd: %d\n" , |
163 | dbg->type); |
164 | return -EINVAL; |
165 | } |
166 | |
167 | ret = snprintf(buf: ret_buf, size: sizeof(ret_buf), fmt: "0x%llx\n" , out); |
168 | |
169 | return simple_read_from_buffer(to: buffer, count, ppos, from: ret_buf, available: ret); |
170 | } |
171 | |
172 | static const struct file_operations hinic_dbg_cmd_fops = { |
173 | .owner = THIS_MODULE, |
174 | .open = simple_open, |
175 | .read = hinic_dbg_cmd_read, |
176 | }; |
177 | |
178 | static int create_dbg_files(struct hinic_dev *dev, enum hinic_dbg_type type, void *data, |
179 | struct dentry *root, struct hinic_debug_priv **dbg, char **field, |
180 | int nfile) |
181 | { |
182 | struct hinic_debug_priv *tmp; |
183 | int i; |
184 | |
185 | tmp = kzalloc(size: sizeof(*tmp), GFP_KERNEL); |
186 | if (!tmp) |
187 | return -ENOMEM; |
188 | |
189 | tmp->dev = dev; |
190 | tmp->object = data; |
191 | tmp->type = type; |
192 | tmp->root = root; |
193 | |
194 | for (i = 0; i < nfile; i++) { |
195 | tmp->field_id[i] = i; |
196 | debugfs_create_file(name: field[i], mode: 0400, parent: root, data: &tmp->field_id[i], fops: &hinic_dbg_cmd_fops); |
197 | } |
198 | |
199 | *dbg = tmp; |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static void rem_dbg_files(struct hinic_debug_priv *dbg) |
205 | { |
206 | if (dbg->type != HINIC_DBG_FUNC_TABLE) |
207 | debugfs_remove_recursive(dentry: dbg->root); |
208 | |
209 | kfree(objp: dbg); |
210 | } |
211 | |
212 | int hinic_sq_debug_add(struct hinic_dev *dev, u16 sq_id) |
213 | { |
214 | struct hinic_sq *sq; |
215 | struct dentry *root; |
216 | char sub_dir[16]; |
217 | |
218 | sq = dev->txqs[sq_id].sq; |
219 | |
220 | sprintf(buf: sub_dir, fmt: "0x%x" , sq_id); |
221 | |
222 | root = debugfs_create_dir(name: sub_dir, parent: dev->sq_dbgfs); |
223 | |
224 | return create_dbg_files(dev, type: HINIC_DBG_SQ_INFO, data: sq, root, dbg: &sq->dbg, field: sq_fields, |
225 | ARRAY_SIZE(sq_fields)); |
226 | } |
227 | |
228 | void hinic_sq_debug_rem(struct hinic_sq *sq) |
229 | { |
230 | if (sq->dbg) |
231 | rem_dbg_files(dbg: sq->dbg); |
232 | } |
233 | |
234 | int hinic_rq_debug_add(struct hinic_dev *dev, u16 rq_id) |
235 | { |
236 | struct hinic_rq *rq; |
237 | struct dentry *root; |
238 | char sub_dir[16]; |
239 | |
240 | rq = dev->rxqs[rq_id].rq; |
241 | |
242 | sprintf(buf: sub_dir, fmt: "0x%x" , rq_id); |
243 | |
244 | root = debugfs_create_dir(name: sub_dir, parent: dev->rq_dbgfs); |
245 | |
246 | return create_dbg_files(dev, type: HINIC_DBG_RQ_INFO, data: rq, root, dbg: &rq->dbg, field: rq_fields, |
247 | ARRAY_SIZE(rq_fields)); |
248 | } |
249 | |
250 | void hinic_rq_debug_rem(struct hinic_rq *rq) |
251 | { |
252 | if (rq->dbg) |
253 | rem_dbg_files(dbg: rq->dbg); |
254 | } |
255 | |
256 | int hinic_func_table_debug_add(struct hinic_dev *dev) |
257 | { |
258 | if (HINIC_IS_VF(dev->hwdev->hwif)) |
259 | return 0; |
260 | |
261 | return create_dbg_files(dev, type: HINIC_DBG_FUNC_TABLE, data: dev, root: dev->func_tbl_dbgfs, dbg: &dev->dbg, |
262 | field: func_table_fields, ARRAY_SIZE(func_table_fields)); |
263 | } |
264 | |
265 | void hinic_func_table_debug_rem(struct hinic_dev *dev) |
266 | { |
267 | if (!HINIC_IS_VF(dev->hwdev->hwif) && dev->dbg) |
268 | rem_dbg_files(dbg: dev->dbg); |
269 | } |
270 | |
271 | void hinic_sq_dbgfs_init(struct hinic_dev *nic_dev) |
272 | { |
273 | nic_dev->sq_dbgfs = debugfs_create_dir(name: "SQs" , parent: nic_dev->dbgfs_root); |
274 | } |
275 | |
276 | void hinic_sq_dbgfs_uninit(struct hinic_dev *nic_dev) |
277 | { |
278 | debugfs_remove_recursive(dentry: nic_dev->sq_dbgfs); |
279 | } |
280 | |
281 | void hinic_rq_dbgfs_init(struct hinic_dev *nic_dev) |
282 | { |
283 | nic_dev->rq_dbgfs = debugfs_create_dir(name: "RQs" , parent: nic_dev->dbgfs_root); |
284 | } |
285 | |
286 | void hinic_rq_dbgfs_uninit(struct hinic_dev *nic_dev) |
287 | { |
288 | debugfs_remove_recursive(dentry: nic_dev->rq_dbgfs); |
289 | } |
290 | |
291 | void hinic_func_tbl_dbgfs_init(struct hinic_dev *nic_dev) |
292 | { |
293 | if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) |
294 | nic_dev->func_tbl_dbgfs = debugfs_create_dir(name: "func_table" , parent: nic_dev->dbgfs_root); |
295 | } |
296 | |
297 | void hinic_func_tbl_dbgfs_uninit(struct hinic_dev *nic_dev) |
298 | { |
299 | if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) |
300 | debugfs_remove_recursive(dentry: nic_dev->func_tbl_dbgfs); |
301 | } |
302 | |
303 | void hinic_dbg_init(struct hinic_dev *nic_dev) |
304 | { |
305 | nic_dev->dbgfs_root = debugfs_create_dir(name: pci_name(pdev: nic_dev->hwdev->hwif->pdev), |
306 | parent: hinic_dbgfs_root); |
307 | } |
308 | |
309 | void hinic_dbg_uninit(struct hinic_dev *nic_dev) |
310 | { |
311 | debugfs_remove_recursive(dentry: nic_dev->dbgfs_root); |
312 | nic_dev->dbgfs_root = NULL; |
313 | } |
314 | |
315 | void hinic_dbg_register_debugfs(const char *debugfs_dir_name) |
316 | { |
317 | hinic_dbgfs_root = debugfs_create_dir(name: debugfs_dir_name, NULL); |
318 | } |
319 | |
320 | void hinic_dbg_unregister_debugfs(void) |
321 | { |
322 | debugfs_remove_recursive(dentry: hinic_dbgfs_root); |
323 | hinic_dbgfs_root = NULL; |
324 | } |
325 | |