[qca-edma] Add stats support for per-precedence stats
Add support for per-precedence counters for ingress/egress packets
Change-Id: I230a5e1cee5ea17ae57ade48d1f8984a3e583147
Signed-off-by: Rakesh Nair <ranair@codeaurora.org>
diff --git a/Makefile b/Makefile
index 8d6404d..c2ad52d 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,7 @@
essedma-objs += edma_axi.o \
edma.o \
+ edma_stats.o \
edma_ethtool.o
ESSEDMA_INCLUDE = -I$(obj)
diff --git a/edma.c b/edma.c
index 4e83c2e..3932454 100644
--- a/edma.c
+++ b/edma.c
@@ -15,12 +15,15 @@
#include <linux/platform_device.h>
#include <linux/if_vlan.h>
+#include <linux/kernel.h>
#include "ess_edma.h"
#include "edma.h"
extern struct net_device *edma_netdev[EDMA_MAX_PORTID_SUPPORTED];
bool edma_stp_rstp;
u16 edma_ath_eth_type;
+extern u8 edma_dscp2ac_tbl[EDMA_PRECEDENCE_MAX];
+extern u8 edma_per_prec_stats_enable;
/* edma_skb_priority_offset()
* get edma skb priority
@@ -795,6 +798,14 @@
erdr->pending_fill = ret_count;
}
+ /*
+ * We increment per-precedence counters for the rx packets
+ */
+ if (edma_per_prec_stats_enable) {
+ edma_cinfo->edma_ethstats.rx_prec[priority]++;
+ edma_cinfo->edma_ethstats.rx_ac[edma_dscp2ac_tbl[priority]]++;
+ }
+
/* At this point skb should go to stack */
napi_gro_receive(napi, skb);
}
@@ -1372,6 +1383,37 @@
}
}
+ /* If sysctl support for per-precedence stats are enabled */
+ if (edma_per_prec_stats_enable) {
+ struct iphdr *ip_hdr = NULL;
+ struct ipv6hdr *ip6_hdr = NULL;
+ uint8_t precedence = 0xff;
+
+ if (likely(htons(ETH_P_IP) == skb->protocol)) {
+ ip_hdr = (struct iphdr *)skb_network_header(skb);
+ if (ip_hdr && ((ip_hdr->protocol == IPPROTO_UDP) || (ip_hdr->protocol == IPPROTO_TCP)))
+ precedence = ip_hdr->tos >> EDMA_DSCP_PREC_SHIFT;
+
+ /* Increment per-precedence counters for tx packets
+ * and set the precedence in the TPD.
+ */
+ edma_cinfo->edma_ethstats.tx_prec[precedence]++;
+ edma_cinfo->edma_ethstats.tx_ac[edma_dscp2ac_tbl[precedence]]++;
+ tpd->word3 |= precedence << EDMA_TPD_PRIO_SHIFT;
+ } else if (htons(ETH_P_IPV6) == skb->protocol) {
+ ip6_hdr = (struct ipv6hdr *)skb_network_header(skb);
+ if (ip6_hdr && ((ip6_hdr->nexthdr == IPPROTO_UDP) || (ip6_hdr->nexthdr == IPPROTO_TCP)))
+ precedence = ip6_hdr->priority >> EDMA_DSCP6_PREC_SHIFT;
+
+ /* Increment per-precedence counters for tx packets
+ * and set the precedence in the TPD for v6 packets.
+ */
+ edma_cinfo->edma_ethstats.tx_prec[precedence]++;
+ edma_cinfo->edma_ethstats.tx_ac[edma_dscp2ac_tbl[precedence]]++;
+ tpd->word3 |= precedence << EDMA_TPD_PRIO_SHIFT;
+ }
+ }
+
/* If tpd or sw_desc is still unitiialized then we need to return */
if ((!tpd) || (!sw_desc))
return -EINVAL;
diff --git a/edma.h b/edma.h
index f58f7fe..330d261 100644
--- a/edma.h
+++ b/edma.h
@@ -89,6 +89,9 @@
/* tpd word 3 bit 18-28 */
#define EDMA_TPD_PORT_BITMAP_SHIFT 18
+/* tpd word 3 bit 29-31 */
+#define EDMA_TPD_PRIO_SHIFT 29
+
#define EDMA_TPD_FROM_CPU_SHIFT 25
#define EDMA_FROM_CPU_MASK 0x80
@@ -179,6 +182,16 @@
#define EDMA_GMAC_NO_MDIO_PHY PHY_MAX_ADDR
+#define EDMA_PRECEDENCE_MAX 8
+
+#define EDMA_AC_BK 0 /* Access Category: Background */
+#define EDMA_AC_BE 1 /* Access Category: Best Effort */
+#define EDMA_AC_VI 2 /* Access Category: Video */
+#define EDMA_AC_VO 3 /* Access Category: Voice */
+#define EDMA_AC_MAX 4
+
+#define EDMA_DSCP2AC_INPUT_PARAMS_MAX 2
+
extern int ssdk_rfs_ipct_rule_set(__be32 ip_src, __be32 ip_dst,
__be16 sport, __be16 dport,
uint8_t proto, u16 loadbalance, bool action);
@@ -234,6 +247,10 @@
u64 rx_q7_byte;
u64 tx_desc_error;
u64 rx_alloc_fail_ctr;
+ u64 tx_prec[EDMA_PRECEDENCE_MAX];
+ u64 rx_prec[EDMA_PRECEDENCE_MAX];
+ u64 rx_ac[EDMA_AC_MAX];
+ u64 tx_ac[EDMA_AC_MAX];
};
struct edma_mdio_data {
@@ -465,4 +482,14 @@
void edma_change_rx_coalesce(int usecs);
void edma_get_tx_rx_coalesce(u32 *reg_val);
void edma_clear_irq_status(void);
+
+int edma_dscp2ac_mapping_update(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos);
+int edma_per_prec_stats_enable_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos);
+int edma_prec_stats_reset_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos);
#endif /* _EDMA_H_ */
diff --git a/edma_axi.c b/edma_axi.c
index c6722bd..f4ec659 100644
--- a/edma_axi.c
+++ b/edma_axi.c
@@ -73,6 +73,11 @@
char edma_tx_irq[16][64];
char edma_rx_irq[8][64];
struct net_device *edma_netdev[EDMA_MAX_PORTID_SUPPORTED];
+
+extern u8 edma_dscp2ac_tbl[EDMA_PRECEDENCE_MAX];
+extern u8 edma_per_prec_stats_enable;
+extern u8 edma_prec_stats_reset;
+
static struct phy_device *edma_phydev[EDMA_MAX_PORTID_SUPPORTED];
static int edma_link_detect_bmp;
static int phy_dev_state[EDMA_MAX_PORTID_SUPPORTED];
@@ -1004,6 +1009,27 @@
.mode = 0644,
.proc_handler = edma_disable_rss_func
},
+ {
+ .procname = "dscp2ac",
+ .data = &edma_dscp2ac_tbl,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = edma_dscp2ac_mapping_update
+ },
+ {
+ .procname = "per_prec_stats_enable",
+ .data = &edma_per_prec_stats_enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = edma_per_prec_stats_enable_handler,
+ },
+ {
+ .procname = "per_prec_stats_reset",
+ .data = &edma_prec_stats_reset,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = edma_prec_stats_reset_handler,
+ },
{}
};
@@ -1597,6 +1623,12 @@
edma_stats_timer.function = edma_statistics_timer; /* timer handler */
add_timer(&edma_stats_timer);
+ /*
+ * Initialize dscp2ac mapping table
+ */
+ for (i = 0 ; i < EDMA_PRECEDENCE_MAX ; i++)
+ edma_dscp2ac_tbl[i] = EDMA_AC_BE;
+
return 0;
edma_phy_attach_fail:
diff --git a/edma_ethtool.c b/edma_ethtool.c
index b257d68..4c20e40 100644
--- a/edma_ethtool.c
+++ b/edma_ethtool.c
@@ -78,6 +78,30 @@
{"rx_q7_byte", EDMA_STAT(rx_q7_byte)},
{"tx_desc_error", EDMA_STAT(tx_desc_error)},
{"rx_alloc_fail_ctr", EDMA_STAT(rx_alloc_fail_ctr)},
+ {"rx_prec_0", EDMA_STAT(rx_prec[0])},
+ {"rx_prec_1", EDMA_STAT(rx_prec[1])},
+ {"rx_prec_2", EDMA_STAT(rx_prec[2])},
+ {"rx_prec_3", EDMA_STAT(rx_prec[3])},
+ {"rx_prec_4", EDMA_STAT(rx_prec[4])},
+ {"rx_prec_5", EDMA_STAT(rx_prec[5])},
+ {"rx_prec_6", EDMA_STAT(rx_prec[6])},
+ {"rx_prec_7", EDMA_STAT(rx_prec[7])},
+ {"rx_ac_bk", EDMA_STAT(rx_ac[EDMA_AC_BK])},
+ {"rx_ac_be", EDMA_STAT(rx_ac[EDMA_AC_BE])},
+ {"rx_ac_vi", EDMA_STAT(rx_ac[EDMA_AC_VI])},
+ {"rx_ac_vo", EDMA_STAT(rx_ac[EDMA_AC_VO])},
+ {"tx_prec_0", EDMA_STAT(tx_prec[0])},
+ {"tx_prec_1", EDMA_STAT(tx_prec[1])},
+ {"tx_prec_2", EDMA_STAT(tx_prec[2])},
+ {"tx_prec_3", EDMA_STAT(tx_prec[3])},
+ {"tx_prec_4", EDMA_STAT(tx_prec[4])},
+ {"tx_prec_5", EDMA_STAT(tx_prec[5])},
+ {"tx_prec_6", EDMA_STAT(tx_prec[6])},
+ {"tx_prec_7", EDMA_STAT(tx_prec[7])},
+ {"tx_ac_bk", EDMA_STAT(tx_ac[EDMA_AC_BK])},
+ {"tx_ac_be", EDMA_STAT(tx_ac[EDMA_AC_BE])},
+ {"tx_ac_vi", EDMA_STAT(tx_ac[EDMA_AC_VI])},
+ {"tx_ac_vo", EDMA_STAT(tx_ac[EDMA_AC_VO])},
};
#define EDMA_STATS_LEN ARRAY_SIZE(edma_gstrings_stats)
diff --git a/edma_stats.c b/edma_stats.c
new file mode 100644
index 0000000..961e56c
--- /dev/null
+++ b/edma_stats.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2017, 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 "edma.h"
+#include "ess_edma.h"
+
+#define EDMA_SYSCTL_RBUF_MAX_SIZE 300
+#define EDMA_SYSCTL_WBUF_MAX_SIZE 7
+
+extern struct net_device *edma_netdev[EDMA_MAX_PORTID_SUPPORTED];
+
+const char *edma_access_category[] = {
+ "BK",
+ "BE",
+ "VI",
+ "VO",
+};
+
+u8 edma_dscp2ac_tbl[EDMA_PRECEDENCE_MAX];
+u8 edma_per_prec_stats_enable __read_mostly = 0;
+u8 edma_prec_stats_reset __read_mostly = 0;
+
+/* edma_dscp2ac_mapping_update()
+ * Map dscp to user-provided Access category values
+ */
+int edma_dscp2ac_mapping_update(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ char *tokens[EDMA_DSCP2AC_INPUT_PARAMS_MAX];
+ char *readbuf = NULL;
+ size_t add_len = 0;
+ int len, i, ret;
+ u8 precedence, ac;
+ char writebuf[EDMA_SYSCTL_WBUF_MAX_SIZE];
+ loff_t w_offset = 0;
+ char *str;
+ int count;
+
+ if (write) {
+ add_len = simple_write_to_buffer(writebuf, sizeof(writebuf), &w_offset, buffer, EDMA_SYSCTL_WBUF_MAX_SIZE - 1);
+ writebuf[add_len] = '\0';
+
+ count = 0;
+ str = writebuf;
+ tokens[count] = strsep(&str, " ");
+ while (tokens[count] != NULL) {
+ count++;
+ if (count == EDMA_DSCP2AC_INPUT_PARAMS_MAX) {
+ break;
+ }
+
+ tokens[count] = strsep(&str, " ");
+ }
+
+ ret = sscanf(tokens[0], "%hhu", &precedence);
+ if (ret != 1) {
+ pr_err("Failed to read precedence token\n");
+ return -EFAULT;
+ }
+
+ ret = sscanf(tokens[1], "%hhu", &ac);
+ if (ret != 1) {
+ pr_err("Failed to read AC token\n");
+ return -EFAULT;
+ }
+
+ /* Use first 3-bits of the 6-bit input DSCP for precedence */
+ precedence = precedence >> 3;
+
+ if (precedence > 7 || ac > 3 ) {
+ pr_info(" Invalid Input. Valid Range: precedence - 0-7, ac - 0-3\n");
+ return -EINVAL;
+ }
+
+ /* Update access category in the global dscp table */
+ edma_dscp2ac_tbl[precedence] = ac;
+
+ return 0;
+ }
+
+ readbuf = vmalloc(EDMA_SYSCTL_RBUF_MAX_SIZE);
+ if (!readbuf) {
+ pr_err("Failed to allocate Memory\n");
+ return -EINVAL;
+ }
+
+ len = scnprintf(readbuf + add_len, 45 ,"%s \n", "precedence: 0 1 2 3 4 5 6 7\n");
+ add_len += len;
+ len = scnprintf(readbuf + add_len, 15, "%s", "AC: ");
+ add_len += len;
+
+ for (i = 0; i < EDMA_PRECEDENCE_MAX; i++) {
+ len = scnprintf(readbuf + add_len, 6, " %s ", edma_access_category[edma_dscp2ac_tbl[i]]);
+ add_len += len;
+ }
+
+ len = scnprintf(readbuf + add_len, 4, "\n\n");
+ add_len += len;
+
+ /* Add Usage/help details in the read buffer */
+ len = scnprintf(readbuf + add_len, 40, "Usage: echo \"<dscp> <AC>\" > dscp2ac\n");
+ add_len += len;
+ len = scnprintf(readbuf + add_len, 37, "dscp: 6 bits dscp value in Decimal\n");
+ add_len += len;
+ len = scnprintf(readbuf + add_len, 55, "AC: 0 --> BK\n 1 --> BE\n 2 --> VI\n 3 --> VO\n");
+ add_len += len;
+
+ add_len = simple_read_from_buffer(buffer, *lenp, ppos, readbuf, add_len);
+ *lenp = add_len;
+
+ vfree(readbuf);
+
+ return 0;
+}
+
+/* edma_per_prec_stats_enable_handler()
+ * Enable per precedence statistics
+ */
+int edma_per_prec_stats_enable_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec(table, write, buffer, lenp, ppos);
+
+ if ((!write) || (ret))
+ return ret;
+
+ switch (edma_per_prec_stats_enable) {
+ case 0:
+ case 1:
+ break;
+ default:
+ pr_err("Invalid input. Valid values: <0|1>\n");
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+/* edma_prec_stats_reset_handler()
+ * Reset per-precedence statistics
+ */
+int edma_prec_stats_reset_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ struct edma_adapter *adapter;
+ struct edma_common_info *edma_cinfo;
+ int ret;
+
+ if (!edma_netdev[0]) {
+ pr_err("Invalid Netdevice\n");
+ return -1;
+ }
+
+ adapter = netdev_priv(edma_netdev[0]);
+ edma_cinfo = adapter->edma_cinfo;
+
+ ret = proc_dointvec(table, write, buffer, lenp, ppos);
+
+ if ((!write) || (ret))
+ return ret;
+
+ switch (edma_prec_stats_reset) {
+ case 0:
+ break;
+ case 1:
+ memset(&edma_cinfo->edma_ethstats.tx_prec, 0, sizeof(u64) * EDMA_PRECEDENCE_MAX);
+ memset(&edma_cinfo->edma_ethstats.rx_prec, 0, sizeof(u64) * EDMA_PRECEDENCE_MAX);
+ memset(&edma_cinfo->edma_ethstats.tx_ac, 0, sizeof(u64) * EDMA_AC_MAX);
+ memset(&edma_cinfo->edma_ethstats.rx_ac, 0, sizeof(u64) * EDMA_AC_MAX);
+ break;
+ default:
+ pr_err("Invalid input\n");
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
diff --git a/ess_edma.h b/ess_edma.h
index bd5ef0b..e5a5738 100644
--- a/ess_edma.h
+++ b/ess_edma.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014 - 2017, 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
@@ -333,4 +333,8 @@
#define EDMA_RRD_PORT_TYPE_MASK 0x1F
#define EDMA_RRD_L4OFFSET_SHIFT 4
#define EDMA_RRD_L4OFFSET_MASK 0xFF
+
+/* Shift Bit positions for 3-bit precedence */
+#define EDMA_DSCP_PREC_SHIFT 5
+#define EDMA_DSCP6_PREC_SHIFT 1
#endif /* _ESS_EDMA_H_ */