On 15 Jul 2025, at 14:57, Ilya Maximets wrote:

> On 7/15/25 11:13 AM, 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.
>>
>> Reportde-at: 
>> https://patchwork.ozlabs.org/project/openvswitch/patch/20250506022337.3242-1-danieldin...@gmail.com/
>> Fixes: 076caa2fb077 ("ofproto: Meter translation.")
>> Signed-off-by: Eelco Chaudron <echau...@redhat.com>
>>
>> ---
>> v2: - Removed redundant extra newlines.
>>     - Update comment to reflect reality
>>     - Use the add_of_ports MACRO
>>     - Use 'ovs-ofctl compose-packet --bare' for packet construction
>>     - Fix some grep commands.
>> ---
>>  lib/dpif.c            | 65 +++++++++++++++++--------------------------
>>  tests/ofproto-dpif.at | 36 ++++++++++++++++++++++++
>>  2 files changed, 61 insertions(+), 40 deletions(-)
>
> Thanks, Eelco.  This version seems fine overall, see a couple nits below.

Thanks, I’ll sent a quick v3 fixing all your nits below.

Cheers,

Eelco

>>
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index a064f717f..3d0d366f1 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,21 @@ 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) {
>> +            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 +1246,9 @@ 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;
>> -        }
>> +        /* Re-initialize the 'aux->meter_actions' ofpbuf as it could have 
>> been
>> +         * used for sending the additional meter and/or tunnel actions. */
>> +        ofpbuf_reinit(&aux->meter_actions, 0);
>
> Should we maybe use ofpbuf_clear() instead?  It doesn't seem to
> be necessary to reallocate the buffer.

Ack, this would be nicer.

>>          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
>
> Trailing comma is missing.
>
>> +    };
>>      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..770337361 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -6621,6 +6621,42 @@ 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
>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 add-meter br0 \
>> +  'meter=1 pktps burst stats bands=type=drop rate=100 burst_size=100'])
>> +
>> +add_of_ports --pcap br0 1 2
>> +
>> +AT_CHECK([ovs-appctl vlog/set dpif:dbg])
>> +
>> +eth="eth_src=00:00:00:00:00:ec,eth_dst=ff:ff:ff:ff:ff:ff,arp"
>> +arp="arp_tpa=192.168.0.1,arp_spa=192.168.0.100,arp_op=1"
>> +packet=$(ovs-ofctl compose-packet --bare "$eth,$arp")
>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 dnl
>
> 'dnl' here, but '\' in the command below.  Might be better to just use
> '\' in all cases.  Modern autotest works fine with line continuations.
> Even better than with dnl, as dnl sometimes ends up with the rest of
> the command not showing up in the log.
>
>> +  
>> "table=1,eth_type=0x0806,actions=meter:1,clone(set_field:2->arp_op,output(port=p2,max_len=128)),output:p1"])
>> +
>> +AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 CONTROLLER \
>> +  "meter:1,resubmit(,1)" "$packet"])
>> +
>> +dnl Verify the orinigal packet is received on port 1.
>> +OVS_WAIT_UNTIL([test `ovs-pcap p1-tx.pcap | grep -c "$packet"` -eq 1])
>
> May be better to use $() syntax instead of ``, since you're already
> using $() above.
>
>> +
>> +dnl Verify the modified packet is received on port 2.
>> +arp="arp_tpa=192.168.0.1,arp_spa=192.168.0.100,arp_op=2"
>> +packet=$(ovs-ofctl compose-packet --bare "$eth,$arp")
>> +OVS_WAIT_UNTIL([test `ovs-pcap p2-tx.pcap | grep -c "$packet"` -eq 1])
>
> $()
>
>> +
>> +dnl Make sure all meters, and outputs datapath actions get executed.
>> +OVS_WAIT_UNTIL([grep "sub-execute meter(0),meter(0),2 on packet arp" dnl
>> +                  ovs-vswitchd.log])
>
> dnl vs '\'
>
>> +OVS_WAIT_UNTIL([grep "sub-execute 1 on packet arp" ovs-vswitchd.log])
>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>>
>>  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