| /* |
| ************************************************************************** |
| * Copyright (c) 2020, The Linux Foundation. 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 <nss_api_if.h> |
| #include "nss_connmgr_tunipip6_priv.h" |
| |
| /* |
| * nss_tunipip6_stats_str |
| * tunipip6 statistics strings for NSS tunnel stats |
| */ |
| static int8_t *nss_tunipip6_stats_str[NSS_TUNIPIP6_STATS_MAX] = { |
| "rx pkts", |
| "rx bytes", |
| "tx pkts", |
| "tx bytes", |
| "rx queue 0 dropped", |
| "rx queue 1 dropped", |
| "rx queue 2 dropped", |
| "rx queue 3 dropped", |
| "encap low headroom", |
| "encap unhandled_protocol", |
| "encap enqueue fail", |
| "encap tunnel exist", |
| "encap total fmr_count", |
| "encap fmr add count", |
| "encap fmr del count", |
| "encap fmr flush count", |
| "encap fmr update_count", |
| "encap fmr add_fail count", |
| "encap fmr del_fail count", |
| "encap error no fmr", |
| "encap bmr add count", |
| "encap bmr del count", |
| "encap error bmr exist", |
| "encap error no bmr", |
| "decap enqueue fail", |
| }; |
| |
| /* |
| * nss_tunipip6_stats_show() |
| * Read tunipip6 tunnel statistics |
| */ |
| static int nss_tunipip6_stats_show(struct seq_file *m, void __attribute__((unused))*p) |
| { |
| int i; |
| struct nss_tunipip6_instance *tun_inst; |
| struct nss_tunipip6_stats *tunipip6_tunnel_stats; |
| |
| tun_inst = vzalloc(sizeof(struct nss_tunipip6_instance)); |
| if (!tun_inst) { |
| nss_tunipip6_warning("Failed to allocate memory for tun_inst\n"); |
| return -ENOMEM; |
| } |
| |
| tunipip6_tunnel_stats = vzalloc(sizeof(struct nss_tunipip6_stats)); |
| if (!tunipip6_tunnel_stats) { |
| nss_tunipip6_warning("Failed to allocate memory for tunipip6_tunnel_stats\n"); |
| vfree(tun_inst); |
| return -ENOMEM; |
| } |
| |
| /* |
| * Copy the tunnel and stats information from the tunnel instance. |
| */ |
| spin_lock_bh(&tunipip6_ctx.lock); |
| memcpy(tun_inst, m->private, sizeof(struct nss_tunipip6_instance)); |
| memcpy(tunipip6_tunnel_stats, &tun_inst->stats, sizeof(struct nss_tunipip6_stats)); |
| spin_unlock_bh(&tunipip6_ctx.lock); |
| |
| seq_printf(m, "\n\tInner ifnum %u stats:\n", tun_inst->inner_ifnum); |
| for (i = 0; i < NSS_TUNIPIP6_STATS_MAX; i++) { |
| seq_printf(m, "\t\t%s = %llu\n", |
| nss_tunipip6_stats_str[i], |
| tunipip6_tunnel_stats->inner_stats[i]); |
| } |
| |
| seq_printf(m, "\n\tOuter ifnum %u stats:\n", tun_inst->outer_ifnum); |
| for (i = 0; i < NSS_TUNIPIP6_STATS_MAX; i++) { |
| seq_printf(m, "\t\t%s = %llu\n", |
| nss_tunipip6_stats_str[i], |
| tunipip6_tunnel_stats->outer_stats[i]); |
| } |
| |
| seq_printf(m, "\n%s tunnel stats end\n\n", tun_inst->dev->name); |
| vfree(tun_inst); |
| vfree(tunipip6_tunnel_stats); |
| return 0; |
| } |
| |
| /* |
| * nss_tunipip6_stats_open() |
| */ |
| static int nss_tunipip6_stats_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, nss_tunipip6_stats_show, inode->i_private); |
| } |
| |
| /* |
| * nss_tunipip6_stats_update() |
| * Update inner or outer node statistics |
| */ |
| static void nss_tunipip6_stats_update(uint64_t *stats, struct nss_tunipip6_stats_sync_msg *stats_msg) |
| { |
| uint32_t i, *src; |
| uint64_t *dest = stats; |
| |
| src = &stats_msg->node_stats.rx_packets; |
| for (i = NSS_TUNIPIP6_STATS_RX_PKTS; i < NSS_TUNIPIP6_STATS_MAX; i++, src++, dest++) { |
| *dest += *src; |
| } |
| } |
| |
| /* |
| * nss_tunipip6_stats_sync() |
| * Sync function for tunipip6 statistics |
| */ |
| void nss_tunipip6_stats_sync(struct net_device *dev, struct nss_tunipip6_msg *ntm) |
| { |
| uint32_t ifnum = ntm->cm.interface; |
| struct nss_tunipip6_stats_sync_msg *stats = &ntm->msg.stats; |
| struct nss_tunipip6_instance *ntii; |
| struct nss_tunipip6_stats *s; |
| |
| spin_lock_bh(&tunipip6_ctx.lock); |
| ntii = nss_tunipip6_find_instance(dev); |
| if (!ntii) { |
| spin_unlock_bh(&tunipip6_ctx.lock); |
| nss_tunipip6_warning("%px: Not able to find context for device: %s\n", dev, dev->name); |
| return; |
| } |
| |
| s = &ntii->stats; |
| if (ntii->inner_ifnum == ifnum) { |
| nss_tunipip6_stats_update(s->inner_stats, stats); |
| s->inner_stats[NSS_TUNIPIP6_STATS_CONFIG_ENCAP_TOTAL_FMR] = stats->tun_stats.encap.cfg.total_fmr; |
| } else if (ntii->outer_ifnum == ifnum) { |
| nss_tunipip6_stats_update(s->outer_stats, stats); |
| } else { |
| nss_tunipip6_warning("%px: Netdev=%s invalid interface number. Interface No: %u\n", dev, dev->name, ifnum); |
| } |
| |
| spin_unlock_bh(&tunipip6_ctx.lock); |
| } |
| |
| /* |
| * nss_tunipip6_stats_ops |
| * File operations for tunipip6 tunnel stats |
| */ |
| static const struct file_operations nss_tunipip6_stats_ops = { \ |
| .open = nss_tunipip6_stats_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = seq_release |
| }; |
| |
| /* |
| * nss_tunipip6_stats_dentry_destroy() |
| * Remove debufs file for given tunnel. |
| */ |
| void nss_tunipip6_stats_dentry_destroy(struct nss_tunipip6_instance *tun_inst) |
| { |
| debugfs_remove(tun_inst->dentry); |
| } |
| |
| /* |
| * nss_tunipip6_stats_dentry_create() |
| * Create dentry for a given tunnel. |
| */ |
| bool nss_tunipip6_stats_dentry_create(struct nss_tunipip6_instance *tun_inst) |
| { |
| char dentry_name[IFNAMSIZ]; |
| |
| scnprintf(dentry_name, sizeof(dentry_name), "%s", tun_inst->dev->name); |
| tun_inst->dentry = debugfs_create_file(dentry_name, S_IRUGO, |
| tunipip6_ctx.tunipip6_dentry_dir, tun_inst, &nss_tunipip6_stats_ops); |
| if (!tun_inst->dentry) { |
| nss_tunipip6_warning("Debugfs file creation failed for tun %s\n", tun_inst->dev->name); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * nss_tunipip6_stats_dentry_deinit() |
| * Cleanup the debugfs tree. |
| */ |
| void nss_tunipip6_stats_dentry_deinit(void) |
| { |
| if (tunipip6_ctx.tunipip6_dentry_dir) { |
| debugfs_remove_recursive(tunipip6_ctx.tunipip6_dentry_dir); |
| } |
| } |
| |
| /* |
| * nss_tunipip6_stats_dentry_init() |
| * Create tunipip6 tunnel statistics debugfs entry. |
| */ |
| bool nss_tunipip6_stats_dentry_init(void) |
| { |
| /* |
| * Initialize debugfs directory. |
| */ |
| tunipip6_ctx.tunipip6_dentry_dir = debugfs_create_dir("qca-nss-tunipip6", NULL); |
| if (!tunipip6_ctx.tunipip6_dentry_dir) { |
| nss_tunipip6_warning("Failed to create debug entry for subsystem: qca-nss-tunipip6\n"); |
| return false; |
| } |
| |
| return true; |
| } |