blob: bb0615b6587cdec627261b49a426e350696a618d [file] [log] [blame]
/*
* Copyright (c) 2009 Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "module_ethernet.h"
#include "fe.h"
#include "module_Rx.h"
#include "module_stat.h"
#include "layer2.h"
#include "module_timer.h"
#include "module_bridge.h"
#if !defined (COMCERTO_2000)
PVOID bridge_snapshot_alloc(U32 size)
{
return Heap_Alloc(size);
}
void bridge_snapshot_free(PVOID p)
{
Heap_Free(p);
}
#else
extern U32 L2Bridge_timeout;
PVOID bridge_snapshot_alloc(U32 size)
{
return pfe_kzalloc(size, GFP_KERNEL);
}
void bridge_snapshot_free(PVOID p)
{
pfe_kfree(p);
}
#endif
/* This function returns total bridge entries configured
in a given hash index */
static int rx_Get_Hash_BridgeEntries(int bridge_hash_index)
{
int tot_bridge_entries = 0;
PL2Bridge_entry pEntry;
struct slist_entry *entry;
slist_for_each(pEntry, entry, &bridge_cache[bridge_hash_index], list)
{
tot_bridge_entries++;
}
return tot_bridge_entries;
}
/* This function fills the snapshot of bridge entries
in a given hash index */
static int rx_Bridge_Get_Hash_Snapshot(int bt_hash_index, int br_tot_entries, PL2BridgeQueryEntryResponse pBridgeSnapshot)
{
int tot_bridge_entries=0;
PL2Bridge_entry pBridgeEntry;
struct slist_entry *entry;
slist_for_each(pBridgeEntry, entry, &bridge_cache[bt_hash_index], list)
{
if(br_tot_entries > 0)
{
pBridgeSnapshot->input_interface = pBridgeEntry->input_interface;
pBridgeSnapshot->input_vlan = htons(pBridgeEntry->input_vlan);
pBridgeSnapshot->output_interface = pBridgeEntry->output_itf->index;
pBridgeSnapshot->output_vlan = htons(pBridgeEntry->output_vlan);
SFL_memcpy(pBridgeSnapshot->destaddr, pBridgeEntry->da, 2 * ETHER_ADDR_LEN);
pBridgeSnapshot->ethertype = htons(pBridgeEntry->ethertype);
pBridgeSnapshot->pkt_priority = pBridgeEntry->pkt_priority == 0xFF ? 0x8000 : pBridgeEntry->pkt_priority;
pBridgeSnapshot->vlan_priority = pBridgeEntry->vlan_priority_mark ? 0x8000 : pBridgeEntry->vlan_priority;
pBridgeSnapshot->session_id = htons(pBridgeEntry->session_id);
pBridgeSnapshot->qmod = pBridgeEntry->qmod;
SFL_memcpy(pBridgeSnapshot->input_name, get_onif_name(phy_port[pBridgeEntry->input_interface].itf.index), 16);
SFL_memcpy(pBridgeSnapshot->output_name, get_onif_name(pBridgeEntry->output_itf->index), 16);
pBridgeSnapshot++;
tot_bridge_entries++;
br_tot_entries--;
}
}
return tot_bridge_entries;
}
/* This function creates the snapshot memory and returns the
next bridge entry from the snapshop of the bridge entries of a
single hash to the caller */
int rx_Get_Next_Hash_BridgeEntry(PL2BridgeQueryEntryResponse pBridgeCmd, int reset_action)
{
int bridge_hash_entries;
PL2BridgeQueryEntryResponse pBridge;
static PL2BridgeQueryEntryResponse pBridgeSnapshot = NULL;
static int bridge_hash_index = 0, bridge_snapshot_entries =0, bridge_snapshot_index=0, bridge_snapshot_buf_entries = 0;
if(reset_action)
{
bridge_hash_index = 0;
bridge_snapshot_entries = 0;
bridge_snapshot_index = 0;
if(pBridgeSnapshot)
{
bridge_snapshot_free(pBridgeSnapshot);
pBridgeSnapshot = NULL;
}
bridge_snapshot_buf_entries = 0;
return NO_ERR;
}
if (bridge_snapshot_index == 0)
{
while( bridge_hash_index < NUM_BT_ENTRIES)
{
bridge_hash_entries = rx_Get_Hash_BridgeEntries(bridge_hash_index);
if(bridge_hash_entries == 0)
{
bridge_hash_index++;
continue;
}
if(bridge_hash_entries > bridge_snapshot_buf_entries)
{
if(pBridgeSnapshot)
bridge_snapshot_free(pBridgeSnapshot);
pBridgeSnapshot = bridge_snapshot_alloc(bridge_hash_entries * sizeof(L2BridgeQueryEntryResponse));
if (!pBridgeSnapshot)
{
bridge_hash_index = 0;
bridge_snapshot_buf_entries = 0;
return ERR_NOT_ENOUGH_MEMORY;
}
bridge_snapshot_buf_entries = bridge_hash_entries;
}
bridge_snapshot_entries = rx_Bridge_Get_Hash_Snapshot(bridge_hash_index , bridge_hash_entries, pBridgeSnapshot);
break;
}
if (bridge_hash_index >= NUM_BT_ENTRIES)
{
bridge_hash_index = 0;
if(pBridgeSnapshot)
{
bridge_snapshot_free(pBridgeSnapshot);
pBridgeSnapshot = NULL;
}
bridge_snapshot_buf_entries = 0;
return ERR_BRIDGE_ENTRY_NOT_FOUND;
}
}
pBridge = &pBridgeSnapshot[bridge_snapshot_index++];
SFL_memcpy(pBridgeCmd, pBridge, sizeof(L2BridgeQueryEntryResponse));
if (bridge_snapshot_index == bridge_snapshot_entries)
{
bridge_snapshot_index = 0;
bridge_hash_index ++;
}
return NO_ERR;
}
#ifdef CFG_STATS
/* This function fills the snapshot of bridge entries
in a given hash index */
static int stat_Bridge_Get_Hash_Snapshot(int stat_bt_hash_index, int stat_tot_br_entries, PStatBridgeEntryResponse pStatBridgeSnapshot)
{
int stat_bridge_entries=0;
PL2Bridge_entry pStatBridgeEntry;
struct slist_entry *entry;
slist_for_each(pStatBridgeEntry, entry, &bridge_cache[stat_bt_hash_index], list)
{
if(stat_tot_br_entries > 0)
{
pStatBridgeSnapshot->eof = 0;
pStatBridgeSnapshot->input_interface = pStatBridgeEntry->input_interface;
pStatBridgeSnapshot->input_vlan = htons(pStatBridgeEntry->input_vlan);
pStatBridgeSnapshot->output_interface = pStatBridgeEntry->output_itf->index;
pStatBridgeSnapshot->output_vlan = htons(pStatBridgeEntry->output_vlan);
SFL_memcpy(pStatBridgeSnapshot->dst_mac, pStatBridgeEntry->da, 2 * ETHER_ADDR_LEN);
pStatBridgeSnapshot->etherType = htons(pStatBridgeEntry->ethertype);
pStatBridgeSnapshot->session_id = htons(pStatBridgeEntry->session_id);
#if !defined (COMCERTO_2000)
pStatBridgeSnapshot->total_packets_transmitted = pStatBridgeEntry->total_packets_transmitted;
#else
{
struct _thw_L2Bridge_entry *hw_l2bridge = pStatBridgeEntry->hw_l2bridge;
class_statistics_get_ddr(hw_l2bridge->total_packets_transmitted,
&pStatBridgeSnapshot->total_packets_transmitted,
sizeof(pStatBridgeSnapshot->total_packets_transmitted),
gStatBridgeQueryStatus & STAT_BRIDGE_QUERY_RESET);
}
#endif
SFL_memcpy(pStatBridgeSnapshot->input_name, get_onif_name(phy_port[pStatBridgeEntry->input_interface].itf.index), 16);
SFL_memcpy(pStatBridgeSnapshot->output_name, get_onif_name(pStatBridgeEntry->output_itf->index), 16);
#if !defined (COMCERTO_2000)
if(gStatBridgeQueryStatus & STAT_BRIDGE_QUERY_RESET)
pStatBridgeEntry->total_packets_transmitted = 0;
#endif
pStatBridgeSnapshot++;
stat_bridge_entries++;
stat_tot_br_entries--;
}
}
return stat_bridge_entries;
}
/* This function creates the snapshot memory and returns the
next bridge entry from the snapshop of the bridge entries of a
single hash to the caller */
int rx_Get_Next_Hash_Stat_BridgeEntry(PStatBridgeEntryResponse pStatBridgeCmd, int reset_action)
{
int stat_bridge_hash_entries;
PStatBridgeEntryResponse pStatBridge;
static PStatBridgeEntryResponse pStatBridgeSnapshot = NULL;
static int stat_bridge_hash_index = 0, stat_bridge_snapshot_entries =0, stat_bridge_snapshot_index=0, stat_bridge_snapshot_buf_entries = 0;
if(reset_action)
{
stat_bridge_hash_index = 0;
stat_bridge_snapshot_entries = 0;
stat_bridge_snapshot_index = 0;
if(pStatBridgeSnapshot)
{
bridge_snapshot_free(pStatBridgeSnapshot);
pStatBridgeSnapshot = NULL;
}
stat_bridge_snapshot_buf_entries = 0;
return NO_ERR;
}
if (stat_bridge_snapshot_index == 0)
{
while( stat_bridge_hash_index < NUM_BT_ENTRIES)
{
stat_bridge_hash_entries = rx_Get_Hash_BridgeEntries(stat_bridge_hash_index);
if(stat_bridge_hash_entries == 0)
{
stat_bridge_hash_index++;
continue;
}
if(stat_bridge_hash_entries > stat_bridge_snapshot_buf_entries)
{
if(pStatBridgeSnapshot)
bridge_snapshot_free(pStatBridgeSnapshot);
pStatBridgeSnapshot = bridge_snapshot_alloc(stat_bridge_hash_entries * sizeof(StatBridgeEntryResponse));
if (!pStatBridgeSnapshot)
{
stat_bridge_hash_index = 0;
stat_bridge_snapshot_buf_entries = 0;
return ERR_NOT_ENOUGH_MEMORY;
}
stat_bridge_snapshot_buf_entries = stat_bridge_hash_entries;
}
stat_bridge_snapshot_entries = stat_Bridge_Get_Hash_Snapshot(stat_bridge_hash_index ,stat_bridge_hash_entries, pStatBridgeSnapshot);
break;
}
if (stat_bridge_hash_index >= NUM_BT_ENTRIES)
{
stat_bridge_hash_index = 0;
if(pStatBridgeSnapshot)
{
bridge_snapshot_free(pStatBridgeSnapshot);
pStatBridgeSnapshot = NULL;
}
stat_bridge_snapshot_buf_entries = 0;
return ERR_BRIDGE_ENTRY_NOT_FOUND;
}
}
pStatBridge = &pStatBridgeSnapshot[stat_bridge_snapshot_index++];
SFL_memcpy(pStatBridgeCmd, pStatBridge, sizeof(StatBridgeEntryResponse));
if (stat_bridge_snapshot_index == stat_bridge_snapshot_entries)
{
stat_bridge_snapshot_index = 0;
stat_bridge_hash_index ++;
}
return NO_ERR;
}
#endif /* CFG_STATS */
/* This function returns total L2Flow entries configured
in a given hash index */
static int rx_Get_Hash_L2FlowEntries(int L2Flow_hash_index)
{
int tot_bridge_entries = 0;
PL2Flow_entry pL2FlowEntry;
PL3Flow_entry pL3FlowEntry;
struct slist_entry *entry;
/* Hash index matches L2 Flow table*/
if(L2Flow_hash_index < NUM_BT_ENTRIES)
{
/* Only process L2 only entries */
slist_for_each(pL2FlowEntry, entry, &bridge_cache[L2Flow_hash_index], list)
{
if(!pL2FlowEntry->nb_l3_ref)
tot_bridge_entries++;
}
}
else
{
/* Index matches L3 table, process L3 entries */
slist_for_each(pL3FlowEntry, entry, &bridge_l3_cache[L2Flow_hash_index -NUM_BT_ENTRIES], list)
{
tot_bridge_entries++;
}
}
return tot_bridge_entries;
}
/* This function fills the snapshot of bridge entries
in a given hash index */
static int rx_Get_Hash_Snapshot_L2FlowEntries(int L2Flow_hash_index, int L2Flow_tot_entries, PL2BridgeL2FlowEntryCommand pL2FlowSnapshot)
{
int tot_bridge_entries = 0;
PL2Flow_entry pL2FlowEntry;
PL3Flow_entry pL3FlowEntry;
if(L2Flow_hash_index < NUM_BT_ENTRIES){
pL2FlowEntry = container_of(slist_first(&bridge_cache[L2Flow_hash_index]), typeof(L2Flow_entry), list);
pL3FlowEntry = NULL;
}
else{
pL3FlowEntry = container_of(slist_first(&bridge_l3_cache[L2Flow_hash_index - NUM_BT_ENTRIES]), typeof(L3Flow_entry), list);
pL2FlowEntry = pL3FlowEntry->l2flow_entry;
}
while ((pL2FlowEntry) && (L2Flow_tot_entries > 0)) {
memset(pL2FlowSnapshot, 0 , sizeof(L2BridgeL2FlowEntryCommand));
/* L2 info */
SFL_memcpy(pL2FlowSnapshot->destaddr, pL2FlowEntry->l2flow.da, 2 * ETHER_ADDR_LEN);
pL2FlowSnapshot->ethertype = pL2FlowEntry->l2flow.ethertype;
pL2FlowSnapshot->vlan_tag = pL2FlowEntry->l2flow.vlan_tag;
pL2FlowSnapshot->session_id = pL2FlowEntry->l2flow.session_id;
SFL_memcpy(pL2FlowSnapshot->input_name, get_onif_name(pL2FlowEntry->input_ifindex), INTERFACE_NAME_LENGTH);
SFL_memcpy(pL2FlowSnapshot->output_name, get_onif_name(pL2FlowEntry->output_if->index), INTERFACE_NAME_LENGTH);
/* L3-4 info */
if(pL3FlowEntry){
SFL_memcpy(pL2FlowSnapshot->saddr, pL3FlowEntry->l3flow.saddr, IPV6_ADDRESS_LENGTH);
SFL_memcpy(pL2FlowSnapshot->daddr, pL3FlowEntry->l3flow.daddr, IPV6_ADDRESS_LENGTH);
pL2FlowSnapshot->proto = pL3FlowEntry->l3flow.proto;
pL2FlowSnapshot->sport = pL3FlowEntry->l3flow.sport;
pL2FlowSnapshot->dport = pL3FlowEntry->l3flow.dport;
pL2FlowSnapshot->timeout = (L2Bridge_timeout - (ct_timer - pL3FlowEntry->last_l3flow_timer)) / CT_TICKS_PER_SECOND;
}
else
pL2FlowSnapshot->timeout = (L2Bridge_timeout - (ct_timer - pL2FlowEntry->last_l2flow_timer)) / CT_TICKS_PER_SECOND;
/* Get next entry */
if(pL3FlowEntry){
pL3FlowEntry = container_of(slist_next(&pL3FlowEntry->list), typeof(L3Flow_entry), list);
if(pL3FlowEntry)
pL2FlowEntry = pL3FlowEntry->l2flow_entry;
else
pL2FlowEntry = NULL;
}
else
pL2FlowEntry = container_of(slist_next(&pL2FlowEntry->list), typeof(L2Flow_entry), list);
pL2FlowSnapshot++;
tot_bridge_entries++;
L2Flow_tot_entries--;
}
return tot_bridge_entries;
}
/* This function creates the snapshot memory and returns the
next bridge entry from the snapshop of the bridge entries of a
single hash to the caller
Both L2 and L3 tables are linearly parsed */
int rx_Get_Next_Hash_L2FlowEntry(PL2BridgeL2FlowEntryCommand pL2FlowCmd, int reset_action)
{
int L2Flow_hash_entries;
static PL2BridgeL2FlowEntryCommand pL2FlowSnapshot = NULL;
static int L2Flow_hash_index = 0, L2Flow_snapshot_entries = 0,
L2Flow_snapshot_index = 0, L2Flow_snapshot_buf_entries = 0;
/* Reset */
if(reset_action){
L2Flow_hash_index = 0;
L2Flow_snapshot_entries = 0;
L2Flow_snapshot_index = 0;
if(pL2FlowSnapshot){
bridge_snapshot_free(pL2FlowSnapshot);
pL2FlowSnapshot = NULL;
}
L2Flow_snapshot_buf_entries = 0;
}
if (L2Flow_snapshot_index == 0){
while( L2Flow_hash_index < (NUM_BT_ENTRIES + NUM_BT_L3_ENTRIES)){
/* Get next non-null bucket */
L2Flow_hash_entries = rx_Get_Hash_L2FlowEntries(L2Flow_hash_index);
if(L2Flow_hash_entries == 0){
L2Flow_hash_index++;
continue;
}
/* Alloc snapshot buffer if needed */
if(L2Flow_hash_entries > L2Flow_snapshot_buf_entries){
if(pL2FlowSnapshot)
bridge_snapshot_free(pL2FlowSnapshot);
pL2FlowSnapshot = bridge_snapshot_alloc(L2Flow_hash_entries * sizeof(L2BridgeL2FlowEntryCommand));
if (!pL2FlowSnapshot){
L2Flow_hash_index = 0;
L2Flow_snapshot_buf_entries = 0;
return ERR_NOT_ENOUGH_MEMORY;
}
L2Flow_snapshot_buf_entries = L2Flow_hash_entries;
}
L2Flow_snapshot_entries = rx_Get_Hash_Snapshot_L2FlowEntries(L2Flow_hash_index , L2Flow_hash_entries, pL2FlowSnapshot);
break;
}
/* No more entries */
if (L2Flow_hash_index >= (NUM_BT_ENTRIES + NUM_BT_L3_ENTRIES)){
L2Flow_hash_index = 0;
if(pL2FlowSnapshot){
bridge_snapshot_free(pL2FlowSnapshot);
pL2FlowSnapshot = NULL;
}
L2Flow_snapshot_buf_entries = 0;
return ERR_BRIDGE_ENTRY_NOT_FOUND;
}
}
SFL_memcpy(pL2FlowCmd, &pL2FlowSnapshot[L2Flow_snapshot_index++], sizeof(L2BridgeL2FlowEntryCommand));
if (L2Flow_snapshot_index == L2Flow_snapshot_entries){
L2Flow_snapshot_index = 0;
L2Flow_hash_index ++;
}
return NO_ERR;
}