On 14 Jul 2025, at 18:42, Ilya Maximets wrote:

> On 7/11/25 2:40 PM, Eelco Chaudron wrote:
>> When a meter action is encountered and stored in the auxiliary
>> structure, and subsequently, a non-meter action is processed
>> within a nested list during callback execution, an infinite
>> loop is triggered.
>>
>> This patch maintains the current behavior but stores all
>> required meter actions in an ofpbuf for deferred execution.
>>
>> Fixes: 076caa2fb077 ("ofproto: Meter translation.")
>> Signed-off-by: Eelco Chaudron <echau...@redhat.com>
>> ---
>>  lib/dpif.c            | 65 +++++++++++++++++--------------------------
>>  tests/ofproto-dpif.at | 41 +++++++++++++++++++++++++++
>>  2 files changed, 66 insertions(+), 40 deletions(-)
>
> Hi, Eelco.  Thanks for the patch!
>
> It appears to be solving the same problem as:
>  
> https://patchwork.ozlabs.org/project/openvswitch/patch/20250506022337.3242-1-danieldin...@gmail.com/
>
> Please, at least, add a link to it as Reported-at.  The solution seems
> fairly different and you also have a proper test implemented, and there
> were no updates for the previous patch in the past two months, so I won't
> insist on getting the previous version, even though it was submitted first.

Thank for the review Ilya. You are right, I should have added the reported-at. 
Will do this in V2.

> See some comments below.

All seem valid, but one, as noted below, will resolve the rest in v2.

>>
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index a064f717f..0fd3d880c 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1163,7 +1163,7 @@ struct dpif_execute_helper_aux {
>>      struct dpif *dpif;
>>      const struct flow *flow;
>>      int error;
>> -    const struct nlattr *meter_action; /* Non-NULL, if have a meter action. 
>> */
>> +    struct ofpbuf meter_actions;
>>  };
>>
>>  /* This is called for actions that need the context of the datapath to be
>> @@ -1180,10 +1180,14 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>
>>      switch ((enum ovs_action_attr)type) {
>>      case OVS_ACTION_ATTR_METER:
>> -        /* Maintain a pointer to the first meter action seen. */
>> -        if (!aux->meter_action) {
>> -            aux->meter_action = action;
>> -        }
>> +        /* XXX: This code collects meter actions since the last action
>> +         * execution via the datapath to be executed right before the
>> +         * current action that needs to be executed by the datapath.
>> +         * This is only an approximation, but better than nothing.
>> +         * Fundamentally, we should have a mechanism by which the
>> +         * datapath could return the result of the meter action so that
>> +         * we could execute them at the right order. */
>> +        ofpbuf_put(&aux->meter_actions, action, NLA_ALIGN(action->nla_len));
>>          break;
>>
>>      case OVS_ACTION_ATTR_CT:
>> @@ -1196,43 +1200,22 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>      case OVS_ACTION_ATTR_SAMPLE:
>>      case OVS_ACTION_ATTR_RECIRC: {
>>          struct dpif_execute execute;
>> -        struct ofpbuf execute_actions;
>> -        uint64_t stub[256 / 8];
>>          struct pkt_metadata *md = &packet->md;
>>
>> -        if (flow_tnl_dst_is_set(&md->tunnel) || aux->meter_action) {
>> -            ofpbuf_use_stub(&execute_actions, stub, sizeof stub);
>> -
>> -            if (aux->meter_action) {
>> -                const struct nlattr *a = aux->meter_action;
>> -
>> -                /* XXX: This code collects meter actions since the last 
>> action
>> -                 * execution via the datapath to be executed right before 
>> the
>> -                 * current action that needs to be executed by the datapath.
>> -                 * This is only an approximation, but better than nothing.
>> -                 * Fundamentally, we should have a mechanism by which the
>> -                 * datapath could return the result of the meter action so 
>> that
>> -                 * we could execute them at the right order. */
>> -                do {
>> -                    ofpbuf_put(&execute_actions, a, NLA_ALIGN(a->nla_len));
>> -                    /* Find next meter action before 'action', if any. */
>> -                    do {
>> -                        a = nl_attr_next(a);
>> -                    } while (a != action &&
>> -                             nl_attr_type(a) != OVS_ACTION_ATTR_METER);
>> -                } while (a != action);
>> -            }
>> +        if (flow_tnl_dst_is_set(&md->tunnel) || aux->meter_actions.size) {
>> +
>
> The empty line here is not needed.

ACK

>> +            struct ofpbuf *execute_actions = &aux->meter_actions;
>>
>>              /* The Linux kernel datapath throws away the tunnel information
>>               * that we supply as metadata.  We have to use a "set" action to
>>               * supply it. */
>>              if (flow_tnl_dst_is_set(&md->tunnel)) {
>> -                odp_put_tunnel_action(&md->tunnel, &execute_actions, NULL);
>> +                odp_put_tunnel_action(&md->tunnel, execute_actions, NULL);
>>              }
>> -            ofpbuf_put(&execute_actions, action, 
>> NLA_ALIGN(action->nla_len));
>> +            ofpbuf_put(execute_actions, action, NLA_ALIGN(action->nla_len));
>>
>> -            execute.actions = execute_actions.data;
>> -            execute.actions_len = execute_actions.size;
>> +            execute.actions = execute_actions->data;
>> +            execute.actions_len = execute_actions->size;
>>          } else {
>>              execute.actions = action;
>>              execute.actions_len = NLA_ALIGN(action->nla_len);
>> @@ -1264,12 +1247,8 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>
>>          dp_packet_delete(clone);
>>
>> -        if (flow_tnl_dst_is_set(&md->tunnel) || aux->meter_action) {
>> -            ofpbuf_uninit(&execute_actions);
>> -
>> -            /* Do not re-use the same meters for later output actions. */
>> -            aux->meter_action = NULL;
>> -        }
>> +        /* Do not re-use the same meters for later output actions. */
>> +        ofpbuf_reinit(&aux->meter_actions, 0);
>
> The comment here is a bit misleading, because the ofpbuf was already reused
> for other purposes and contains more than just meters.
>
> I'd suggest to move this re-init inside the if condition that steals the
> data, and actually use ofpbuf_steal_data() while pointing execute.actions
> to this buffer to avoid any confusion.

I tried your suggestion, and the code became harder to understand/read. 
Especially as we still need to free() the stolen buffer. Instead, I decided to 
update the comment in v2.

>>          break;
>>      }
>>
>> @@ -1306,14 +1285,20 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>  static int
>>  dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
>>  {
>> -    struct dpif_execute_helper_aux aux = {dpif, execute->flow, 0, NULL};
>> +    struct dpif_execute_helper_aux aux = {
>> +        .dpif = dpif,
>> +        .flow = execute->flow,
>> +        .error = 0
>> +    };
>>      struct dp_packet_batch pb;
>>
>>      COVERAGE_INC(dpif_execute_with_help);
>>
>> +    ofpbuf_init(&aux.meter_actions, 0);
>>      dp_packet_batch_init_packet(&pb, execute->packet);
>>      odp_execute_actions(&aux, &pb, false, execute->actions,
>>                          execute->actions_len, dpif_execute_helper_cb);
>> +    ofpbuf_uninit(&aux.meter_actions);
>>      return aux.error;
>>  }
>>
>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>> index 7930b3f29..702fb8d31 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -6621,6 +6621,47 @@ This flow is handled by the userspace slow path 
>> because it:
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>
>> +AT_SETUP([ofproto-dpif - meter with slow-path action])
>> +
>> +OVS_VSWITCHD_START([ dnl
>> +  add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 -- \
>> +  add-port br0 p1 -- set Interface p1 type=dummy ofport_request=2])
>
> add_of_ports --pcap br0 1 2

ACK

>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 add-meter br0 \
>> +  'meter=1 pktps burst stats bands=type=drop rate=100 burst_size=100'])
>> +AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap])
>> +AT_CHECK([ovs-vsctl -- set Interface p1 options:tx_pcap=p1.pcap])
>
> Not needed with the above add_of_ports.

ACK

>> +
>> +AT_CHECK([ovs-appctl vlog/set dpif:dbg])
>> +
>> +dnl Hex bytes for the following packet:
>> +dnl   00:00:00:00:00:0c -> ff:ff:ff:ff:ff:ff
>> +dnl   ARP 42 Who has 192.168.0.1? Tell 192.168.0.100
>> +eth="ffffffffffff0000000000ec0806"
>> +arp="0001080006040001000c29abcdefc0a80064000000000000c0a80001"
>> +packet="${eth}${arp}"
>
> This should be done with 'ovs-ofctl compose-packet --bare'.

ACK

>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 dnl
>> +  
>> "table=1,eth_type=0x0806,actions=meter:1,clone(set_field:2->arp_op,output(port=p1,max_len=128)),output:p0"])
>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 CONTROLLER \
>> +  "meter:1,resubmit(,1)" "$packet"])
>> +
>> +# Verify the orinigal packet is received on port 0.
>> +OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep -E "$packet" | wc -l` -ge 1])
>> +
>> +dnl Turn the packet defenition into a reply and verify it's received on 
>> port 1.
>> +packet=$(echo "$packet" | sed 's/\(.\{36\}\)01/\102/')
>
> And another compose-packet here.

ACK

>> +OVS_WAIT_UNTIL([test `ovs-pcap p1.pcap | grep -E "$packet" | wc -l` -ge 1])
>
> It's a dummy datapath, we know the exact number of packets, also grep -c can 
> count,
> and -E is probably not needed.

ACK

>> +
>> +dnl Make sure all meters, and outputs datapath actions get executed.
>> +OVS_WAIT_UNTIL([grep -E "sub-execute meter\(0\),meter\(0\),. on packet arp" 
>> dnl
>> +                  ovs-vswitchd.log])
>> +OVS_WAIT_UNTIL([grep -E "sub-execute . on packet arp" ovs-vswitchd.log])
>
> We know exact port numbers, so may avoid -E and escaping of the parenthesis.

Not sure why, but previously I had dp port numbers change on me. I did another 
100x run, and they are stable now, so I removed the -E.

>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>> +
>
> No need for the extra line.

ACK
>>
>>  dnl CHECK_CONTINUATION(TITLE, N_PORTS0, N_PORTS1, ACTIONS0, ACTIONS1, 
>> [EXTRA_SETUP])
>>  dnl

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to