/*! \file ngknet_procfs.c * * * */ /* * $Copyright: Copyright 2018-2020 Broadcom. All rights reserved. * The term 'Broadcom' refers to Broadcom Inc. and/or its subsidiaries. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * A copy of the GNU General Public License version 2 (GPLv2) can * be found in the LICENSES folder.$ */ #include #include #include "ngknet_main.h" #include "ngknet_extra.h" extern struct ngknet_dev ngknet_devices[]; static struct proc_dir_entry *proc_root = NULL; static void proc_data_show(struct seq_file *m, const unsigned char *buf, size_t len) { uint32_t i; if (!buf || !len) { seq_printf(m, "\n"); return; } for (i = 0; i < len; i++) { seq_printf(m, "%02x ", buf[i]); if ((i + 1) % 32 == 0 || (i + 1) == len) { seq_printf(m, "\n"); if ((i + 1) < len) { seq_printf(m, " "); } } } } static int proc_debug_level_show(struct seq_file *m, void *v) { seq_printf(m, "Debug level: 0x%x\n", ngknet_debug_level_get()); return 0; } static int proc_debug_level_open(struct inode *inode, struct file *file) { return single_open(file, proc_debug_level_show, NULL); } static ssize_t proc_debug_level_write(struct file *file, const char *buf, size_t count, loff_t *loff) { char level_str[11] = {0}; int debug_level; if (copy_from_user(level_str, buf, sizeof(level_str) - 1)) { return -EFAULT; } debug_level = simple_strtol(level_str, NULL, 16); ngknet_debug_level_set(debug_level); printk("Debug level set to: 0x%x\n", debug_level); return count; } static int proc_debug_level_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_debug_level_fops = { owner: THIS_MODULE, open: proc_debug_level_open, read: seq_read, write: proc_debug_level_write, llseek: seq_lseek, release: proc_debug_level_release, }; static int proc_device_info_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; struct bcmcnet_dev_info *info; int di, qi, ai = 0; int rv; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } ai++; rv = bcmcnet_pdma_dev_info_get(&dev->pdma_dev); if (SHR_FAILURE(rv)) { printk("ngknet: get device%d info failed\n", di); break; } info = &dev->pdma_dev.info; seq_printf(m, "dev_no: %d\n", di); seq_printf(m, "dev_name: %s\n", info->dev_name); seq_printf(m, "dev_id: 0x%x\n", info->dev_id); seq_printf(m, "dev_type: %d\n", info->dev_type); seq_printf(m, "max_groups: %d\n", info->max_groups); seq_printf(m, "max_queues: %d\n", info->max_queues); seq_printf(m, "bm_groups: 0x%x\n", info->bm_groups); seq_printf(m, "bm_rx_queues: 0x%x\n", info->bm_rx_queues); seq_printf(m, "bm_tx_queues: 0x%x\n", info->bm_tx_queues); seq_printf(m, "nb_groups: %d\n", info->nb_groups); seq_printf(m, "nb_rx_queues: %d\n", info->nb_rx_queues); seq_printf(m, "nb_tx_queues: %d\n", info->nb_tx_queues); seq_printf(m, "rx_desc_size: %d\n", info->rx_desc_size); seq_printf(m, "tx_desc_size: %d\n", info->tx_desc_size); seq_printf(m, "rx_ph_size: %d\n", info->rx_ph_size); seq_printf(m, "tx_ph_size: %d\n", info->tx_ph_size); for (qi = 0; qi < info->nb_rx_queues; qi++) { seq_printf(m, "rx_buf_sz[%d]: %d\n", qi, info->rx_buf_size[qi]); } for (qi = 0; qi < info->nb_rx_queues; qi++) { seq_printf(m, "nb_rx_desc[%d]: %d\n", qi, info->nb_rx_desc[qi]); } for (qi = 0; qi < info->nb_tx_queues; qi++) { seq_printf(m, "nb_tx_desc[%d]: %d\n", qi, info->nb_tx_desc[qi]); } } if (!ai) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "------------------------\n"); seq_printf(m, "Total %d devices\n", ai); } return 0; } static int proc_device_info_open(struct inode *inode, struct file *file) { return single_open(file, proc_device_info_show, NULL); } static int proc_device_info_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_device_info_fops = { owner: THIS_MODULE, open: proc_device_info_open, read: seq_read, llseek: seq_lseek, release: proc_device_info_release, }; static int proc_filter_info_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; ngknet_filter_t filt = {0}; int di, dn = 0, fn = 0; int rv; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } dn++; do { rv = ngknet_filter_get_next(dev, &filt); if (SHR_FAILURE(rv)) { printk("ngknet: get device%d filter failed\n", di); break; } fn++; seq_printf(m, "\n"); seq_printf(m, "dev_no: %d\n", di); seq_printf(m, "id: %d\n", filt.id); seq_printf(m, "next: %d\n", filt.next); seq_printf(m, "type: %d\n", filt.type); seq_printf(m, "flags: 0x%x\n", filt.flags); seq_printf(m, "prio: %d\n", filt.priority); seq_printf(m, "chan: %d\n", filt.chan); seq_printf(m, "desc: %s\n", filt.desc); seq_printf(m, "dest_type: %d\n", filt.dest_type); seq_printf(m, "dest_id: %d\n", filt.dest_id); seq_printf(m, "dest_proto: 0x%x\n", filt.dest_proto); seq_printf(m, "mirror_type: %d\n", filt.mirror_type); seq_printf(m, "mirror_id: %d\n", filt.mirror_id); seq_printf(m, "mirror_proto: 0x%x\n", filt.mirror_proto); seq_printf(m, "oob_offset: %d\n", filt.oob_data_offset); seq_printf(m, "oob_size: %d\n", filt.oob_data_size); seq_printf(m, "pkt_offset: %d\n", filt.pkt_data_offset); seq_printf(m, "pkt_size: %d\n", filt.pkt_data_size); seq_printf(m, "filt_data: "); proc_data_show(m, filt.data.b, filt.oob_data_size + filt.pkt_data_size); seq_printf(m, "filt_mask: "); proc_data_show(m, filt.mask.b, filt.oob_data_size + filt.pkt_data_size); seq_printf(m, "user_data: "); proc_data_show(m, filt.user_data, NGKNET_FILTER_USER_DATA); seq_printf(m, "hits: %llu\n", ((struct filt_ctrl *)dev->fc[filt.id])->hits); } while (filt.next); } if (!dn) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "--------------------------------\n"); seq_printf(m, "Total %d devices, %d filters\n", dn, fn); } return 0; } static int proc_filter_info_open(struct inode *inode, struct file *file) { return single_open(file, proc_filter_info_show, NULL); } static int proc_filter_info_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_filter_info_fops = { owner: THIS_MODULE, open: proc_filter_info_open, read: seq_read, llseek: seq_lseek, release: proc_filter_info_release, }; static int proc_netif_info_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; struct net_device *ndev; struct ngknet_private *priv; ngknet_netif_t netif = {0}; int di, ma, dn = 0, nn = 0; int rv; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } dn++; do { rv = ngknet_netif_get_next(dev, &netif); if (SHR_FAILURE(rv)) { printk("ngknet: get device%d netif failed\n", di); break; } nn++; ndev = netif.id == 0 ? dev->net_dev : dev->vdev[netif.id]; priv = netdev_priv(ndev); seq_printf(m, "\n"); seq_printf(m, "dev_no: %d\n", di); seq_printf(m, "id: %d\n", netif.id); seq_printf(m, "next: %d\n", netif.next); seq_printf(m, "type: %d\n", netif.type); seq_printf(m, "flags: 0x%x\n", netif.flags); seq_printf(m, "vlan: %d\n", netif.vlan); seq_printf(m, "mac: "); for (ma = 0; ma < 6; ma++) { if (ma == 5) { seq_printf(m, "%02x\n", netif.macaddr[ma]); } else { seq_printf(m, "%02x:", netif.macaddr[ma]); } } seq_printf(m, "mtu: %d\n", netif.mtu); seq_printf(m, "chan: %d\n", netif.chan); seq_printf(m, "name: %s\n", netif.name); seq_printf(m, "meta_off: %d\n", netif.meta_off); seq_printf(m, "meta_len: %d\n", netif.meta_len); seq_printf(m, "meta_data: "); proc_data_show(m, netif.meta_data, netif.meta_len); seq_printf(m, "user_data: "); proc_data_show(m, netif.user_data, NGKNET_NETIF_USER_DATA); seq_printf(m, "rx_packets: %lu\n", priv->stats.rx_packets); seq_printf(m, "rx_bytes: %lu\n", priv->stats.rx_bytes); seq_printf(m, "rx_dropped: %lu\n", priv->stats.rx_dropped); seq_printf(m, "rx_errors: %lu\n", priv->stats.rx_errors); seq_printf(m, "tx_packets: %lu\n", priv->stats.tx_packets); seq_printf(m, "tx_bytes: %lu\n", priv->stats.tx_bytes); seq_printf(m, "tx_dropped: %lu\n", priv->stats.tx_dropped); seq_printf(m, "tx_errors: %lu\n", priv->stats.tx_errors); } while (netif.next); } if (!dn) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "--------------------------------\n"); seq_printf(m, "Total %d devices, %d netifs\n", dn, nn); } return 0; } static int proc_netif_info_open(struct inode *inode, struct file *file) { return single_open(file, proc_netif_info_show, NULL); } static int proc_netif_info_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_netif_info_fops = { owner: THIS_MODULE, open: proc_netif_info_open, read: seq_read, llseek: seq_lseek, release: proc_netif_info_release, }; static int proc_pkt_stats_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; struct bcmcnet_dev_stats *stats; int di, qi, ai = 0; int rv; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } ai++; rv = bcmcnet_pdma_dev_stats_get(&dev->pdma_dev); if (SHR_FAILURE(rv)) { printk("ngknet: get device%d stats failed\n", di); break; } stats = &dev->pdma_dev.stats; seq_printf(m, "rx_packets: %llu\n", (unsigned long long)stats->rx_packets); seq_printf(m, "rx_bytes: %llu\n", (unsigned long long)stats->rx_bytes); for (qi = 0; qi < dev->pdma_dev.ctrl.nb_rxq; qi++) { seq_printf(m, "rx_packets[%d]: %llu\n", qi, (unsigned long long)stats->rxq_packets[qi]); seq_printf(m, "rx_bytes[%d]: %llu\n", qi, (unsigned long long)stats->rxq_bytes[qi]); } seq_printf(m, "rx_dropped: %llu\n", (unsigned long long)stats->rx_dropped); seq_printf(m, "rx_errors: %llu\n", (unsigned long long)stats->rx_errors); seq_printf(m, "rx_nomems: %llu\n", (unsigned long long)stats->rx_nomems); seq_printf(m, "tx_packets: %llu\n", (unsigned long long)stats->tx_packets); seq_printf(m, "tx_bytes: %llu\n", (unsigned long long)stats->tx_bytes); for (qi = 0; qi < dev->pdma_dev.ctrl.nb_txq; qi++) { seq_printf(m, "tx_packets[%d]: %llu\n", qi, (unsigned long long)stats->txq_packets[qi]); seq_printf(m, "tx_bytes[%d]: %llu\n", qi, (unsigned long long)stats->txq_bytes[qi]); } seq_printf(m, "tx_dropped: %llu\n", (unsigned long long)stats->tx_dropped); seq_printf(m, "tx_errors: %llu\n", (unsigned long long)stats->tx_errors); seq_printf(m, "tx_xoffs: %llu\n", (unsigned long long)stats->tx_xoffs); seq_printf(m, "interrupts: %llu\n", (unsigned long long)stats->intrs); } if (!ai) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "------------------------\n"); seq_printf(m, "Total %d devices\n", ai); } return 0; } static int proc_pkt_stats_open(struct inode *inode, struct file *file) { return single_open(file, proc_pkt_stats_show, NULL); } static int proc_pkt_stats_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_pkt_stats_fops = { owner: THIS_MODULE, open: proc_pkt_stats_open, read: seq_read, llseek: seq_lseek, release: proc_pkt_stats_release, }; static int proc_rate_limit_show(struct seq_file *m, void *v) { seq_printf(m, "Rx rate limit: %d pps\n", ngknet_rx_rate_limit_get()); return 0; } static int proc_rate_limit_open(struct inode *inode, struct file *file) { return single_open(file, proc_rate_limit_show, NULL); } static ssize_t proc_rate_limit_write(struct file *file, const char *buf, size_t count, loff_t *loff) { char limit_str[9] = {0}; int rate_limit; if (copy_from_user(limit_str, buf, sizeof(limit_str) - 1)) { return -EFAULT; } rate_limit = simple_strtol(limit_str, NULL, 10); ngknet_rx_rate_limit_set(rate_limit); printk("Rx rate limit set to: %d pps\n", rate_limit); return count; } static int proc_rate_limit_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_rate_limit_fops = { owner: THIS_MODULE, open: proc_rate_limit_open, read: seq_read, write: proc_rate_limit_write, llseek: seq_lseek, release: proc_rate_limit_release, }; static int proc_reg_status_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; int di, qi, ai = 0; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } ai++; for (qi = 0; qi < dev->pdma_dev.ctrl.nb_rxq; qi++) { bcmcnet_pdma_rx_queue_reg_dump(&dev->pdma_dev, qi); } for (qi = 0; qi < dev->pdma_dev.ctrl.nb_txq; qi++) { bcmcnet_pdma_tx_queue_reg_dump(&dev->pdma_dev, qi); } } if (!ai) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "------------------------\n"); seq_printf(m, "Total %d devices\n", ai); } return 0; } static int proc_reg_status_open(struct inode *inode, struct file *file) { return single_open(file, proc_reg_status_show, NULL); } static int proc_reg_status_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_reg_status_fops = { owner: THIS_MODULE, open: proc_reg_status_open, read: seq_read, llseek: seq_lseek, release: proc_reg_status_release, }; static int proc_ring_status_show(struct seq_file *m, void *v) { struct ngknet_dev *dev; int di, qi, ai = 0; for (di = 0; di < NUM_PDMA_DEV_MAX; di++) { dev = &ngknet_devices[di]; if (!(dev->flags & NGKNET_DEV_ACTIVE)) { continue; } ai++; seq_printf(m, "%s-%d, ", "Unit", di); for (qi = 0; qi < dev->pdma_dev.ctrl.nb_rxq; qi++) { bcmcnet_pdma_rx_ring_dump(&dev->pdma_dev, qi); } seq_printf(m, "%s%d, ", "Rx queues: ", qi); for (qi = 0; qi < dev->pdma_dev.ctrl.nb_txq; qi++) { bcmcnet_pdma_tx_ring_dump(&dev->pdma_dev, qi); } seq_printf(m, "%s%d. ", "Tx queues: ", qi); seq_printf(m, "\n"); } if (!ai) { seq_printf(m, "%s\n", "No active device"); } else { seq_printf(m, "------------------------\n"); seq_printf(m, "Total %d devices\n", ai); } return 0; } static int proc_ring_status_open(struct inode *inode, struct file *file) { return single_open(file, proc_ring_status_show, NULL); } static int proc_ring_status_release(struct inode *inode, struct file *file) { return single_release(inode, file); } static struct file_operations proc_ring_status_fops = { owner: THIS_MODULE, open: proc_ring_status_open, read: seq_read, llseek: seq_lseek, release: proc_ring_status_release, }; int ngknet_procfs_init(void) { struct proc_dir_entry *entry = NULL; proc_root = proc_mkdir(NGKNET_MODULE_NAME, NULL); if (proc_root == NULL) { printk(KERN_ERR "ngknet: proc_mkdir failed\n"); return -1; } PROC_CREATE(entry, "debug_level", 0666, proc_root, &proc_debug_level_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "device_info", 0444, proc_root, &proc_device_info_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "filter_info", 0444, proc_root, &proc_filter_info_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "netif_info", 0444, proc_root, &proc_netif_info_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "pkt_stats", 0444, proc_root, &proc_pkt_stats_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "rate_limit", 0666, proc_root, &proc_rate_limit_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "reg_status", 0444, proc_root, &proc_reg_status_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } PROC_CREATE(entry, "ring_status", 0444, proc_root, &proc_ring_status_fops); if (entry == NULL) { printk(KERN_ERR "ngknet: proc_create failed\n"); return -1; } return 0; } int ngknet_procfs_cleanup(void) { remove_proc_entry("debug_level", proc_root); remove_proc_entry("device_info", proc_root); remove_proc_entry("filter_info", proc_root); remove_proc_entry("netif_info", proc_root); remove_proc_entry("pkt_stats", proc_root); remove_proc_entry("rate_limit", proc_root); remove_proc_entry("reg_status", proc_root); remove_proc_entry("ring_status", proc_root); remove_proc_entry(NGKNET_MODULE_NAME, NULL); return 0; }