| // SPDX-License-Identifier: GPL-2.0-only |
| /* Huawei HiNIC PCI Express Linux driver |
| * Copyright(c) 2017 Huawei Technologies Co., Ltd |
| */ |
| |
| #include <linux/debugfs.h> |
| #include <linux/device.h> |
| |
| #include "hinic_debugfs.h" |
| |
| static struct dentry *hinic_dbgfs_root; |
| |
| enum sq_dbg_info { |
| GLB_SQ_ID, |
| SQ_PI, |
| SQ_CI, |
| SQ_FI, |
| SQ_MSIX_ENTRY, |
| }; |
| |
| static char *sq_fields[] = {"glb_sq_id", "sq_pi", "sq_ci", "sq_fi", "sq_msix_entry"}; |
| |
| static u64 hinic_dbg_get_sq_info(struct hinic_dev *nic_dev, struct hinic_sq *sq, int idx) |
| { |
| struct hinic_wq *wq = sq->wq; |
| |
| switch (idx) { |
| case GLB_SQ_ID: |
| return nic_dev->hwdev->func_to_io.global_qpn + sq->qid; |
| case SQ_PI: |
| return atomic_read(&wq->prod_idx) & wq->mask; |
| case SQ_CI: |
| return atomic_read(&wq->cons_idx) & wq->mask; |
| case SQ_FI: |
| return be16_to_cpu(*(__be16 *)(sq->hw_ci_addr)) & wq->mask; |
| case SQ_MSIX_ENTRY: |
| return sq->msix_entry; |
| } |
| |
| return 0; |
| } |
| |
| enum rq_dbg_info { |
| GLB_RQ_ID, |
| RQ_HW_PI, |
| RQ_SW_CI, |
| RQ_SW_PI, |
| RQ_MSIX_ENTRY, |
| }; |
| |
| static char *rq_fields[] = {"glb_rq_id", "rq_hw_pi", "rq_sw_ci", "rq_sw_pi", "rq_msix_entry"}; |
| |
| static u64 hinic_dbg_get_rq_info(struct hinic_dev *nic_dev, struct hinic_rq *rq, int idx) |
| { |
| struct hinic_wq *wq = rq->wq; |
| |
| switch (idx) { |
| case GLB_RQ_ID: |
| return nic_dev->hwdev->func_to_io.global_qpn + rq->qid; |
| case RQ_HW_PI: |
| return be16_to_cpu(*(__be16 *)(rq->pi_virt_addr)) & wq->mask; |
| case RQ_SW_CI: |
| return atomic_read(&wq->cons_idx) & wq->mask; |
| case RQ_SW_PI: |
| return atomic_read(&wq->prod_idx) & wq->mask; |
| case RQ_MSIX_ENTRY: |
| return rq->msix_entry; |
| } |
| |
| return 0; |
| } |
| |
| enum func_tbl_info { |
| VALID, |
| RX_MODE, |
| MTU, |
| RQ_DEPTH, |
| QUEUE_NUM, |
| }; |
| |
| static char *func_table_fields[] = {"valid", "rx_mode", "mtu", "rq_depth", "cfg_q_num"}; |
| |
| static int hinic_dbg_get_func_table(struct hinic_dev *nic_dev, int idx) |
| { |
| struct tag_sml_funcfg_tbl *funcfg_table_elem; |
| struct hinic_cmd_lt_rd *read_data; |
| u16 out_size = sizeof(*read_data); |
| int ret = ~0; |
| int err; |
| |
| read_data = kzalloc(sizeof(*read_data), GFP_KERNEL); |
| if (!read_data) |
| return ~0; |
| |
| read_data->node = TBL_ID_FUNC_CFG_SM_NODE; |
| read_data->inst = TBL_ID_FUNC_CFG_SM_INST; |
| read_data->entry_size = HINIC_FUNCTION_CONFIGURE_TABLE_SIZE; |
| read_data->lt_index = HINIC_HWIF_FUNC_IDX(nic_dev->hwdev->hwif); |
| read_data->len = HINIC_FUNCTION_CONFIGURE_TABLE_SIZE; |
| |
| err = hinic_port_msg_cmd(nic_dev->hwdev, HINIC_PORT_CMD_RD_LINE_TBL, read_data, |
| sizeof(*read_data), read_data, &out_size); |
| if (err || out_size != sizeof(*read_data) || read_data->status) { |
| netif_err(nic_dev, drv, nic_dev->netdev, |
| "Failed to get func table, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, read_data->status, out_size); |
| kfree(read_data); |
| return ~0; |
| } |
| |
| funcfg_table_elem = (struct tag_sml_funcfg_tbl *)read_data->data; |
| |
| switch (idx) { |
| case VALID: |
| ret = funcfg_table_elem->dw0.bs.valid; |
| break; |
| case RX_MODE: |
| ret = funcfg_table_elem->dw0.bs.nic_rx_mode; |
| break; |
| case MTU: |
| ret = funcfg_table_elem->dw1.bs.mtu; |
| break; |
| case RQ_DEPTH: |
| ret = funcfg_table_elem->dw13.bs.cfg_rq_depth; |
| break; |
| case QUEUE_NUM: |
| ret = funcfg_table_elem->dw13.bs.cfg_q_num; |
| break; |
| } |
| |
| kfree(read_data); |
| |
| return ret; |
| } |
| |
| static ssize_t hinic_dbg_cmd_read(struct file *filp, char __user *buffer, size_t count, |
| loff_t *ppos) |
| { |
| struct hinic_debug_priv *dbg; |
| char ret_buf[20]; |
| int *desc; |
| u64 out; |
| int ret; |
| |
| desc = filp->private_data; |
| dbg = container_of(desc, struct hinic_debug_priv, field_id[*desc]); |
| |
| switch (dbg->type) { |
| case HINIC_DBG_SQ_INFO: |
| out = hinic_dbg_get_sq_info(dbg->dev, dbg->object, *desc); |
| break; |
| |
| case HINIC_DBG_RQ_INFO: |
| out = hinic_dbg_get_rq_info(dbg->dev, dbg->object, *desc); |
| break; |
| |
| case HINIC_DBG_FUNC_TABLE: |
| out = hinic_dbg_get_func_table(dbg->dev, *desc); |
| break; |
| |
| default: |
| netif_warn(dbg->dev, drv, dbg->dev->netdev, "Invalid hinic debug cmd: %d\n", |
| dbg->type); |
| return -EINVAL; |
| } |
| |
| ret = snprintf(ret_buf, sizeof(ret_buf), "0x%llx\n", out); |
| |
| return simple_read_from_buffer(buffer, count, ppos, ret_buf, ret); |
| } |
| |
| static const struct file_operations hinic_dbg_cmd_fops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = hinic_dbg_cmd_read, |
| }; |
| |
| static int create_dbg_files(struct hinic_dev *dev, enum hinic_dbg_type type, void *data, |
| struct dentry *root, struct hinic_debug_priv **dbg, char **field, |
| int nfile) |
| { |
| struct hinic_debug_priv *tmp; |
| int i; |
| |
| tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); |
| if (!tmp) |
| return -ENOMEM; |
| |
| tmp->dev = dev; |
| tmp->object = data; |
| tmp->type = type; |
| tmp->root = root; |
| |
| for (i = 0; i < nfile; i++) { |
| tmp->field_id[i] = i; |
| debugfs_create_file(field[i], 0400, root, &tmp->field_id[i], &hinic_dbg_cmd_fops); |
| } |
| |
| *dbg = tmp; |
| |
| return 0; |
| } |
| |
| static void rem_dbg_files(struct hinic_debug_priv *dbg) |
| { |
| if (dbg->type != HINIC_DBG_FUNC_TABLE) |
| debugfs_remove_recursive(dbg->root); |
| |
| kfree(dbg); |
| } |
| |
| int hinic_sq_debug_add(struct hinic_dev *dev, u16 sq_id) |
| { |
| struct hinic_sq *sq; |
| struct dentry *root; |
| char sub_dir[16]; |
| |
| sq = dev->txqs[sq_id].sq; |
| |
| sprintf(sub_dir, "0x%x", sq_id); |
| |
| root = debugfs_create_dir(sub_dir, dev->sq_dbgfs); |
| |
| return create_dbg_files(dev, HINIC_DBG_SQ_INFO, sq, root, &sq->dbg, sq_fields, |
| ARRAY_SIZE(sq_fields)); |
| } |
| |
| void hinic_sq_debug_rem(struct hinic_sq *sq) |
| { |
| if (sq->dbg) |
| rem_dbg_files(sq->dbg); |
| } |
| |
| int hinic_rq_debug_add(struct hinic_dev *dev, u16 rq_id) |
| { |
| struct hinic_rq *rq; |
| struct dentry *root; |
| char sub_dir[16]; |
| |
| rq = dev->rxqs[rq_id].rq; |
| |
| sprintf(sub_dir, "0x%x", rq_id); |
| |
| root = debugfs_create_dir(sub_dir, dev->rq_dbgfs); |
| |
| return create_dbg_files(dev, HINIC_DBG_RQ_INFO, rq, root, &rq->dbg, rq_fields, |
| ARRAY_SIZE(rq_fields)); |
| } |
| |
| void hinic_rq_debug_rem(struct hinic_rq *rq) |
| { |
| if (rq->dbg) |
| rem_dbg_files(rq->dbg); |
| } |
| |
| int hinic_func_table_debug_add(struct hinic_dev *dev) |
| { |
| if (HINIC_IS_VF(dev->hwdev->hwif)) |
| return 0; |
| |
| return create_dbg_files(dev, HINIC_DBG_FUNC_TABLE, dev, dev->func_tbl_dbgfs, &dev->dbg, |
| func_table_fields, ARRAY_SIZE(func_table_fields)); |
| } |
| |
| void hinic_func_table_debug_rem(struct hinic_dev *dev) |
| { |
| if (!HINIC_IS_VF(dev->hwdev->hwif) && dev->dbg) |
| rem_dbg_files(dev->dbg); |
| } |
| |
| void hinic_sq_dbgfs_init(struct hinic_dev *nic_dev) |
| { |
| nic_dev->sq_dbgfs = debugfs_create_dir("SQs", nic_dev->dbgfs_root); |
| } |
| |
| void hinic_sq_dbgfs_uninit(struct hinic_dev *nic_dev) |
| { |
| debugfs_remove_recursive(nic_dev->sq_dbgfs); |
| } |
| |
| void hinic_rq_dbgfs_init(struct hinic_dev *nic_dev) |
| { |
| nic_dev->rq_dbgfs = debugfs_create_dir("RQs", nic_dev->dbgfs_root); |
| } |
| |
| void hinic_rq_dbgfs_uninit(struct hinic_dev *nic_dev) |
| { |
| debugfs_remove_recursive(nic_dev->rq_dbgfs); |
| } |
| |
| void hinic_func_tbl_dbgfs_init(struct hinic_dev *nic_dev) |
| { |
| if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) |
| nic_dev->func_tbl_dbgfs = debugfs_create_dir("func_table", nic_dev->dbgfs_root); |
| } |
| |
| void hinic_func_tbl_dbgfs_uninit(struct hinic_dev *nic_dev) |
| { |
| if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) |
| debugfs_remove_recursive(nic_dev->func_tbl_dbgfs); |
| } |
| |
| void hinic_dbg_init(struct hinic_dev *nic_dev) |
| { |
| nic_dev->dbgfs_root = debugfs_create_dir(pci_name(nic_dev->hwdev->hwif->pdev), |
| hinic_dbgfs_root); |
| } |
| |
| void hinic_dbg_uninit(struct hinic_dev *nic_dev) |
| { |
| debugfs_remove_recursive(nic_dev->dbgfs_root); |
| nic_dev->dbgfs_root = NULL; |
| } |
| |
| void hinic_dbg_register_debugfs(const char *debugfs_dir_name) |
| { |
| hinic_dbgfs_root = debugfs_create_dir(debugfs_dir_name, NULL); |
| } |
| |
| void hinic_dbg_unregister_debugfs(void) |
| { |
| debugfs_remove_recursive(hinic_dbgfs_root); |
| hinic_dbgfs_root = NULL; |
| } |