// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include #include "ionic.h" #include "ionic_bus.h" #include "ionic_lif.h" #include "ionic_debugfs.h" #include "kcompat.h" #ifdef CONFIG_DEBUG_FS static struct dentry *ionic_dir; void ionic_debugfs_create(void) { ionic_dir = debugfs_create_dir(IONIC_DRV_NAME, NULL); } void ionic_debugfs_destroy(void) { debugfs_remove_recursive(ionic_dir); } void ionic_debugfs_add_dev(struct ionic *ionic) { ionic->dentry = debugfs_create_dir(ionic_bus_info(ionic), ionic_dir); } void ionic_debugfs_del_dev(struct ionic *ionic) { debugfs_remove_recursive(ionic->dentry); ionic->dentry = NULL; } static int bars_show(struct seq_file *seq, void *v) { struct ionic *ionic = seq->private; struct ionic_dev_bar *bars = ionic->bars; unsigned int i; for (i = 0; i < IONIC_BARS_MAX; i++) if (bars[i].vaddr) seq_printf(seq, "BAR%d: len 0x%lx vaddr %pK bus_addr %pad\n", i, bars[i].len, bars[i].vaddr, &bars[i].bus_addr); return 0; } DEFINE_SHOW_ATTRIBUTE(bars); void ionic_debugfs_add_bars(struct ionic *ionic) { debugfs_create_file("bars", 0400, ionic->dentry, ionic, &bars_fops); } static const struct debugfs_reg32 dev_cmd_regs[] = { { .name = "db", .offset = 0, }, { .name = "done", .offset = 4, }, { .name = "cmd.word[0]", .offset = 8, }, { .name = "cmd.word[1]", .offset = 12, }, { .name = "cmd.word[2]", .offset = 16, }, { .name = "cmd.word[3]", .offset = 20, }, { .name = "cmd.word[4]", .offset = 24, }, { .name = "cmd.word[5]", .offset = 28, }, { .name = "cmd.word[6]", .offset = 32, }, { .name = "cmd.word[7]", .offset = 36, }, { .name = "cmd.word[8]", .offset = 40, }, { .name = "cmd.word[9]", .offset = 44, }, { .name = "cmd.word[10]", .offset = 48, }, { .name = "cmd.word[11]", .offset = 52, }, { .name = "cmd.word[12]", .offset = 56, }, { .name = "cmd.word[13]", .offset = 60, }, { .name = "cmd.word[14]", .offset = 64, }, { .name = "cmd.word[15]", .offset = 68, }, { .name = "comp.word[0]", .offset = 72, }, { .name = "comp.word[1]", .offset = 76, }, { .name = "comp.word[2]", .offset = 80, }, { .name = "comp.word[3]", .offset = 84, }, }; void ionic_debugfs_add_dev_cmd(struct ionic *ionic) { struct debugfs_regset32 *dev_cmd_regset; struct device *dev = ionic->dev; dev_cmd_regset = devm_kzalloc(dev, sizeof(*dev_cmd_regset), GFP_KERNEL); if (!dev_cmd_regset) return; dev_cmd_regset->regs = dev_cmd_regs; dev_cmd_regset->nregs = ARRAY_SIZE(dev_cmd_regs); dev_cmd_regset->base = ionic->idev.dev_cmd_regs; debugfs_create_regset32("dev_cmd", 0400, ionic->dentry, dev_cmd_regset); } static void identity_show_qtype(struct seq_file *seq, const char *name, struct lif_logical_qtype *qtype) { seq_printf(seq, "%s_qtype:\t%d\n", name, qtype->qtype); seq_printf(seq, "%s_count:\t%d\n", name, qtype->qid_count); seq_printf(seq, "%s_base:\t%d\n", name, qtype->qid_base); } static int identity_show(struct seq_file *seq, void *v) { struct ionic *ionic = seq->private; struct identity *ident; struct ionic_dev *idev; ident = &ionic->ident; idev = &ionic->idev; seq_printf(seq, "asic_type: 0x%x\n", idev->dev_info.asic_type); seq_printf(seq, "asic_rev: 0x%x\n", idev->dev_info.asic_rev); seq_printf(seq, "serial_num: %s\n", idev->dev_info.serial_num); seq_printf(seq, "fw_version: %s\n", idev->dev_info.fw_version); seq_printf(seq, "fw_status: 0x%x\n", ioread8(&idev->dev_info_regs->fw_status)); seq_printf(seq, "fw_heartbeat: 0x%x\n", ioread32(&idev->dev_info_regs->fw_heartbeat)); seq_printf(seq, "nlifs: %d\n", ident->dev.nlifs); seq_printf(seq, "nintrs: %d\n", ident->dev.nintrs); seq_printf(seq, "eth_eq_count: %d\n", ident->dev.eq_count); seq_printf(seq, "ndbpgs_per_lif: %d\n", ident->dev.ndbpgs_per_lif); seq_printf(seq, "intr_coal_mult: %d\n", ident->dev.intr_coal_mult); seq_printf(seq, "intr_coal_div: %d\n", ident->dev.intr_coal_div); seq_printf(seq, "max_ucast_filters: %d\n", ident->lif.eth.max_ucast_filters); seq_printf(seq, "max_mcast_filters: %d\n", ident->lif.eth.max_mcast_filters); seq_printf(seq, "rdma_qp_opcodes: %d\n", ident->lif.rdma.qp_opcodes); seq_printf(seq, "rdma_admin_opcodes: %d\n", ident->lif.rdma.admin_opcodes); seq_printf(seq, "rdma_max_stride: %d\n", ident->lif.rdma.max_stride); seq_printf(seq, "rdma_cl_stride: %d\n", ident->lif.rdma.cl_stride); seq_printf(seq, "rdma_pte_stride: %d\n", ident->lif.rdma.pte_stride); seq_printf(seq, "rdma_rrq_stride: %d\n", ident->lif.rdma.rrq_stride); seq_printf(seq, "rdma_rsq_stride: %d\n", ident->lif.rdma.rsq_stride); identity_show_qtype(seq, "rdma_aq", &ident->lif.rdma.aq_qtype); identity_show_qtype(seq, "rdma_sq", &ident->lif.rdma.sq_qtype); identity_show_qtype(seq, "rdma_rq", &ident->lif.rdma.rq_qtype); identity_show_qtype(seq, "rdma_cq", &ident->lif.rdma.cq_qtype); identity_show_qtype(seq, "rdma_eq", &ident->lif.rdma.eq_qtype); return 0; } DEFINE_SHOW_ATTRIBUTE(identity); void ionic_debugfs_add_ident(struct ionic *ionic) { debugfs_create_file("identity", 0400, ionic->dentry, ionic, &identity_fops); } void ionic_debugfs_add_sizes(struct ionic *ionic) { debugfs_create_u32("nlifs", 0400, ionic->dentry, (u32 *)&ionic->ident.dev.nlifs); debugfs_create_u32("nintrs", 0400, ionic->dentry, &ionic->nintrs); debugfs_create_u32("ntxqs_per_lif", 0400, ionic->dentry, (u32 *)&ionic->ident.lif.eth.config.queue_count[IONIC_QTYPE_TXQ]); debugfs_create_u32("nrxqs_per_lif", 0400, ionic->dentry, (u32 *)&ionic->ident.lif.eth.config.queue_count[IONIC_QTYPE_RXQ]); } static int q_tail_show(struct seq_file *seq, void *v) { struct ionic_queue *q = seq->private; seq_printf(seq, "%d\n", q->tail->index); return 0; } DEFINE_SHOW_ATTRIBUTE(q_tail); static int q_head_show(struct seq_file *seq, void *v) { struct ionic_queue *q = seq->private; seq_printf(seq, "%d\n", q->head->index); return 0; } DEFINE_SHOW_ATTRIBUTE(q_head); static int cq_tail_show(struct seq_file *seq, void *v) { struct ionic_cq *cq = seq->private; seq_printf(seq, "%d\n", cq->tail->index); return 0; } DEFINE_SHOW_ATTRIBUTE(cq_tail); static const struct debugfs_reg32 intr_ctrl_regs[] = { { .name = "coal_init", .offset = 0, }, { .name = "mask", .offset = 4, }, { .name = "credits", .offset = 8, }, { .name = "mask_on_assert", .offset = 12, }, { .name = "coal_timer", .offset = 16, }, }; void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq) { struct dentry *qcq_dentry, *q_dentry, *cq_dentry, *intr_dentry; struct ionic_dev *idev = &lif->ionic->idev; struct debugfs_regset32 *intr_ctrl_regset; struct ionic_intr_info *intr = &qcq->intr; struct debugfs_blob_wrapper *desc_blob; struct device *dev = lif->ionic->dev; struct ionic_queue *q = &qcq->q; struct ionic_cq *cq = &qcq->cq; struct dentry *stats_dentry; qcq_dentry = debugfs_create_dir(q->name, lif->dentry); if (IS_ERR_OR_NULL(qcq_dentry)) return; qcq->dentry = qcq_dentry; debugfs_create_x32("total_size", 0400, qcq_dentry, &qcq->total_size); debugfs_create_x64("base_pa", 0400, qcq_dentry, &qcq->base_pa); debugfs_create_bool("armed", 0400, qcq_dentry, &qcq->armed); q_dentry = debugfs_create_dir("q", qcq_dentry); if (IS_ERR_OR_NULL(q_dentry)) return; debugfs_create_u32("index", 0400, q_dentry, &q->index); debugfs_create_x64("base_pa", 0400, q_dentry, &q->base_pa); if (qcq->flags & IONIC_QCQ_F_SG) { debugfs_create_x64("sg_base_pa", 0400, q_dentry, &q->sg_base_pa); debugfs_create_u32("sg_desc_size", 0400, q_dentry, &q->sg_desc_size); } debugfs_create_u32("num_descs", 0400, q_dentry, &q->num_descs); debugfs_create_u32("desc_size", 0400, q_dentry, &q->desc_size); debugfs_create_u32("pid", 0400, q_dentry, &q->pid); debugfs_create_u32("qid", 0400, q_dentry, &q->hw_index); debugfs_create_u32("qtype", 0400, q_dentry, &q->hw_type); debugfs_create_u64("drop", 0400, q_dentry, &q->drop); debugfs_create_u64("stop", 0400, q_dentry, &q->stop); debugfs_create_u64("wake", 0400, q_dentry, &q->wake); debugfs_create_file("tail", 0400, q_dentry, q, &q_tail_fops); debugfs_create_file("head", 0400, q_dentry, q, &q_head_fops); desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL); if (!desc_blob) return; desc_blob->data = q->base; desc_blob->size = (unsigned long)q->num_descs * q->desc_size; debugfs_create_blob("desc_blob", 0400, q_dentry, desc_blob); if (qcq->flags & IONIC_QCQ_F_SG) { desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL); if (!desc_blob) return; desc_blob->data = q->sg_base; desc_blob->size = (unsigned long)q->num_descs * q->sg_desc_size; debugfs_create_blob("sg_desc_blob", 0400, q_dentry, desc_blob); } if (qcq->flags & IONIC_QCQ_F_TX_STATS) { stats_dentry = debugfs_create_dir("tx_stats", q_dentry); if (IS_ERR_OR_NULL(stats_dentry)) return; debugfs_create_u64("dma_map_err", 0400, stats_dentry, &qcq->stats->tx.dma_map_err); debugfs_create_u64("pkts", 0400, stats_dentry, &qcq->stats->tx.pkts); debugfs_create_u64("bytes", 0400, stats_dentry, &qcq->stats->tx.bytes); debugfs_create_u64("clean", 0400, stats_dentry, &qcq->stats->tx.clean); debugfs_create_u64("linearize", 0400, stats_dentry, &qcq->stats->tx.linearize); debugfs_create_u64("no_csum", 0400, stats_dentry, &qcq->stats->tx.no_csum); debugfs_create_u64("csum", 0400, stats_dentry, &qcq->stats->tx.csum); debugfs_create_u64("crc32_csum", 0400, stats_dentry, &qcq->stats->tx.crc32_csum); debugfs_create_u64("tso", 0400, stats_dentry, &qcq->stats->tx.tso); debugfs_create_u64("frags", 0400, stats_dentry, &qcq->stats->tx.frags); } if (qcq->flags & IONIC_QCQ_F_RX_STATS) { stats_dentry = debugfs_create_dir("rx_stats", q_dentry); if (IS_ERR_OR_NULL(stats_dentry)) return; debugfs_create_u64("dma_map_err", 0400, stats_dentry, &qcq->stats->rx.dma_map_err); debugfs_create_u64("alloc_err", 0400, stats_dentry, &qcq->stats->rx.alloc_err); debugfs_create_u64("pkts", 0400, stats_dentry, &qcq->stats->rx.pkts); debugfs_create_u64("bytes", 0400, stats_dentry, &qcq->stats->rx.bytes); debugfs_create_u64("csum_none", 0400, stats_dentry, &qcq->stats->rx.csum_none); debugfs_create_u64("csum_complete", 0400, stats_dentry, &qcq->stats->rx.csum_complete); debugfs_create_u64("csum_error", 0400, stats_dentry, &qcq->stats->rx.csum_error); } cq_dentry = debugfs_create_dir("cq", qcq_dentry); if (IS_ERR_OR_NULL(cq_dentry)) return; debugfs_create_x64("base_pa", 0400, cq_dentry, &cq->base_pa); debugfs_create_u32("num_descs", 0400, cq_dentry, &cq->num_descs); debugfs_create_u32("desc_size", 0400, cq_dentry, &cq->desc_size); debugfs_create_bool("done_color", 0400, cq_dentry, &cq->done_color); debugfs_create_file("tail", 0400, cq_dentry, cq, &cq_tail_fops); desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL); if (!desc_blob) return; desc_blob->data = cq->base; desc_blob->size = (unsigned long)cq->num_descs * cq->desc_size; debugfs_create_blob("desc_blob", 0400, cq_dentry, desc_blob); if (qcq->flags & IONIC_QCQ_F_INTR) { intr_dentry = debugfs_create_dir("intr", qcq_dentry); if (IS_ERR_OR_NULL(intr_dentry)) return; debugfs_create_u32("index", 0400, intr_dentry, &intr->index); debugfs_create_u32("vector", 0400, intr_dentry, &intr->vector); intr_ctrl_regset = devm_kzalloc(dev, sizeof(*intr_ctrl_regset), GFP_KERNEL); if (!intr_ctrl_regset) return; intr_ctrl_regset->regs = intr_ctrl_regs; intr_ctrl_regset->nregs = ARRAY_SIZE(intr_ctrl_regs); intr_ctrl_regset->base = &idev->intr_ctrl[intr->index]; debugfs_create_regset32("intr_ctrl", 0400, intr_dentry, intr_ctrl_regset); } if (qcq->flags & IONIC_QCQ_F_NOTIFYQ) { stats_dentry = debugfs_create_dir("notifyblock", qcq_dentry); if (IS_ERR_OR_NULL(stats_dentry)) return; debugfs_create_u64("eid", 0400, stats_dentry, (u64 *)&lif->info->status.eid); debugfs_create_u16("link_status", 0400, stats_dentry, (u16 *)&lif->info->status.link_status); debugfs_create_u32("link_speed", 0400, stats_dentry, (u32 *)&lif->info->status.link_speed); debugfs_create_u16("link_down_count", 0400, stats_dentry, (u16 *)&lif->info->status.link_down_count); } } static int netdev_show(struct seq_file *seq, void *v) { struct net_device *netdev = seq->private; seq_printf(seq, "%s\n", netdev->name); return 0; } DEFINE_SHOW_ATTRIBUTE(netdev); static int lif_identity_show(struct seq_file *seq, void *v) { union lif_identity *lid = seq->private; seq_printf(seq, "capabilities: 0x%llx\n", lid->capabilities); seq_printf(seq, "eth-version: 0x%x\n", lid->eth.version); seq_printf(seq, "max_ucast_filters: %d\n", lid->eth.max_ucast_filters); seq_printf(seq, "max_mcast_filters: %d\n", lid->eth.max_mcast_filters); seq_printf(seq, "rss_ind_tbl_sz: %d\n", lid->eth.rss_ind_tbl_sz); seq_printf(seq, "min_frame_size: %d\n", lid->eth.min_frame_size); seq_printf(seq, "max_frame_size: %d\n", lid->eth.max_frame_size); seq_printf(seq, "state: %d\n", lid->eth.config.state); seq_printf(seq, "name: \"%s\"\n", lid->eth.config.name); seq_printf(seq, "mtu: %d\n", lid->eth.config.mtu); seq_printf(seq, "mac: %pM\n", lid->eth.config.mac); seq_printf(seq, "features: 0x%08llx\n", lid->eth.config.features); seq_printf(seq, "adminq-count: %d\n", lid->eth.config.queue_count[IONIC_QTYPE_ADMINQ]); seq_printf(seq, "notifyq-count: %d\n", lid->eth.config.queue_count[IONIC_QTYPE_NOTIFYQ]); seq_printf(seq, "rxq-count: %d\n", lid->eth.config.queue_count[IONIC_QTYPE_RXQ]); seq_printf(seq, "txq-count: %d\n", lid->eth.config.queue_count[IONIC_QTYPE_TXQ]); seq_printf(seq, "eq-count: %d\n", lid->eth.config.queue_count[IONIC_QTYPE_EQ]); seq_printf(seq, "\n"); seq_printf(seq, "rdma_version: 0x%x\n", lid->rdma.version); seq_printf(seq, "rdma_qp_opcodes: %d\n", lid->rdma.qp_opcodes); seq_printf(seq, "rdma_admin_opcodes: %d\n", lid->rdma.admin_opcodes); seq_printf(seq, "rdma_npts_per_lif: %d\n", lid->rdma.npts_per_lif); seq_printf(seq, "rdma_nmrs_per_lif: %d\n", lid->rdma.nmrs_per_lif); seq_printf(seq, "rdma_nahs_per_lif: %d\n", lid->rdma.nahs_per_lif); seq_printf(seq, "rdma_max_stride: %d\n", lid->rdma.max_stride); seq_printf(seq, "rdma_cl_stride: %d\n", lid->rdma.cl_stride); seq_printf(seq, "rdma_pte_stride: %d\n", lid->rdma.pte_stride); seq_printf(seq, "rdma_rrq_stride: %d\n", lid->rdma.rrq_stride); seq_printf(seq, "rdma_rsq_stride: %d\n", lid->rdma.rsq_stride); seq_printf(seq, "rdma_dcqcn_profiles: %d\n", lid->rdma.dcqcn_profiles); identity_show_qtype(seq, "rdma_aq", &lid->rdma.aq_qtype); identity_show_qtype(seq, "rdma_sq", &lid->rdma.sq_qtype); identity_show_qtype(seq, "rdma_rq", &lid->rdma.rq_qtype); identity_show_qtype(seq, "rdma_cq", &lid->rdma.cq_qtype); identity_show_qtype(seq, "rdma_eq", &lid->rdma.eq_qtype); return 0; } DEFINE_SHOW_ATTRIBUTE(lif_identity); void ionic_debugfs_add_lif(struct ionic_lif *lif) { struct dentry *identity_dentry; struct dentry *netdev_dentry; lif->dentry = debugfs_create_dir(lif->name, lif->ionic->dentry); if (IS_ERR_OR_NULL(lif->dentry)) return; netdev_dentry = debugfs_create_file("netdev", 0400, lif->dentry, lif->netdev, &netdev_fops); if (IS_ERR_OR_NULL(netdev_dentry)) return; identity_dentry = debugfs_create_file("identity", 0400, lif->dentry, lif->identity, &lif_identity_fops); } void ionic_debugfs_del_lif(struct ionic_lif *lif) { debugfs_remove_recursive(lif->dentry); lif->dentry = NULL; } void ionic_debugfs_add_eq(struct ionic_eq *eq) { const int ring_bytes = sizeof(struct ionic_eq_comp) * IONIC_EQ_DEPTH; struct device *dev = eq->ionic->dev; struct debugfs_blob_wrapper *blob; struct debugfs_regset32 *regset; struct dentry *ent; char name[40]; snprintf(name, sizeof(name), "eq%02u", eq->index); ent = debugfs_create_dir(name, eq->ionic->dentry); if (IS_ERR_OR_NULL(ent)) return; blob = devm_kzalloc(dev, sizeof(*blob), GFP_KERNEL); blob->data = eq->ring[0].base; blob->size = ring_bytes; debugfs_create_blob("ring0", 0400, ent, blob); blob = devm_kzalloc(dev, sizeof(*blob), GFP_KERNEL); blob->data = eq->ring[1].base; blob->size = ring_bytes; debugfs_create_blob("ring1", 0400, ent, blob); regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL); regset->regs = intr_ctrl_regs; regset->nregs = ARRAY_SIZE(intr_ctrl_regs); regset->base = &eq->ionic->idev.intr_ctrl[eq->intr.index]; debugfs_create_regset32("intr_ctrl", 0400, ent, regset); } void ionic_debugfs_del_qcq(struct ionic_qcq *qcq) { debugfs_remove_recursive(qcq->dentry); qcq->dentry = NULL; } #endif