| /* |
| * 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; |
| } |
| |