Skip to content

Commit

Permalink
Adding support for metering
Browse files Browse the repository at this point in the history
- Metering P4 model changes in BMV2
- Changes to support the SAI api generation for metering tables
  • Loading branch information
vijasrin committed May 11, 2023
1 parent 778eb99 commit a50336b
Show file tree
Hide file tree
Showing 23 changed files with 392 additions and 58 deletions.
32 changes: 25 additions & 7 deletions dash-pipeline/SAI/sai_api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,26 @@ def p4_annotation_to_sai_attr(p4rt, sai_attr):
sai_attr['type'] = kv['value']['stringValue']
elif kv['key'] == 'isresourcetype':
sai_attr['isresourcetype'] = kv['value']['stringValue']
elif kv['key'] == 'isreadonly':
sai_attr['isreadonly'] = kv['value']['stringValue']
elif kv['key'] == 'objects':
sai_attr['objectName'] = kv['value']['stringValue']
elif kv['key'] == 'isattribute':
sai_attr['isattribute'] = kv['value']['stringValue']
else:
print("Unknown attr annotation " + kv['key'])
exit(1)
sai_attr['field'] = sai_type_to_field[sai_attr['type']]

def p4_annotation_to_sai_table(p4rt, sai_table):
for anno in p4rt[STRUCTURED_ANNOTATIONS_TAG]:
if anno[NAME_TAG] == SAI_TAG:
for kv in anno[KV_PAIR_LIST_TAG][KV_PAIRS_TAG]:
if kv['key'] == 'isobject':
sai_table['is_object'] = kv['value']['stringValue']
if kv['key'] == 'ignoretable':
sai_table['ignore_table'] = kv['value']['stringValue']

def get_sai_key_type(key_size, key_header, key_field):
if key_size == 1:
return 'bool', "booldata"
Expand Down Expand Up @@ -252,7 +265,11 @@ def generate_sai_apis(program, ignore_tables):
sai_table_data[ACTIONS_TAG] = []
sai_table_data[ACTION_PARAMS_TAG] = []

if STRUCTURED_ANNOTATIONS_TAG in table['preamble']:
p4_annotation_to_sai_table(table['preamble'], sai_table_data)
table_control, table_name = table[PREAMBLE_TAG][NAME_TAG].split('.', 1)
if 'ignore_table' in sai_table_data.keys():
ignore_tables.append(table_name)
if table_name in ignore_tables:
continue

Expand Down Expand Up @@ -300,13 +317,14 @@ def generate_sai_apis(program, ignore_tables):
fill_action_params(sai_table_data[ACTION_PARAMS_TAG], param_names, all_actions[action_id])
sai_table_data[ACTIONS_TAG].append(all_actions[action_id])

if len(sai_table_data['keys']) == 1 and sai_table_data['keys'][0]['sai_key_name'].endswith(table_name.split('.')[-1] + '_id'):
sai_table_data['is_object'] = 'true'
elif len(sai_table_data['keys']) > 5:
sai_table_data['is_object'] = 'true'
else:
sai_table_data['is_object'] = 'false'
sai_table_data['name'] = sai_table_data['name'] + '_entry'
if 'is_object' not in sai_table_data.keys():
if len(sai_table_data['keys']) == 1 and sai_table_data['keys'][0]['sai_key_name'].endswith(table_name.split('.')[-1] + '_id'):
sai_table_data['is_object'] = 'true'
elif len(sai_table_data['keys']) > 5:
sai_table_data['is_object'] = 'true'
else:
sai_table_data['is_object'] = 'false'
sai_table_data['name'] = sai_table_data['name'] + '_entry'

table_names.append(sai_table_data[NAME_TAG])
is_new_api = True
Expand Down
2 changes: 2 additions & 0 deletions dash-pipeline/SAI/templates/saiapi.cpp.j2
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ sai_status_t sai_create_{{ table.name }}(
for (uint32_t i = 0; i < attr_count; i++) {
switch(attr_list[i].id) {
{% for key in table['keys'] %}
{% if key.isattribute != 'false' %}
case SAI_{{ table.name | upper }}_ATTR_{{ key.sai_key_name | upper }}: {
auto mf = matchActionEntry->add_match();
mf->set_field_id({{key.id}});
Expand Down Expand Up @@ -118,6 +119,7 @@ sai_status_t sai_create_{{ table.name }}(
{% endif %}
break;
}
{% endif %}
{% endfor %}
{% if table['keys'] | selectattr('match_type', 'ne', 'exact') | list | length > 0 %}
{% if table['keys'] | selectattr('match_type', 'eq', 'lpm') | list | length == 0 %}
Expand Down
8 changes: 8 additions & 0 deletions dash-pipeline/SAI/templates/saiapi.h.j2
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ typedef enum _sai_{{ table.name }}_attr_t
{% if table.is_object == 'true' %}
{% if table['keys'] | length > 1 %}
{% for key in table['keys'] %}
{% if key.isattribute != 'false' %}
/**
* @brief {{ key.match_type | capitalize | replace('Lpm', 'LPM') }} matched key {{ key.sai_key_name }}
*
Expand Down Expand Up @@ -149,6 +150,7 @@ typedef enum _sai_{{ table.name }}_attr_t
*/
SAI_{{ table.name | upper }}_ATTR_{{ key.sai_key_name | upper }}_MASK,

{% endif %}
{% endif %}
{% endfor %}
{% endif %}
Expand All @@ -158,7 +160,11 @@ typedef enum _sai_{{ table.name }}_attr_t
* @brief Action {% for action in param.paramActions %}{{ action }}{{ ", " if not loop.last else "" }}{% endfor %} parameter {{ param.name | upper }}
*
* @type {{ param.type }}
{% if param.isreadonly == 'true' %}
* @flags READ_ONLY
{% else %}
* @flags CREATE_AND_SET
{% endif %}
{% if param.type == 'sai_uint16_t' %}
* @isvlan false
{% endif %}
Expand All @@ -177,8 +183,10 @@ typedef enum _sai_{{ table.name }}_attr_t
{% elif param.field == 's32' %}
* @default {{ param.default }}
{% else %}
{% if param.isreadonly != 'true' %}
* @default 0
{% endif %}
{% endif %}
{% if table.actions | length > 1 %}
{% if param.paramActions | length > 0 %}
* @validonly {% for action in param.paramActions %}SAI_{{ table.name | upper }}_ATTR_ACTION == SAI_{{ table.name | upper }}_ACTION_{{ action | upper }}{{ " or " if not loop.last else "" }}{% endfor %}
Expand Down
21 changes: 21 additions & 0 deletions dash-pipeline/SAI/templates/utils.h.j2
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ void ipaddrSetVal(const sai_ip_address_t &value, T &t, int bits = -1){
}
}

template<typename T>
void ipaddrSetMask(const sai_attribute_value_t &value, T &t, int bits = -1){
ipaddrSetMask(value.ipaddr, t);
}

template<typename T>
void ipaddrSetMask(const sai_ip_address_t &value, T &t, int bits = -1){
switch(value.addr_family) {
case SAI_IP_ADDR_FAMILY_IPV4: {
uint32_t mask = value.addr.ip4;
t->set_mask(&mask, 4);
}
break;
case SAI_IP_ADDR_FAMILY_IPV6: {
t->set_mask(const_cast<uint8_t*>(&value.addr.ip6[0]), 16);
}
break;
default: assert(0 && "unrecognzed value.ipaddr.addr_family");
}
}

template<typename T>
void macSetVal(const sai_attribute_value_t &value, T &t, int bits = -1){
t->set_value(const_cast<uint8_t*>(&value.mac[0]), 6);
Expand Down
7 changes: 7 additions & 0 deletions dash-pipeline/bmv2/dash_metadata.p4
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ struct metadata_t {
bit<16> stage3_dash_acl_group_id;
bit<16> stage4_dash_acl_group_id;
bit<16> stage5_dash_acl_group_id;
bit<1> meter_policy_en;
bit<1> mapping_meter_class_override;
bit<16> meter_policy_id;
bit<16> policy_meter_class;
bit<16> route_meter_class;
bit<16> mapping_meter_class;
bit<16> meter_class;
tag_map_t src_tag_map;
tag_map_t dst_tag_map;
}
Expand Down
30 changes: 25 additions & 5 deletions dash-pipeline/bmv2/dash_outbound.p4
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,32 @@
control outbound(inout headers_t hdr,
inout metadata_t meta)
{
action route_vnet(bit<16> dst_vnet_id) {
action set_route_meter_attrs(bit<1> meter_policy_en,
bit<16> meter_class) {
meta.meter_policy_en = meter_policy_en;
meta.meter_class = meter_class;
}
action route_vnet(bit<16> dst_vnet_id,
bit<1> meter_policy_en,
bit<16> meter_class) {
meta.dst_vnet_id = dst_vnet_id;
set_route_meter_attrs(meter_policy_en, meter_class);
}

action route_vnet_direct(bit<16> dst_vnet_id,
bit<1> is_overlay_ip_v4_or_v6,
IPv4ORv6Address overlay_ip) {
IPv4ORv6Address overlay_ip,
bit<1> meter_policy_en,
bit<16> meter_class) {
meta.dst_vnet_id = dst_vnet_id;
meta.lkup_dst_ip_addr = overlay_ip;
meta.is_lkup_dst_ip_v6 = is_overlay_ip_v4_or_v6;
set_route_meter_attrs(meter_policy_en, meter_class);
}

action route_direct() {
action route_direct(bit<1> meter_policy_en,
bit<16> meter_class) {
set_route_meter_attrs(meter_policy_en, meter_class);
/* send to underlay router without any encap */
}

Expand All @@ -42,7 +55,9 @@ control outbound(inout headers_t hdr,
bit<1> is_underlay_sip_v4_or_v6,
IPv4ORv6Address underlay_sip,
dash_encapsulation_t dash_encapsulation,
bit<24> tunnel_key) {
bit<24> tunnel_key,
bit<1> meter_policy_en,
bit<16> meter_class) {
/* Assume the overlay addresses provided are always IPv6 and the original are IPv4 */
/* assert(is_overlay_dip_v4_or_v6 == 1 && is_overlay_sip_v4_or_v6 == 1);
assert(is_overlay_dip_mask_v4_or_v6 == 1 && is_overlay_sip_mask_v4_or_v6 == 1);
Expand All @@ -62,6 +77,7 @@ control outbound(inout headers_t hdr,
meta.encap_data.overlay_dmac = hdr.ethernet.dst_addr;
meta.encap_data.dash_encapsulation = dash_encapsulation;
meta.encap_data.service_tunnel_key = tunnel_key;
set_route_meter_attrs(meter_policy_en, meter_class);
}

#ifdef TARGET_BMV2_V1MODEL
Expand Down Expand Up @@ -104,11 +120,15 @@ control outbound(inout headers_t hdr,

action set_tunnel_mapping(IPv4Address underlay_dip,
EthernetAddress overlay_dmac,
bit<1> use_dst_vnet_vni) {
bit<1> use_dst_vnet_vni,
bit<16> meter_class,
bit<1> meter_class_override) {
if (use_dst_vnet_vni == 1)
meta.vnet_id = meta.dst_vnet_id;
meta.encap_data.overlay_dmac = overlay_dmac;
meta.encap_data.underlay_dip = underlay_dip;
meta.meter_class = meter_class;
meta.mapping_meter_class_override = meter_class_override;
}

#ifdef TARGET_BMV2_V1MODEL
Expand Down
119 changes: 119 additions & 0 deletions dash-pipeline/bmv2/dash_pipeline.p4
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ control dash_ingress(
@Sai[type="sai_uint32_t"]
bit<24> vm_vni,
bit<16> vnet_id,
bit<16> v4_meter_policy_id,
bit<16> v6_meter_policy_id,
ACL_GROUPS_PARAM(inbound_v4),
ACL_GROUPS_PARAM(inbound_v6),
ACL_GROUPS_PARAM(outbound_v4),
Expand All @@ -133,12 +135,14 @@ control dash_ingress(
} else {
ACL_GROUPS_COPY_TO_META(inbound_v6);
}
meta.meter_policy_id = v6_meter_policy_id;
} else {
if (meta.direction == dash_direction_t.OUTBOUND) {
ACL_GROUPS_COPY_TO_META(outbound_v4);
} else {
ACL_GROUPS_COPY_TO_META(inbound_v4);
}
meta.meter_policy_id = v4_meter_policy_id;
}
}

Expand Down Expand Up @@ -231,6 +235,98 @@ control dash_ingress(
const default_action = deny;
}

action set_meter_policy_attrs(@Sai[type="sai_ip_addr_family_t", isresourcetype="true"] bit<32> ip_addr_family) {
if (ip_addr_family == 0) /* SAI_IP_ADDR_FAMILY_IPV4 */ {
if (meta.is_overlay_ip_v6 == 1) {
meta.dropped = true;
}
} else {
if (meta.is_overlay_ip_v6 == 0) {
meta.dropped = true;
}
}
}

@name("meter_policy|dash_meter")
@Sai[isobject="true"]
table meter_policy {
key = {
meta.meter_policy_id : exact @name("meta.meter_policy_id:meter_policy_id");
}
actions = {
set_meter_policy_attrs();
}
}

action meter_rule_action(bit<16> meter_class) {
meta.policy_meter_class = meter_class;
}

@name("meter_rule|dash_meter")
@Sai[isobject="true"]
table meter_rule {
key = {
meta.meter_policy_id: exact @name("meta.meter_policy_id:meter_policy_id") @Sai[type="sai_object_id_t", isresourcetype="true", objects="METER_POLICY"];
hdr.ipv4.dst_addr : ternary @name("hdr.ipv4.dst_addr:dip");
}

actions = {
meter_rule_action;
@defaultonly deny;
}
const default_action = deny;
}

action meter_bucket_action(
@Sai[type="sai_uint32_t", isreadonly="true"] bit<32> outbound_bytes_counter,
@Sai[type="sai_uint32_t", isreadonly="true"] bit<32> inbound_bytes_counter) {
// read only counters for SAI api generation
}

@name("meter_bucket|dash_meter")
@Sai[isobject="true"]
table meter_bucket {
key = {
meta.eni_id: exact @name("meta.eni_id:eni_id");
meta.meter_class: exact @name("meta.meter_class:meter_class");
}
actions = {
meter_bucket_action;
}
}

#ifdef TARGET_BMV2_V1MODEL
direct_counter(CounterType.bytes) meter_bucket_outbound_counter;
#endif
@name("meter_bucket_outbound|dash_meter")
@Sai[ignoretable="true"]
table meter_bucket_outbound {
key = {
meta.meter_class: exact @name("meta.meter_class:meter_class");
meta.eni_id: exact @name("meta.eni_id:eni_id");
}
actions = { NoAction; }
#ifdef TARGET_BMV2_V1MODEL
counters = meter_bucket_outbound_counter;
#endif
}

#ifdef TARGET_BMV2_V1MODEL
direct_counter(CounterType.bytes) meter_bucket_inbound_counter;
#endif
@name("meter_bucket_inbound|dash_meter")
@Sai[ignoretable="true"]
table meter_bucket_inbound {
key = {
meta.meter_class: exact @name("meta.meter_class:meter_class");
meta.eni_id: exact @name("meta.eni_id:eni_id");
}
actions = { NoAction; }
#ifdef TARGET_BMV2_V1MODEL
counters = meter_bucket_inbound_counter;
#endif
}

action set_eni(bit<16> eni_id) {
meta.eni_id = eni_id;
}
Expand Down Expand Up @@ -388,6 +484,29 @@ control dash_ingress(
inbound.apply(hdr, meta);
}

if (meta.meter_policy_en == 1) {
meter_policy.apply();
meter_rule.apply();
}

{
if (meta.meter_policy_en == 1) {
meta.meter_class = meta.policy_meter_class;
} else {
meta.meter_class = meta.route_meter_class;
}
if ((meta.meter_class == 0) || (meta.mapping_meter_class_override == 1)) {
meta.meter_class = meta.mapping_meter_class;
}
}

meter_bucket.apply();
if (meta.direction == dash_direction_t.OUTBOUND) {
meter_bucket_outbound.apply();
} else if (meta.direction == dash_direction_t.INBOUND) {
meter_bucket_inbound.apply();
}

eni_meter.apply();

if (meta.dropped) {
Expand Down
8 changes: 8 additions & 0 deletions dash-pipeline/tests/libsai/vnet_out/vnet_out.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ int main(int argc, char **argv)
attr.value.u32 = vnet_id;
attrs.push_back(attr);

attr.id = SAI_ENI_ATTR_V4_METER_POLICY_ID;
attr.value.oid = SAI_NULL_OBJECT_ID;
attrs.push_back(attr);

attr.id = SAI_ENI_ATTR_V6_METER_POLICY_ID;
attr.value.oid = SAI_NULL_OBJECT_ID;
attrs.push_back(attr);

std::unordered_map<uint32_t, uint16_t> acl_group_ids = {
{SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID, in_acl_group_id},
{SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID, in_acl_group_id},
Expand Down
Loading

0 comments on commit a50336b

Please sign in to comment.