/* Calculate offsets for entry */ staticvoid vcap_data_offset_get(conststruct vcap_props *vcap, struct vcap_data *data, int ix)
{ int num_subwords_per_entry, num_subwords_per_action; int i, col, offset, num_entries_per_row, base;
u32 width = vcap->tg_width;
switch (data->tg_sw) { case VCAP_TG_FULL:
num_entries_per_row = 1; break; case VCAP_TG_HALF:
num_entries_per_row = 2; break; case VCAP_TG_QUARTER:
num_entries_per_row = 4; break; default: return;
}
col = (ix % num_entries_per_row);
num_subwords_per_entry = (vcap->sw_count / num_entries_per_row);
base = (vcap->sw_count - col * num_subwords_per_entry -
num_subwords_per_entry);
data->tg_value = 0;
data->tg_mask = 0; for (i = 0; i < num_subwords_per_entry; i++) {
offset = ((base + i) * width);
data->tg_value |= (data->tg_sw << offset);
data->tg_mask |= GENMASK(offset + width - 1, offset);
}
/* Calculate key/action/counter offsets */
col = (num_entries_per_row - col - 1);
data->key_offset = (base * vcap->entry_width) / vcap->sw_count;
data->counter_offset = (num_subwords_per_entry * col *
vcap->counter_width);
i = data->type;
width = vcap->action_table[i].width;
num_subwords_per_action = vcap->action_table[i].count;
data->action_offset = ((num_subwords_per_action * col * width) /
num_entries_per_row);
data->action_offset += vcap->action_type_width;
}
staticvoid vcap_data_set(u32 *data, u32 offset, u32 len, u32 value)
{
u32 i, v, m;
for (i = 0; i < len; i++, offset++) {
v = data[offset / ENTRY_WIDTH];
m = (1 << (offset % ENTRY_WIDTH)); if (value & (1 << i))
v |= m; else
v &= ~m;
data[offset / ENTRY_WIDTH] = v;
}
}
static u32 vcap_data_get(u32 *data, u32 offset, u32 len)
{
u32 i, v, m, value = 0;
for (i = 0; i < len; i++, offset++) {
v = data[offset / ENTRY_WIDTH];
m = (1 << (offset % ENTRY_WIDTH)); if (v & m)
value |= (1 << i);
} return value;
}
int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix)
{ struct qos_policer_conf pp = {0}; struct vcap_policer_entry *tmp, *n;
u8 z = 0;
list_for_each_entry_safe(tmp, n, &ocelot->vcap_pol.pol_list, list) if (tmp->pol_ix == pol_ix) {
z = refcount_dec_and_test(&tmp->refcount); if (z) {
list_del(&tmp->list);
kfree(tmp);
}
}
staticstruct ocelot_vcap_filter*
ocelot_vcap_block_find_filter_by_index(struct ocelot_vcap_block *block, int index)
{ struct ocelot_vcap_filter *tmp; int i = 0;
list_for_each_entry(tmp, &block->rules, list) { if (i == index) return tmp;
++i;
}
/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based *ondestinationandsourceMACaddresses,butonlyonhigher-levelprotocol *information.TheonlyframetypestomatchonkeyscontainingMACaddresses *inthiscasearenon-SNAP,non-ARP,non-IPandnon-OAMframes. * *If@on=true,thentheaboveframetypes(SNAP,ARP,IPandOAM)willmatch *onMAC_ETYPEkeyssuchasdestinationandsourceMAConthisingressport. *Howeverthesettinghasthesideeffectofmakingtheseframesnotmatching *onany_other_keysthanMAC_ETYPEones.
*/ staticvoid ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port, int lookup, bool on)
{
u32 val = 0;
if (on)
val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(BIT(lookup)) |
ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(BIT(lookup)) |
ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(BIT(lookup)) |
ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(BIT(lookup)) |
ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(BIT(lookup));
staticbool
ocelot_vcap_is_problematic_mac_etype(struct ocelot_vcap_filter *filter)
{
u16 proto, mask;
if (filter->key_type != OCELOT_VCAP_KEY_ETYPE) returnfalse;
proto = ntohs(*(__be16 *)filter->key.etype.etype.value);
mask = ntohs(*(__be16 *)filter->key.etype.etype.mask);
/* ETH_P_ALL match, so all protocols below are included */ if (mask == 0) returntrue; if (proto == ETH_P_ARP) returntrue; if (proto == ETH_P_IP) returntrue; if (proto == ETH_P_IPV6) returntrue;
returnfalse;
}
staticbool
ocelot_vcap_is_problematic_non_mac_etype(struct ocelot_vcap_filter *filter)
{ if (filter->key_type == OCELOT_VCAP_KEY_SNAP) returntrue; if (filter->key_type == OCELOT_VCAP_KEY_ARP) returntrue; if (filter->key_type == OCELOT_VCAP_KEY_IPV4) returntrue; if (filter->key_type == OCELOT_VCAP_KEY_IPV6) returntrue; returnfalse;
}
/* We only have the S2_IP_TCPUDP_DIS set of knobs for VCAP IS2 */ if (filter->block_id != VCAP_IS2) returntrue;
if (ocelot_vcap_is_problematic_mac_etype(filter)) { /* Search for any non-MAC_ETYPE rules on the port */ for (i = 0; i < block->count; i++) {
tmp = ocelot_vcap_block_find_filter_by_index(block, i); if (tmp->ingress_port_mask & filter->ingress_port_mask &&
tmp->lookup == filter->lookup &&
ocelot_vcap_is_problematic_non_mac_etype(tmp)) returnfalse;
}
for_each_set_bit(port, &filter->ingress_port_mask,
ocelot->num_phys_ports)
ocelot_match_all_as_mac_etype(ocelot, port,
filter->lookup, true);
} elseif (ocelot_vcap_is_problematic_non_mac_etype(filter)) { /* Search for any MAC_ETYPE rules on the port */ for (i = 0; i < block->count; i++) {
tmp = ocelot_vcap_block_find_filter_by_index(block, i); if (tmp->ingress_port_mask & filter->ingress_port_mask &&
tmp->lookup == filter->lookup &&
ocelot_vcap_is_problematic_mac_etype(tmp)) returnfalse;
}
int ocelot_vcap_filter_add(struct ocelot *ocelot, struct ocelot_vcap_filter *filter, struct netlink_ext_ack *extack)
{ struct ocelot_vcap_block *block = &ocelot->block[filter->block_id]; int i, index, ret;
if (!ocelot_exclusive_mac_etype_filter_rules(ocelot, filter)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules, use the other IS2 lookup"); return -EBUSY;
}
/* Add filter to the linked list */
ret = ocelot_vcap_filter_add_to_block(ocelot, block, filter, extack); if (ret) return ret;
/* Get the index of the inserted filter */
index = ocelot_vcap_block_get_filter_index(block, filter); if (index < 0) return index;
/* Move down the rules to make place for the new filter */ for (i = block->count - 1; i > index; i--) { struct ocelot_vcap_filter *tmp;
tmp = ocelot_vcap_block_find_filter_by_index(block, i); /* Read back the filter's counters before moving it */
vcap_entry_get(ocelot, i - 1, tmp);
vcap_entry_set(ocelot, i, tmp);
}
/* Now insert the new filter */
vcap_entry_set(ocelot, index, filter); return0;
}
EXPORT_SYMBOL(ocelot_vcap_filter_add);
list_for_each_entry_safe(tmp, n, &block->rules, list) { if (ocelot_vcap_filter_equal(filter, tmp)) {
ocelot_vcap_filter_del_aux_resources(ocelot, tmp);
list_del(&tmp->list);
kfree(tmp);
}
}
block->count--;
}
int ocelot_vcap_filter_del(struct ocelot *ocelot, struct ocelot_vcap_filter *filter)
{ struct ocelot_vcap_block *block = &ocelot->block[filter->block_id]; struct ocelot_vcap_filter del_filter; int i, index;
/* Need to inherit the block_id so that vcap_entry_set() *doesnotgetconfusedandknowswheretoinstallit.
*/
memset(&del_filter, 0, sizeof(del_filter));
del_filter.block_id = filter->block_id;
/* Gets index of the filter */
index = ocelot_vcap_block_get_filter_index(block, filter); if (index < 0) return index;
/* Move up all the blocks over the deleted filter */ for (i = index; i < block->count; i++) { struct ocelot_vcap_filter *tmp;
tmp = ocelot_vcap_block_find_filter_by_index(block, i); /* Read back the filter's counters before moving it */
vcap_entry_get(ocelot, i + 1, tmp);
vcap_entry_set(ocelot, i, tmp);
}
/* Now delete the last filter, because it is duplicated */
vcap_entry_set(ocelot, block->count, &del_filter);
return0;
}
EXPORT_SYMBOL(ocelot_vcap_filter_del);
int ocelot_vcap_filter_replace(struct ocelot *ocelot, struct ocelot_vcap_filter *filter)
{ struct ocelot_vcap_block *block = &ocelot->block[filter->block_id]; int index;
index = ocelot_vcap_block_get_filter_index(block, filter); if (index < 0) return index;
staticvoid ocelot_vcap_detect_constants(struct ocelot *ocelot, struct vcap_props *vcap)
{ int counter_memory_width; int num_default_actions; int version;
version = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_VCAP_VER); /* Only version 0 VCAP supported for now */ if (WARN_ON(version != 0)) return;
/* Width in bits of type-group field */
vcap->tg_width = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ENTRY_TG_WIDTH); /* Number of subwords per TCAM row */
vcap->sw_count = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ENTRY_SWCNT); /* Number of rows in TCAM. There can be this many full keys, or double *thisnumberhalfkeys,or4timesthisnumberquarterkeys.
*/
vcap->entry_count = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ENTRY_CNT); /* Assuming there are 4 subwords per TCAM row, their layout in the *actualTCAM(notinthecache)wouldbe: * *|SW3|TG3|SW2|TG2|SW1|TG1|SW0|TG0| * *(whereSW=subwordandTG=Type-Group). * *WhatVCAP_CONST_ENTRY_CNTisgivingusisthewidthofonefullTCAM *row.ButwhensoftwareaccessestheTCAMthroughthecache *registers,theType-Groupvaluesarewrittenthroughanothersetof *registersVCAP_TG_DAT,andtherefore,itappearsasthoughthe4 *subwordsarecontiguousinthecachememory. *Importantmention:regardlessofthenumberofkeyentriesperrow *(andthereforeofkeysize:1fullkeyor2halfkeysor4quarter *keys),softwarealwayshastoconfigure4Type-Groupvalues.For *example,inthecaseof1fullkey,thedriverneedstosetall4 *Type-Grouptobefullkey. * *Forthisreason,weneedtofixupthevaluethatthehardwareis *givingus.Wedon'tactuallycareaboutthewidthoftheentryin *theTCAM.Whatwecareaboutisthewidthoftheentryinthecache *registers,whichishowwegettointeractwithit.Andsincethe *VCAP_ENTRY_DATcacheregistersaccessonlythesubwordsandnotthe *Type-Groups,thismeansweneedtosubtractthewidthofthe *Type-GroupswhenpackingandunpackingkeyentrydatainaTCAMrow.
*/
vcap->entry_width = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ENTRY_WIDTH);
vcap->entry_width -= vcap->tg_width * vcap->sw_count;
num_default_actions = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ACTION_DEF_CNT);
vcap->action_count = vcap->entry_count + num_default_actions;
vcap->action_width = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_ACTION_WIDTH); /* The width of the counter memory, this is the complete width of all *counter-fieldsassociatedwithonefull-wordentry.Thereisone *counterperentrysub-word(seeCAP_CORE::ENTRY_SWCNTfornumberof *subwords.)
*/
vcap->counter_words = vcap->sw_count;
counter_memory_width = ocelot_target_read(ocelot, vcap->target,
VCAP_CONST_CNT_WIDTH);
vcap->counter_width = counter_memory_width / vcap->counter_words;
}
int ocelot_vcap_init(struct ocelot *ocelot)
{ struct qos_policer_conf cpu_drop = {
.mode = MSCC_QOS_RATE_MODE_DATA,
}; int ret, i;
/* Create a policer that will drop the frames for the cpu. *Thispolicerwillbeusedasactionintheaclrulestodrop *frames.
*/
ret = qos_policer_conf_set(ocelot, OCELOT_POLICER_DISCARD, &cpu_drop); if (ret) return ret;
for (i = 0; i < OCELOT_NUM_VCAP_BLOCKS; i++) { struct ocelot_vcap_block *block = &ocelot->block[i]; struct vcap_props *vcap = &ocelot->vcap[i];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.