/* 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;
}
staticvoid vcap_key_bytes_set(conststruct vcap_props *vcap, struct vcap_data *data, int field,
u8 *val, u8 *msk)
{
u32 offset = vcap->keys[field].offset;
u32 count = vcap->keys[field].length;
u32 i, j, n = 0, value = 0, mask = 0;
WARN_ON(count % 8);
/* Data wider than 32 bits are split up in chunks of maximum 32 bits. * The 32 LSB of the data are written to the 32 MSB of the TCAM.
*/
offset += count;
count /= 8;
for (i = 0; i < count; i++) {
j = (count - i - 1);
value += (val[j] << n);
mask += (msk[j] << n);
n += 8; if (n == ENTRY_WIDTH || (i + 1) == count) {
offset -= n;
vcap_key_field_set(data, offset, n, value, mask);
n = 0;
value = 0;
mask = 0;
}
}
}
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 * on destination and source MAC addresses, but only on higher-level protocol * information. The only frame types to match on keys containing MAC addresses * in this case are non-SNAP, non-ARP, non-IP and non-OAM frames. * * If @on=true, then the above frame types (SNAP, ARP, IP and OAM) will match * on MAC_ETYPE keys such as destination and source MAC on this ingress port. * However the setting has the side effect of making these frames not matching * on any _other_ keys than MAC_ETYPE ones.
*/ 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); return 0;
}
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() * does not get confused and knows where to install it.
*/
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);
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 * this number half keys, or 4 times this number quarter keys.
*/
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 * actual TCAM (not in the cache) would be: * * | SW 3 | TG 3 | SW 2 | TG 2 | SW 1 | TG 1 | SW 0 | TG 0 | * * (where SW=subword and TG=Type-Group). * * What VCAP_CONST_ENTRY_CNT is giving us is the width of one full TCAM * row. But when software accesses the TCAM through the cache * registers, the Type-Group values are written through another set of * registers VCAP_TG_DAT, and therefore, it appears as though the 4 * subwords are contiguous in the cache memory. * Important mention: regardless of the number of key entries per row * (and therefore of key size: 1 full key or 2 half keys or 4 quarter * keys), software always has to configure 4 Type-Group values. For * example, in the case of 1 full key, the driver needs to set all 4 * Type-Group to be full key. * * For this reason, we need to fix up the value that the hardware is * giving us. We don't actually care about the width of the entry in * the TCAM. What we care about is the width of the entry in the cache * registers, which is how we get to interact with it. And since the * VCAP_ENTRY_DAT cache registers access only the subwords and not the * Type-Groups, this means we need to subtract the width of the * Type-Groups when packing and unpacking key entry data in a TCAM row.
*/
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-fields associated with one full-word entry. There is one * counter per entry sub-word (see CAP_CORE::ENTRY_SWCNT for number of * 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. * This policer will be used as action in the acl rules to drop * 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.