| /* |
| * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <linux/debugfs.h> |
| #include "edma.h" |
| #include "edma_debug.h" |
| #include "edma_debugfs.h" |
| |
| /* |
| * edma_debugfs_print_banner() |
| * API to print the banner for a node |
| */ |
| static void edma_debugfs_print_banner(struct seq_file *m, char *node) |
| { |
| uint32_t banner_char_len, i; |
| |
| for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++) { |
| seq_printf(m, "_"); |
| } |
| |
| banner_char_len = (EDMA_STATS_BANNER_MAX_LEN - (strlen(node) + 2)) / 2; |
| |
| seq_printf(m, "\n\n"); |
| |
| for (i = 0; i < banner_char_len; i++) { |
| seq_printf(m, "<"); |
| } |
| |
| seq_printf(m, " %s ", node); |
| |
| for (i = 0; i < banner_char_len; i++) { |
| seq_printf(m, ">"); |
| } |
| seq_printf(m, "\n"); |
| |
| for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++) { |
| seq_printf(m, "_"); |
| } |
| |
| seq_printf(m, "\n\n"); |
| } |
| |
| /* |
| * edma_debugfs_rx_rings_stats_show() |
| * EDMA debugfs rx rings stats show API |
| */ |
| static int edma_debugfs_rx_rings_stats_show(struct seq_file *m, void __attribute__((unused))*p) |
| { |
| struct edma_rx_fill_stats *rx_fill_stats; |
| struct edma_rx_desc_stats *rx_desc_stats; |
| struct edma_gbl_ctx *egc = &edma_gbl_ctx; |
| uint32_t rx_fill_start_id = egc->rxfill_ring_start; |
| uint32_t rx_desc_start_id = egc->rxdesc_ring_start; |
| uint32_t i; |
| unsigned int start; |
| |
| rx_fill_stats = kzalloc(egc->num_rxfill_rings * sizeof(struct edma_rx_fill_stats), |
| GFP_KERNEL); |
| if (!rx_fill_stats) { |
| edma_err("Error in allocating the Rx fill stats buffer\n"); |
| return -ENOMEM; |
| } |
| |
| rx_desc_stats = kzalloc(egc->num_rxdesc_rings * sizeof(struct edma_rx_desc_stats), |
| GFP_KERNEL); |
| if (!rx_desc_stats) { |
| edma_err("Error in allocating the Rx descriptor stats buffer\n"); |
| kfree(rx_fill_stats); |
| return -ENOMEM; |
| } |
| |
| /* |
| * Get stats for Rx fill rings |
| */ |
| for (i = 0; i < egc->num_rxfill_rings; i++) { |
| struct edma_rxfill_ring *rxfill_ring; |
| struct edma_rx_fill_stats *stats; |
| |
| rxfill_ring = &egc->rxfill_rings[i]; |
| stats = &rxfill_ring->rx_fill_stats; |
| do { |
| start = u64_stats_fetch_begin_irq(&stats->syncp); |
| rx_fill_stats[i].alloc_failed = stats->alloc_failed; |
| rx_fill_stats[i].page_alloc_failed = stats->page_alloc_failed; |
| } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); |
| } |
| |
| /* |
| * Get stats for Rx Desc rings |
| */ |
| for (i = 0; i < edma_gbl_ctx.num_rxdesc_rings; i++) { |
| struct edma_rxdesc_ring *rxdesc_ring; |
| struct edma_rx_desc_stats *stats; |
| |
| rxdesc_ring = &egc->rxdesc_rings[i]; |
| stats = &rxdesc_ring->rx_desc_stats; |
| do { |
| start = u64_stats_fetch_begin_irq(&stats->syncp); |
| rx_desc_stats[i].src_port_inval = stats->src_port_inval; |
| rx_desc_stats[i].src_port_inval_type = stats->src_port_inval_type; |
| rx_desc_stats[i].src_port_inval_netdev = stats->src_port_inval_netdev; |
| } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); |
| } |
| |
| edma_debugfs_print_banner(m, EDMA_RX_RING_STATS_NODE_NAME); |
| |
| seq_printf(m, "\n#EDMA RX descriptor rings stats:\n\n"); |
| for (i = 0; i < edma_gbl_ctx.num_rxdesc_rings; i++) { |
| seq_printf(m, "\t\tEDMA RX descriptor %d ring stats:\n", i + rx_desc_start_id); |
| seq_printf(m, "\t\t rxdesc[%d]:src_port_inval = %llu\n", |
| i + rx_desc_start_id, rx_desc_stats[i].src_port_inval); |
| seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_type = %llu\n", |
| i + rx_desc_start_id, rx_desc_stats[i].src_port_inval_type); |
| seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_netdev = %llu\n", |
| i + rx_desc_start_id, |
| rx_desc_stats[i].src_port_inval_netdev); |
| seq_printf(m, "\n"); |
| } |
| |
| seq_printf(m, "\n#EDMA RX fill rings stats:\n\n"); |
| for (i = 0; i < edma_gbl_ctx.num_rxfill_rings; i++) { |
| seq_printf(m, "\t\tEDMA RX fill %d ring stats:\n", i + rx_fill_start_id); |
| seq_printf(m, "\t\t rxfill[%d]:alloc_failed = %llu\n", |
| i + rx_fill_start_id, rx_fill_stats[i].alloc_failed); |
| seq_printf(m, "\t\t rxfill[%d]:page_alloc_failed = %llu\n", |
| i + rx_fill_start_id, rx_fill_stats[i].page_alloc_failed); |
| seq_printf(m, "\n"); |
| } |
| |
| kfree(rx_fill_stats); |
| kfree(rx_desc_stats); |
| return 0; |
| } |
| |
| /* |
| * edma_debugfs_tx_rings_stats_show() |
| * EDMA debugfs Tx rings stats show API |
| */ |
| static int edma_debugfs_tx_rings_stats_show(struct seq_file *m, void __attribute__((unused))*p) |
| { |
| struct edma_tx_cmpl_stats *tx_cmpl_stats; |
| struct edma_tx_desc_stats *tx_desc_stats; |
| struct edma_gbl_ctx *egc = &edma_gbl_ctx; |
| uint32_t tx_cmpl_start_id = egc->txcmpl_ring_start; |
| uint32_t tx_desc_start_id = egc->txdesc_ring_start; |
| uint32_t i; |
| unsigned int start; |
| |
| tx_cmpl_stats = kzalloc(egc->num_txcmpl_rings * sizeof(struct edma_tx_cmpl_stats), GFP_KERNEL); |
| if (!tx_cmpl_stats) { |
| edma_err("Error in allocating the Tx complete stats buffer\n"); |
| return -ENOMEM; |
| } |
| |
| tx_desc_stats = kzalloc(egc->num_txdesc_rings * sizeof(struct edma_tx_desc_stats), GFP_KERNEL); |
| if (!tx_desc_stats) { |
| edma_err("Error in allocating the Tx descriptor stats buffer\n"); |
| kfree(tx_cmpl_stats); |
| return -ENOMEM; |
| } |
| |
| /* |
| * Get stats for Tx desc rings |
| */ |
| for (i = 0; i < egc->num_txdesc_rings; i++) { |
| struct edma_txdesc_ring *txdesc_ring; |
| struct edma_tx_desc_stats *stats; |
| |
| txdesc_ring = &egc->txdesc_rings[i]; |
| stats = &txdesc_ring->tx_desc_stats; |
| do { |
| start = u64_stats_fetch_begin_irq(&stats->syncp); |
| tx_desc_stats[i].no_desc_avail = stats->no_desc_avail; |
| } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); |
| } |
| |
| /* |
| * Get stats for Tx Complete rings |
| */ |
| for (i = 0; i < egc->num_txcmpl_rings; i++) { |
| struct edma_txcmpl_ring *txcmpl_ring; |
| struct edma_tx_cmpl_stats *stats; |
| |
| txcmpl_ring = &egc->txcmpl_rings[i]; |
| stats = &txcmpl_ring->tx_cmpl_stats; |
| do { |
| start = u64_stats_fetch_begin_irq(&stats->syncp); |
| tx_cmpl_stats[i].invalid_buffer = stats->invalid_buffer; |
| tx_cmpl_stats[i].errors = stats->errors; |
| tx_cmpl_stats[i].desc_with_more_bit = stats->desc_with_more_bit; |
| tx_cmpl_stats[i].no_pending_desc = stats->no_pending_desc; |
| } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); |
| } |
| |
| edma_debugfs_print_banner(m, EDMA_TX_RING_STATS_NODE_NAME); |
| |
| seq_printf(m, "\n#EDMA TX complete rings stats:\n\n"); |
| for (i = 0; i < edma_gbl_ctx.num_txcmpl_rings; i++) { |
| seq_printf(m, "\t\tEDMA TX complete %d ring stats:\n", i + tx_cmpl_start_id); |
| seq_printf(m, "\t\t txcmpl[%d]:invalid_buffer = %llu\n", |
| i + tx_cmpl_start_id, tx_cmpl_stats[i].invalid_buffer); |
| seq_printf(m, "\t\t txcmpl[%d]:errors = %llu\n", |
| i + tx_cmpl_start_id, tx_cmpl_stats[i].errors); |
| seq_printf(m, "\t\t txcmpl[%d]:desc_with_more_bit = %llu\n", |
| i + tx_cmpl_start_id, tx_cmpl_stats[i].desc_with_more_bit); |
| seq_printf(m, "\t\t txcmpl[%d]:no_pending_desc = %llu\n", |
| i + tx_cmpl_start_id, tx_cmpl_stats[i].no_pending_desc); |
| seq_printf(m, "\n"); |
| } |
| |
| seq_printf(m, "\n#EDMA TX descriptor rings stats:\n\n"); |
| for (i = 0; i < edma_gbl_ctx.num_txdesc_rings; i++) { |
| seq_printf(m, "\t\tEDMA TX descriptor %d ring stats:\n", i + tx_desc_start_id); |
| seq_printf(m, "\t\t txdesc[%d]:no_desc_avail = %llu\n", |
| i + tx_desc_start_id, tx_desc_stats[i].no_desc_avail); |
| seq_printf(m, "\n"); |
| } |
| |
| kfree(tx_cmpl_stats); |
| kfree(tx_desc_stats); |
| return 0; |
| } |
| |
| /* |
| * edma_debugfs_misc_stats_show() |
| * EDMA debugfs miscellaneous stats show API |
| */ |
| static int edma_debugfs_misc_stats_show(struct seq_file *m, void __attribute__((unused))*p) |
| { |
| struct edma_misc_stats *misc_stats, *pcpu_misc_stats; |
| uint32_t cpu; |
| unsigned int start; |
| |
| misc_stats = kzalloc(sizeof(struct edma_misc_stats), GFP_KERNEL); |
| if (!misc_stats) { |
| edma_err("Error in allocating the miscellaneous stats buffer\n"); |
| return -ENOMEM; |
| } |
| |
| /* |
| * Get percpu EDMA miscellaneous stats |
| */ |
| for_each_possible_cpu(cpu) { |
| pcpu_misc_stats = per_cpu_ptr(edma_gbl_ctx.misc_stats, cpu); |
| do { |
| start = u64_stats_fetch_begin_irq(&pcpu_misc_stats->syncp); |
| misc_stats->edma_misc_axi_read_err += |
| pcpu_misc_stats->edma_misc_axi_read_err; |
| misc_stats->edma_misc_axi_write_err += |
| pcpu_misc_stats->edma_misc_axi_write_err; |
| misc_stats->edma_misc_rx_desc_fifo_full += |
| pcpu_misc_stats->edma_misc_rx_desc_fifo_full; |
| misc_stats->edma_misc_rx_buf_size_err += |
| pcpu_misc_stats->edma_misc_rx_buf_size_err; |
| misc_stats->edma_misc_tx_sram_full += |
| pcpu_misc_stats->edma_misc_tx_sram_full; |
| misc_stats->edma_misc_tx_data_len_err += |
| pcpu_misc_stats->edma_misc_tx_data_len_err; |
| misc_stats->edma_misc_tx_timeout += |
| pcpu_misc_stats->edma_misc_tx_timeout; |
| misc_stats->edma_misc_tx_cmpl_buf_full += |
| pcpu_misc_stats->edma_misc_tx_cmpl_buf_full; |
| } while (u64_stats_fetch_retry_irq(&pcpu_misc_stats->syncp, start)); |
| } |
| |
| edma_debugfs_print_banner(m, EDMA_MISC_STATS_NODE_NAME); |
| |
| seq_printf(m, "\n#EDMA miscellaneous stats:\n\n"); |
| seq_printf(m, "\t\t miscellaneous axi read error = %llu\n", |
| misc_stats->edma_misc_axi_read_err); |
| seq_printf(m, "\t\t miscellaneous axi write error = %llu\n", |
| misc_stats->edma_misc_axi_write_err); |
| seq_printf(m, "\t\t miscellaneous Rx descriptor fifo full = %llu\n", |
| misc_stats->edma_misc_rx_desc_fifo_full); |
| seq_printf(m, "\t\t miscellaneous Rx buffer size error = %llu\n", |
| misc_stats->edma_misc_rx_buf_size_err); |
| seq_printf(m, "\t\t miscellaneous Tx SRAM full = %llu\n", |
| misc_stats->edma_misc_tx_sram_full); |
| seq_printf(m, "\t\t miscellaneous Tx data length error = %llu\n", |
| misc_stats->edma_misc_tx_data_len_err); |
| seq_printf(m, "\t\t miscellaneous Tx timeout = %llu\n", |
| misc_stats->edma_misc_tx_timeout); |
| seq_printf(m, "\t\t miscellaneous Tx completion buffer full = %llu\n", |
| misc_stats->edma_misc_tx_cmpl_buf_full); |
| |
| kfree(misc_stats); |
| return 0; |
| } |
| |
| /* |
| * edma_debugs_rx_rings_stats_open() |
| * EDMA debugfs Rx rings open callback API |
| */ |
| static int edma_debugs_rx_rings_stats_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, edma_debugfs_rx_rings_stats_show, inode->i_private); |
| } |
| |
| /* |
| * edma_debugfs_rx_rings_file_ops |
| * File operations for EDMA Rx rings stats |
| */ |
| const struct file_operations edma_debugfs_rx_rings_file_ops = { |
| .open = edma_debugs_rx_rings_stats_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = seq_release |
| }; |
| |
| /* |
| * edma_debugs_tx_rings_stats_open() |
| * EDMA debugfs Tx rings open callback API |
| */ |
| static int edma_debugs_tx_rings_stats_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, edma_debugfs_tx_rings_stats_show, inode->i_private); |
| } |
| |
| /* |
| * edma_debugfs_tx_rings_file_ops |
| * File operations for EDMA Tx rings stats |
| */ |
| const struct file_operations edma_debugfs_tx_rings_file_ops = { |
| .open = edma_debugs_tx_rings_stats_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = seq_release |
| }; |
| |
| /* |
| * edma_debugs_misc_stats_open() |
| * EDMA debugfs miscellaneous stats open callback API |
| */ |
| static int edma_debugs_misc_stats_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, edma_debugfs_misc_stats_show, inode->i_private); |
| } |
| |
| /* |
| * edma_debugfs_misc_file_ops |
| * File operations for EDMA miscellaneous stats |
| */ |
| const struct file_operations edma_debugfs_misc_file_ops = { |
| .open = edma_debugs_misc_stats_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = seq_release |
| }; |
| |
| /* |
| * edma_debugfs_init() |
| * EDMA debugfs init API |
| */ |
| int edma_debugfs_init(void) |
| { |
| edma_gbl_ctx.root_dentry = debugfs_create_dir("qca-nss-dp", NULL); |
| if (!edma_gbl_ctx.root_dentry) { |
| edma_err("Unable to create debugfs qca-nss-dp directory in debugfs\n"); |
| return -1; |
| } |
| |
| edma_gbl_ctx.stats_dentry = debugfs_create_dir("stats", edma_gbl_ctx.root_dentry); |
| if (!edma_gbl_ctx.stats_dentry) { |
| edma_err("Unable to create debugfs stats directory in debugfs\n"); |
| goto debugfs_dir_failed; |
| } |
| |
| if (!debugfs_create_file("rx_ring_stats", S_IRUGO, edma_gbl_ctx.stats_dentry, |
| NULL, &edma_debugfs_rx_rings_file_ops)) { |
| edma_err("Unable to create Rx rings statistics file entry in debugfs\n"); |
| goto debugfs_dir_failed; |
| } |
| |
| if (!debugfs_create_file("tx_ring_stats", S_IRUGO, edma_gbl_ctx.stats_dentry, |
| NULL, &edma_debugfs_tx_rings_file_ops)) { |
| edma_err("Unable to create Tx rings statistics file entry in debugfs\n"); |
| goto debugfs_dir_failed; |
| } |
| |
| /* |
| * Allocate memory for EDMA miscellaneous stats |
| */ |
| if (edma_misc_stats_alloc() < 0) { |
| edma_err("Unable to allocate miscellaneous percpu stats\n"); |
| goto debugfs_dir_failed; |
| } |
| |
| if (!debugfs_create_file("misc_stats", S_IRUGO, edma_gbl_ctx.stats_dentry, |
| NULL, &edma_debugfs_misc_file_ops)) { |
| edma_err("Unable to create EDMA miscellaneous statistics file entry in debugfs\n"); |
| goto debugfs_dir_failed; |
| } |
| |
| return 0; |
| |
| debugfs_dir_failed: |
| debugfs_remove_recursive(edma_gbl_ctx.root_dentry); |
| edma_gbl_ctx.root_dentry = NULL; |
| edma_gbl_ctx.stats_dentry = NULL; |
| return -1; |
| } |
| |
| /* |
| * edma_debugfs_exit() |
| * EDMA debugfs exit API |
| */ |
| void edma_debugfs_exit(void) |
| { |
| /* |
| * Free EDMA miscellaneous stats memory |
| */ |
| edma_misc_stats_free(); |
| |
| if (edma_gbl_ctx.root_dentry) { |
| debugfs_remove_recursive(edma_gbl_ctx.root_dentry); |
| edma_gbl_ctx.root_dentry = NULL; |
| edma_gbl_ctx.stats_dentry = NULL; |
| } |
| } |