| /******************************************************************************* |
| Copyright (C) Marvell International Ltd. and its affiliates |
| |
| This software file (the "File") is owned and distributed by Marvell |
| International Ltd. and/or its affiliates ("Marvell") under the following |
| alternative licensing terms. Once you have made an election to distribute the |
| File under one of the following license alternatives, please (i) delete this |
| introductory statement regarding license alternatives, (ii) delete the two |
| license alternatives that you have not elected to use and (iii) preserve the |
| Marvell copyright notice above. |
| |
| |
| ******************************************************************************** |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| *******************************************************************************/ |
| #include <linux/stddef.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/errno.h> |
| #include <linux/reboot.h> |
| #include <linux/pci.h> |
| #include <linux/kdev_t.h> |
| #include <linux/major.h> |
| #include <linux/console.h> |
| #include <linux/delay.h> |
| #include <linux/ide.h> |
| #include <linux/seq_file.h> |
| |
| #include <asm/system.h> |
| #include <asm/dma.h> |
| #include <asm/io.h> |
| |
| #include <linux/netdevice.h> |
| #include "mvCommon.h" |
| #include "mvOs.h" |
| #include "eth/gbe/mvEthGbe.h" |
| #include "ctrlEnv/mvCtrlEnvLib.h" |
| #include "mv_eth_proc.h" |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| #include "../nfp_mgr/mv_nfp_mgr.h" |
| #endif |
| |
| #include "mv_netdev.h" |
| |
| //#define MV_DEBUG |
| #ifdef MV_DEBUG |
| #define DP printk |
| #else |
| #define DP(fmt,args...) |
| #endif |
| |
| |
| /* global variables from 'regdump' */ |
| static struct proc_dir_entry *mv_eth_tool; |
| |
| static unsigned int port = 0, q = 0, weight = 0, status = 0, mac[6] = {0,}; |
| static unsigned int policy =0, command = 0, packet = 0; |
| static unsigned int value = 0; |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| static unsigned int dip, sip, inport, outport; |
| static unsigned int da[6] = {0,}, sa[6] = {0,}; |
| static unsigned int db_type; |
| #endif /* CONFIG_MV_ETH_NFP */ |
| |
| void run_com_srq(void) |
| { |
| void* port_hndl = mvEthPortHndlGet(port); |
| |
| if(port_hndl == NULL) |
| return; |
| |
| if(q >= MV_ETH_RX_Q_NUM) |
| q = -1; |
| |
| switch(packet) { |
| case PT_BPDU: |
| mvEthBpduRxQueue(port_hndl, q); |
| break; |
| case PT_ARP: |
| mvEthArpRxQueue(port_hndl, q); |
| break; |
| case PT_TCP: |
| mvEthTcpRxQueue(port_hndl, q); |
| break; |
| case PT_UDP: |
| mvEthUdpRxQueue(port_hndl, q); |
| break; |
| default: |
| printk("eth proc unknown packet type.\n"); |
| } |
| |
| } |
| |
| extern void ethMcastAdd(int port, char* macStr, int queue); |
| void run_com_sq(void) { |
| |
| char mac_addr[20]; |
| |
| if(q >= MV_ETH_RX_Q_NUM) |
| q = -1; |
| |
| sprintf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); |
| ethMcastAdd(port, mac_addr, q); |
| } |
| |
| extern void ethPortStatus (int port); |
| extern void ethPortQueues( int port, int rxQueue, int txQueue, int mode); |
| extern void ethPortMcast(int port); |
| extern void ethPortUcastShow(int port); |
| extern void ethPortRegs(int port); |
| extern void ethTxPolicyRegs(int port); |
| extern void ethPortCounters(int port); |
| extern void ethPortRmonCounters(int port); |
| |
| #ifdef CONFIG_MV_ETH_NFP_DUAL |
| extern void eth_remote_port_status_print(int port, int mode); |
| #endif |
| |
| void run_com_stats(void) { |
| printk("\n\n#########################################################################################\n\n"); |
| switch(status) { |
| case STS_PORT: |
| printk(" PORT %d: GET ETH STATUS\n\n",port); |
| mv_eth_status_print(port); |
| ethPortStatus(port); |
| break; |
| |
| case STS_PORT_MAC: |
| ethPortUcastShow(port); |
| ethPortMcast(port); |
| break; |
| |
| case STS_PORT_Q: |
| printk(" PORT %d: GET ETH STATUS ON Q %d\n\n",port,q); |
| ethPortQueues(port, q, q, 1); |
| break; |
| |
| #if (MV_ETH_RX_Q_NUM > 1) |
| case STS_PORT_RXP: |
| printk(" PORT %d: GET ETH RX POLICY STATUS\n\n",port); |
| printk("Not supported\n"); |
| break; |
| #endif /* MV_ETH_RX_Q_NUM > 1 */ |
| |
| case STS_PORT_TOS_MAP: |
| mv_eth_tos_map_show(port); |
| break; |
| |
| case STS_PORT_TXP: |
| printk(" PORT %d: GET ETH TX POLICY STATUS\n\n",port); |
| ethTxPolicyRegs(port); |
| break; |
| |
| case STS_PORT_REGS: |
| printk(" PORT %d: GET ETH PORT REGS STATUS\n\n",port); |
| ethPortRegs(port); |
| break; |
| |
| case STS_PORT_MIB: |
| ethPortCounters(port); |
| ethPortRmonCounters(port); |
| break; |
| |
| case STS_PORT_STATS: |
| printk(" PORT %d: GET ETH STATISTIC STATUS\n\n",port); |
| mv_eth_stats_print(port); |
| break; |
| |
| case STS_NETDEV: |
| mv_eth_netdev_print(port); |
| break; |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| case STS_PORT_NFP_STATS: |
| printk(" PORT %d: NFP statistics\n\n",port); |
| mv_eth_nfp_stats_print(port); |
| break; |
| #endif /* CONFIG_MV_ETH_NFP */ |
| |
| #ifdef CONFIG_MV_GATEWAY |
| case STS_SWITCH_STATS: |
| mv_gtw_switch_stats(port); |
| break; |
| #endif /* CONFIG_MV_GATEWAY */ |
| |
| default: |
| printk(" Unknown status command \n"); |
| } |
| #ifdef CONFIG_MV_ETH_NFP_DUAL |
| eth_remote_port_status_print(port, status); |
| #endif |
| } |
| |
| int run_eth_com(const char *buffer) { |
| |
| int scan_count; |
| scan_count = sscanf(buffer, ETH_CMD_STRING, ETH_SCANF_LIST); |
| if( scan_count != ETH_LIST_LEN) { |
| printk("eth command bad format %x != %x\n", scan_count, ETH_LIST_LEN ); |
| return 1; |
| } |
| switch(command) { |
| |
| case COM_TXDONE_Q: |
| mv_eth_tx_done_quota = value; |
| break; |
| |
| #ifdef CONFIG_MV_ETH_SKB_REUSE |
| case COM_SKB_REUSE: |
| eth_skb_reuse_enable = value; |
| break; |
| #endif /* CONFIG_MV_ETH_SKB_REUSE */ |
| |
| #ifdef CONFIG_NET_SKB_RECYCLE |
| case COM_SKB_RECYCLE: |
| eth_skb_recycle_enable = value; |
| break; |
| #endif /* CONFIG_NET_SKB_RECYCLE */ |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| case COM_NFP: |
| if (value) |
| { |
| printk("Enabling NFP\n"); |
| fp_mgr_enable(); |
| } |
| else |
| { |
| printk("Disabling NFP\n"); |
| fp_mgr_disable(); |
| } |
| break; |
| #endif /* CONFIG_MV_ETH_NFP */ |
| |
| default: |
| printk(" Unknown ETH command \n"); |
| } |
| return 0; |
| } |
| |
| /* Giga Queue commands */ |
| int run_port_queue_cmd(const char *buffer) { |
| |
| int scan_count; |
| |
| scan_count = sscanf(buffer, QUEUE_CMD_STRING, QUEUE_SCANF_LIST); |
| |
| if( scan_count != QUEUE_LIST_LEN) { |
| printk("eth port/queue command bad format %x != %x\n", scan_count, QUEUE_LIST_LEN ); |
| return 1; |
| } |
| |
| switch(command) { |
| case COM_TOS_MAP: |
| mv_eth_tos_map_set(port, value, q); |
| break; |
| |
| default: |
| printk(" Unknown port/queue command \n"); |
| } |
| return 0; |
| } |
| |
| /* Giga Port commands */ |
| int run_port_com(const char *buffer) { |
| |
| int scan_count; |
| void* port_hndl; |
| |
| scan_count = sscanf(buffer, PORT_CMD_STRING, PORT_SCANF_LIST); |
| |
| if( scan_count != PORT_LIST_LEN) { |
| printk("eth port command bad format %x != %x\n", scan_count, PORT_LIST_LEN ); |
| return 1; |
| } |
| if( (port < 0) || (port > mvCtrlEthMaxPortGet()) ) |
| return 1; |
| |
| port_hndl = mvEthPortHndlGet(port); |
| if(port_hndl == NULL) |
| return 1; |
| |
| switch(command) { |
| case COM_RX_COAL: |
| mvEthRxCoalSet(mvEthPortHndlGet(port), value); |
| break; |
| |
| case COM_TX_COAL: |
| mvEthTxCoalSet(mvEthPortHndlGet(port), value); |
| break; |
| |
| #ifdef ETH_MV_TX_EN |
| case COM_TX_EN: |
| if(value > CONFIG_MV_ETH_NUM_OF_RX_DESCR) |
| { |
| printk("Eth TX_EN command bad param: value=%d\n", value); |
| return 1; |
| } |
| |
| eth_tx_en_config(port, value); |
| break; |
| #endif /* ETH_MV_TX_EN */ |
| |
| #if (MV_ETH_VERSION >= 4) |
| case COM_EJP_MODE: |
| mvEthEjpModeSet(mvEthPortHndlGet(port), value); |
| break; |
| #endif /* (MV_ETH_VERSION >= 4) */ |
| case COM_LRO: |
| mv_eth_set_lro(port, value); |
| break; |
| case COM_LRO_DESC: |
| mv_eth_set_lro_desc(port, value); |
| break; |
| |
| case COM_TX_NOQUEUE: |
| mv_eth_set_noqueue(port, value); |
| break; |
| |
| default: |
| printk(" Unknown port command \n"); |
| } |
| return 0; |
| } |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| int run_ip_rule_set_com(const char *buffer) |
| { |
| int scan_count, i; |
| MV_FP_RULE ip_rule; |
| MV_STATUS status = MV_OK; |
| |
| scan_count = sscanf(buffer, IP_RULE_STRING, IP_RULE_SCANF_LIST); |
| |
| if( scan_count != IP_RULE_LIST_LEN) { |
| printk("eth proc bad format %x != %x\n", scan_count, IP_RULE_LIST_LEN); |
| return 1; |
| } |
| memset(&ip_rule, 0, sizeof(ip_rule)); |
| |
| printk("run_ip_rule_set_com: dip=%08x, sip=%08x, inport=%d, outport=%d\n", |
| dip, sip, inport, outport); |
| |
| ip_rule.routingInfo.dstIp = MV_32BIT_BE(dip); |
| ip_rule.routingInfo.srcIp = MV_32BIT_BE(sip); |
| ip_rule.routingInfo.defGtwIp = MV_32BIT_BE(dip); |
| ip_rule.routingInfo.inIfIndex = inport; |
| ip_rule.routingInfo.outIfIndex = outport; |
| ip_rule.routingInfo.aware_flags = 0; |
| |
| for(i=0; i<MV_MAC_ADDR_SIZE; i++) |
| { |
| ip_rule.routingInfo.dstMac[i] = (MV_U8)(da[i] & 0xFF); |
| ip_rule.routingInfo.srcMac[i] = (MV_U8)(sa[i] & 0xFF);; |
| } |
| ip_rule.mgmtInfo.actionType = MV_FP_ROUTE_CMD; |
| ip_rule.mgmtInfo.ruleType = MV_FP_STATIC_RULE; |
| |
| status = fp_rule_set(&ip_rule); |
| if(status != MV_OK) |
| { |
| printk("fp_rule_set FAILED: status=%d\n", status); |
| } |
| return status; |
| } |
| |
| int run_ip_rule_del_com(const char *buffer) |
| { |
| int scan_count; |
| MV_STATUS status = MV_OK; |
| |
| scan_count = sscanf(buffer, IP_RULE_DEL_STRING, IP_RULE_DEL_SCANF_LIST); |
| |
| if( scan_count != IP_RULE_DEL_LIST_LEN) { |
| printk("eth proc bad format %x != %x\n", scan_count, IP_RULE_DEL_LIST_LEN); |
| return 1; |
| } |
| |
| status = fp_rule_delete(MV_32BIT_BE(sip), MV_32BIT_BE(dip), MV_FP_STATIC_RULE); |
| if(status != MV_OK) |
| { |
| printk("fp_rule_delete FAILED: status=%d\n", status); |
| } |
| return status; |
| } |
| |
| int run_fp_db_print_com(const char *buffer) |
| { |
| int scan_count; |
| MV_STATUS status = MV_OK; |
| |
| scan_count = sscanf(buffer, NFP_DB_PRINT_STRING, NFP_DB_PRINT_SCANF_LIST); |
| |
| if( scan_count != NFP_DB_PRINT_LIST_LEN) { |
| printk("eth proc bad format %x != %x\n", scan_count, NFP_DB_PRINT_LIST_LEN); |
| return 1; |
| } |
| |
| if (db_type == DB_ROUTING) |
| status = fp_rule_db_print(MV_FP_DATABASE); |
| #ifdef CONFIG_MV_ETH_NFP_NAT |
| else if (db_type == DB_NAT) |
| status = fp_nat_db_print(MV_FP_DATABASE); |
| #endif /* CONFIG_MV_ETH_NFP_NA */ |
| #ifdef CONFIG_MV_ETH_NFP_FDB |
| else if (db_type == DB_FDB) |
| status = fp_fdb_db_print(MV_FP_DATABASE); |
| #endif /* CONFIG_MV_ETH_NFP_FDB */ |
| #ifdef CONFIG_MV_ETH_NFP_PPP |
| else if (db_type == DB_PPP) |
| status = fp_ppp_db_print(MV_FP_DATABASE); |
| #endif /* CONFIG_MV_ETH_NFP_PPP */ |
| #ifdef CONFIG_MV_ETH_NFP_SEC |
| else if (db_type == DB_SEC) |
| status = fp_sec_db_print(MV_FP_DATABASE); |
| #endif /* CONFIG_MV_ETH_NFP_SEC */ |
| else { |
| printk("Failed to print rule database: unknown DB type\n"); |
| return 1; |
| } |
| return status; |
| } |
| #endif /* CONFIG_MV_ETH_NFP */ |
| |
| int run_com_general(const char *buffer) { |
| |
| int scan_count; |
| |
| scan_count = sscanf(buffer, PROC_STRING, PROC_SCANF_LIST); |
| |
| if( scan_count != LIST_LEN) { |
| printk("eth proc bad format %x != %x\n", scan_count, LIST_LEN ); |
| return 1; |
| } |
| |
| switch(command){ |
| case COM_SRQ: |
| DP(" Port %x: Got SRQ command Q %x and packet type is %x <bpdu/arp/tcp/udp> \n",port,q,packet); |
| run_com_srq(); |
| break; |
| case COM_SQ: |
| DP(" Port %x: Got SQ command Q %x mac %2x:%2x:%2x:%2x:%2x:%2x\n",port, q, |
| mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| run_com_sq(); |
| break; |
| |
| #if (MV_ETH_RX_Q_NUM > 1) |
| case COM_SRP: |
| DP(" Port %x: Got SRP command Q %x policy %x <Fixed/WRR> \n",port,q,policy); |
| printk("Not supported\n"); |
| break; |
| case COM_SRQW: |
| DP(" Port %x: Got SQRW command Q %x weight %x \n",port,q,weight); |
| printk("Not supported\n"); |
| break; |
| case COM_STP: |
| DP("STP cmd - Unsupported: Port %x Q %x policy %x <WRR/FIXED> weight %x\n",port,q,policy,weight); |
| break; |
| #endif /* MV_ETH_RX_Q_NUM > 1 */ |
| |
| case COM_STS: |
| DP(" Port %x: Got STS command status %x\n",port,status); |
| run_com_stats(); |
| break; |
| default: |
| printk("eth proc unknown command.\n"); |
| } |
| return 0; |
| } |
| |
| int mv_eth_tool_write (struct file *file, const char *buffer, |
| unsigned long count, void *data) { |
| |
| sscanf(buffer,"%x",&command); |
| |
| switch (command) { |
| case COM_RX_COAL: |
| case COM_TX_COAL: |
| case COM_TX_EN: |
| case COM_EJP_MODE: |
| case COM_TX_NOQUEUE: |
| case COM_LRO: |
| case COM_LRO_DESC: |
| run_port_com(buffer); |
| break; |
| case COM_TXDONE_Q: |
| case COM_SKB_REUSE: |
| case COM_SKB_RECYCLE: |
| case COM_NFP: |
| run_eth_com(buffer); |
| break; |
| |
| case COM_TOS_MAP: |
| run_port_queue_cmd(buffer); |
| break; |
| |
| #ifdef CONFIG_MV_ETH_NFP |
| case COM_IP_RULE_SET: |
| run_ip_rule_set_com(buffer); |
| break; |
| case COM_IP_RULE_DEL: |
| run_ip_rule_del_com(buffer); |
| break; |
| case COM_NFP_STATUS: |
| fp_mgr_status(); |
| break; |
| case COM_NFP_PRINT: |
| run_fp_db_print_com(buffer); |
| break; |
| #endif /* CONFIG_MV_ETH_NFP */ |
| |
| default: |
| run_com_general(buffer); |
| break; |
| } |
| return count; |
| } |
| |
| static int proc_calc_metrics(char *page, char **start, off_t off, |
| int count, int *eof, int len) |
| { |
| if (len <= off+count) *eof = 1; |
| *start = page + off; |
| len -= off; |
| if (len>count) len = count; |
| if (len<0) len = 0; |
| return len; |
| } |
| |
| |
| |
| int mv_eth_tool_read (char *page, char **start, off_t off, |
| int count, int *eof, void *data) { |
| unsigned int len = 0; |
| |
| //len = sprintf(page, "\n"); |
| //len += sprintf(page+len, "\n"); |
| |
| return proc_calc_metrics(page, start, off, count, eof, len); |
| } |
| |
| |
| |
| int __init start_mv_eth_tool(void) |
| { |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) |
| mv_eth_tool = proc_net_create(FILE_NAME , 0666 , NULL); |
| #else |
| mv_eth_tool = create_proc_entry(FILE_NAME , 0666 , init_net.proc_net); |
| #endif |
| mv_eth_tool->read_proc = mv_eth_tool_read; |
| mv_eth_tool->write_proc = mv_eth_tool_write; |
| mv_eth_tool->nlink = 1; |
| return 0; |
| } |
| |
| module_init(start_mv_eth_tool); |