// SPDX-License-Identifier: GPL-2.0-only /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd */ #include #include #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; } 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; 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) { 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); } 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_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; }