blob: 961e56cfe407631c77cd9d73e899cc8efd2cdfa5 [file] [log] [blame]
/*
* 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;
}