On 3/11/26 10:39 AM, Adrián Moreno wrote:
> On Fri, Mar 06, 2026 at 11:22:51AM +0100, Ilya Maximets wrote:
>> Current output of ofproto/detrace doesn't provide information on what
>> bridge the rules and groups belong to.  This may be confusing in
>> setups with multiple bridges, especially when packets are resubmitted
>> multiple times through different patch ports.
>>
>> Translation cache does have the bridge information though as every
>> time we lookup the rule we add the XC_TABLE entry that has the bridge
>> information.  We also know when the rule lookup was executed but
>> didn't result in a match.  We can report that as well, so it's easier
>> to follow the resubmits.
>>
>> Note: On a miss, the packet hits the internal drop rule in table 254.
>> By reporting the fact that there was no match it's easier to
>> understand why that internal rule is getting hit.
>>
>> There is no extra overhead for providing the bridge information as the
>> data is already there, we just need to print it out.  Retis, that is
>> collecting the detrace output, should also be able to take advantage
>> of this enhancement.
> 
> Tested this on Retis and it "just works" :-)
> 
>>
>> Signed-off-by: Ilya Maximets <[email protected]>
>> ---
>>  ofproto/ofproto-dpif-xlate-cache.c | 15 +++++++++
>>  tests/system-traffic.at            | 51 ++++++++++++++++++++++++------
>>  utilities/checkpatch_dict.txt      |  1 +
>>  3 files changed, 57 insertions(+), 10 deletions(-)
>>
>> diff --git a/ofproto/ofproto-dpif-xlate-cache.c 
>> b/ofproto/ofproto-dpif-xlate-cache.c
>> index c6d935cf0..a225a1b70 100644
>> --- a/ofproto/ofproto-dpif-xlate-cache.c
>> +++ b/ofproto/ofproto-dpif-xlate-cache.c
>> @@ -305,6 +305,7 @@ xlate_cache_steal_entries(struct xlate_cache *dst, 
>> struct xlate_cache *src)
>>  void
>>  xlate_xcache_format(struct ds *s, const struct xlate_cache *xcache)
>>  {
>> +    struct ofproto_dpif *last_ofproto = NULL;
>>      struct ofpbuf entries = xcache->entries;
>>      struct xc_entry *entry;
>>      struct ofgroup *ofg;
>> @@ -312,16 +313,30 @@ xlate_xcache_format(struct ds *s, const struct 
>> xlate_cache *xcache)
>>      XC_ENTRY_FOR_EACH (entry, &entries) {
>>          switch (entry->type) {
>>          case XC_RULE:
>> +            ds_put_cstr(s, "  ");
>>              ofproto_rule_stats_ds(s, &entry->rule->up, true);
>>              break;
>> +
>>          case XC_GROUP:
>> +            ds_put_cstr(s, "  ");
> 
> This results in a slightly weird output because groups with dp_hash
> will result in a recirculation with frozen_actions. In these cases,
> after thrawing there is no rule lookup and therefore, no XC_TABLE
> entry, but the action contains the OFP_GROUP which generates the
> XC_GROUP entry.
> 
> The result is ofproto/detrace shows an indented" group entry with
> no "parent" bridge. The unit tests is a perfect example of this.
> 
> Maybe something like this would help:

Makes sense.  I can add something like this (or exactly this) in v2.
Thanks!

> 
> --- i/ofproto/ofproto-dpif-xlate-cache.c
> +++ w/ofproto/ofproto-dpif-xlate-cache.c
> @@ -318,8 +318,14 @@ xlate_xcache_format(struct ds *s, const struct
> xlate_cache *xcache)
>              break;
> 
>          case XC_GROUP:
> -            ds_put_cstr(s, "  ");
>              ofg = &entry->group.group->up;
> +            struct ofproto_dpif *group_ofproto =
> +                CONTAINER_OF(ofg->ofproto, struct ofproto_dpif, up);
> +            if (last_ofproto != group_ofproto) {
> +                ds_put_format(s, "%s\n", group_ofproto->up.name);
> +                last_ofproto = group_ofproto;
> +            }
> +            ds_put_cstr(s, "  ");
>              ofputil_group_format(s, ofg->group_id, ofg->type,
>                                   entry->group.bucket, &ofg->buckets,
>                                   &ofg->props, OFP15_VERSION,
> 
> 
>>              ofg = &entry->group.group->up;
>>              ofputil_group_format(s, ofg->group_id, ofg->type,
>>                                   entry->group.bucket, &ofg->buckets,
>>                                   &ofg->props, OFP15_VERSION,
>>                                   false, NULL, NULL);
>>              break;
>> +
>>          case XC_TABLE:
>> +            if (last_ofproto != entry->table.ofproto) {
>> +                ds_put_format(s, "%s\n", entry->table.ofproto->up.name);
>> +                last_ofproto = entry->table.ofproto;
>> +            }
>> +            if (!entry->table.match) {
>> +                ds_put_format(s, "  table_id=%"PRIu8", no match\n",
>> +                              entry->table.id);
>> +            }
>> +            break;
>> +
>>          case XC_BOND:
>>          case XC_NETDEV:
>>          case XC_NETFLOW:
>> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
>> index 8f4fdf8b1..ea0dc5ec3 100644
>> --- a/tests/system-traffic.at
>> +++ b/tests/system-traffic.at
>> @@ -2733,7 +2733,12 @@ OVS_TRAFFIC_VSWITCHD_STOP
>>  AT_CLEANUP
>>
>>  AT_SETUP([datapath - Dump OF rules corresponding to UFID])
>> -OVS_TRAFFIC_VSWITCHD_START()
>> +OVS_TRAFFIC_VSWITCHD_START(
>> +   [_ADD_BR([br1]) -- \
>> +    add-port br0 patch1 -- set interface patch1 type=patch \
>> +        options:peer=patch0 ofport_request=3 -- \
>> +    add-port br1 patch0 -- set interface patch0 type=patch \
>> +        options:peer=patch1 ofport_request=4 --])
>>
>>  ADD_NAMESPACES(at_ns0, at_ns1)
>>
>> @@ -2749,14 +2754,24 @@ 
>> group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,ac
>>  ])
>>  AT_DATA([flows.txt], [dnl
>>  table=0,arp,actions=NORMAL
>> +table=0,priority=100,cookie=0x1234abcd,in_port=patch1,ip,nw_dst=10.0.0.2,actions=resubmit(,4)
>>  
>> table=0,priority=100,cookie=0x12345678,in_port=ovs-p1,ip,nw_dst=10.0.0.2,actions=resubmit(,1)
>>  
>> table=0,priority=100,cookie=0xabcedf,in_port=ovs-p2,ip,nw_dst=10.0.0.1,actions=ct(table=3)
>>  table=1,priority=200,ip,actions=group:1
>> -table=2,ip,actions=ovs-p2
>> +table=2,ip,actions=ovs-p2,patch1
>>  table=3,ip,actions=ovs-p1
>> +table=4,ip,actions=drop
>> +])
>> +AT_DATA([flows1.txt], [dnl
>> +table=0,arp,actions=drop
>> +table=0,actions=clone(resubmit(,1)),resubmit(,2)
>> +table=1,ip,actions=set_field:10.0.0.3->nw_dst,in_port
>> +table=2,ip,actions=in_port
>>  ])
>> +
>>  AT_CHECK([ovs-ofctl add-groups br0 groups.txt])
>>  AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
>> +AT_CHECK([ovs-ofctl add-flows br1 flows1.txt])
>>
>>  NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.1 -W 2 10.0.0.2 | FORMAT_PING], 
>> [0], [dnl
>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> @@ -2782,8 +2797,8 @@ AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep 
>> "ipv4" | strip_used | strip
>>            strip_duration | strip_dp_hash | strip_recirc | sed 
>> 's/:never/:0.0s/' | \
>>            strip_ptype | strip_eth | sort], [0], [dnl
>>  
>> recirc_id(<recirc>),in_port(ovs-p1),eth_type(0x0800),ipv4(dst=10.0.0.2,frag=no),
>>  packets:0, bytes:0, used:0.0s, actions:hash(l4(0)),recirc(<recirc>)
>> +recirc_id(<recirc>),in_port(ovs-p1),eth_type(0x0800),ipv4(dst=10.0.0.2,proto=1,frag=no),
>>  packets:0, bytes:0, used:0.0s, actions:ovs-p2
>>  recirc_id(<recirc>),in_port(ovs-p1),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:0.0s, actions:ct(commit),recirc(<recirc>)
>> -recirc_id(<recirc>),in_port(ovs-p1),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:0.0s, actions:ovs-p2
>>  
>> recirc_id(<recirc>),in_port(ovs-p2),eth_type(0x0800),ipv4(dst=10.0.0.1,frag=no),
>>  packets:0, bytes:0, used:0.0s, actions:ct,recirc(<recirc>)
>>  recirc_id(<recirc>),in_port(ovs-p2),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:0.0s, actions:ovs-p1
>>  ])
>> @@ -2791,32 +2806,48 @@ 
>> recirc_id(<recirc>),in_port(ovs-p2),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, b
>>  ufid=$(ovs-appctl dpctl/dump-flows -m 
>> filter='recirc_id(0),in_port(ovs-p1),ipv4' | parse_ufid)
>>  AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \
>>            sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl
>> -cookie=0x12345678, n_packets=3, n_bytes=294, 
>> priority=100,ip,in_port=1,nw_dst=10.0.0.2,actions=resubmit(,1)
>> -table_id=1, n_packets=3, n_bytes=294, priority=200,ip,actions=group:1
>> +br0
>> +  cookie=0x12345678, n_packets=3, n_bytes=294, 
>> priority=100,ip,in_port=1,nw_dst=10.0.0.2,actions=resubmit(,1)
>> +  table_id=1, n_packets=3, n_bytes=294, priority=200,ip,actions=group:1
>>  ])
>>
>>  ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p1),ipv4' | grep 
>> "ct(commit)" | parse_ufid)
>>  AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \
>>            sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl
>> -group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2)
>> +  
>> group_id=1,type=select,selection_method=dp_hash,bucket=bucket_id:0,weight:100,actions=ct(commit,table=2)
> 
> This is the weird output I was refering to in the above comment.
> 
>>  ])
>>
>>  ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p1),ipv4' | grep 
>> "actions:ovs-p2" | parse_ufid)
>>  AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \
>> -          sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl
>> -table_id=2, n_packets=3, n_bytes=294, ip,actions=output:2
>> +          sed -E 's/n_bytes=2(66|80),/n_bytes=294,/' | \
>> +          sed 's/table_id=254, n_packets=[[0-9]]*, 
>> n_bytes=[[0-9]]*,/table_id=254,/'], [0], [dnl
>> +br0
>> +  table_id=2, n_packets=3, n_bytes=294, ip,actions=output:2,output:3
>> +br1
>> +  n_packets=3, n_bytes=294, ,actions=clone(resubmit(,1)),resubmit(,2)
>> +  table_id=1, n_packets=3, n_bytes=294, 
>> ip,actions=mod_nw_dst:10.0.0.3,IN_PORT
>> +br0
>> +  table_id=0, no match
>> +  table_id=254, priority=0,reg0=0x2,actions=drop
>> +br1
>> +  table_id=2, n_packets=3, n_bytes=294, ip,actions=IN_PORT
>> +br0
>> +  cookie=0x1234abcd, n_packets=3, n_bytes=294, 
>> priority=100,ip,in_port=3,nw_dst=10.0.0.2,actions=resubmit(,4)
>> +  table_id=4, ip,actions=drop
>>  ])
>>
>>  ufid=$(ovs-appctl dpctl/dump-flows -m 
>> filter='recirc_id(0),in_port(ovs-p2),ipv4' | grep "ct," | parse_ufid)
>>  AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \
>>            sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl
>> -cookie=0xabcedf, n_packets=3, n_bytes=294, 
>> priority=100,ip,in_port=2,nw_dst=10.0.0.1,actions=ct(table=3)
>> +br0
>> +  cookie=0xabcedf, n_packets=3, n_bytes=294, 
>> priority=100,ip,in_port=2,nw_dst=10.0.0.1,actions=ct(table=3)
>>  ])
>>
>>  ufid=$(ovs-appctl dpctl/dump-flows -m filter='in_port(ovs-p2),ipv4' | grep 
>> "actions:ovs-p1" | parse_ufid)
>>  AT_CHECK([ovs-appctl ofproto/detrace $ufid $pmd_id | ofctl_strip | \
>>            sed -E 's/n_bytes=2(66|80),/n_bytes=294,/'], [0], [dnl
>> -table_id=3, n_packets=3, n_bytes=294, ip,actions=output:1
>> +br0
>> +  table_id=3, n_packets=3, n_bytes=294, ip,actions=output:1
>>  ])
>>
>>  AT_CHECK([ovs-appctl revalidator/resume])
>> diff --git a/utilities/checkpatch_dict.txt b/utilities/checkpatch_dict.txt
>> index d40194a95..c1f43e5af 100644
>> --- a/utilities/checkpatch_dict.txt
>> +++ b/utilities/checkpatch_dict.txt
>> @@ -49,6 +49,7 @@ defragment
>>  deref
>>  dereference
>>  dest
>> +detrace
>>  dhcp
>>  dhcpv4
>>  dhcpv6
>> --
>> 2.53.0
>>
> 
> Thanks.
> Adrián
> 

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to