On 30 Jan 2023, at 17:42, Simon Horman wrote:

> From: Tianyu Yuan <[email protected]>
>
> Allow revalidator to continuously delete police in kernel
> tc datapath until it is deleted.
>
> In current implementation, polices in tc datapath will not
> deleted when they are being used and these remaining polices
> will be cleared when the openvswitch restarts.
>
> This patch supports revalidator to delete remaining polices
> in tc datapath by storing meter_id deleted unsuccessfully in
> a hmap and continuously deleting them in revalidator until
> success.

Hi Tianyu,

Thanks for the new revision of the patch...

Were you able to figure out if we really need to do this during revalidating 
flows?

I was wondering if there would be a different way, i.e. on flow deletion see if 
it was the last flow using this meter and if so remove it then. I guess this 
can be done with the meter having a flag that it has been deleted.

This would be way more elegant than doing this in the revalidator thread.

See some inline comments below. This was a quick review/tests, as the above 
might require a different approach.

Cheers,

Eelco

> Signed-off-by: Tianyu Yuan <[email protected]>
> Signed-off-by: Simon Horman <[email protected]>
>
> ---
>  lib/dpif-netdev.c             |  1 +
>  lib/dpif-netlink.c            | 26 +++++++++++++++++++++++++-
>  lib/dpif-provider.h           |  4 ++++
>  lib/dpif.c                    |  7 +++++++
>  lib/dpif.h                    |  2 ++
>  lib/netdev-offload-tc.c       |  5 ++++-
>  lib/netdev-offload.c          |  8 +++++---
>  ofproto/ofproto-dpif-upcall.c |  6 ++++++
>  ofproto/ofproto-dpif.c        | 10 +++++++++-
>  ofproto/ofproto-dpif.h        |  3 +++
>  10 files changed, 66 insertions(+), 6 deletions(-)
>
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index c9f7179c3b4c..878fe5933d1a 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -9689,6 +9689,7 @@ const struct dpif_class dpif_netdev_class = {
>      dpif_netdev_meter_set,
>      dpif_netdev_meter_get,
>      dpif_netdev_meter_del,
> +    NULL,
>      dpif_netdev_bond_add,
>      dpif_netdev_bond_del,
>      dpif_netdev_bond_stats_get,
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index 026b0daa8d83..8b95912aea97 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -25,6 +25,7 @@
>  #include <net/if.h>
>  #include <linux/types.h>
>  #include <linux/pkt_sched.h>
> +#include <linux/rtnetlink.h>
>  #include <poll.h>
>  #include <stdlib.h>
>  #include <strings.h>
> @@ -37,6 +38,7 @@
>  #include "dpif-provider.h"
>  #include "fat-rwlock.h"
>  #include "flow.h"
> +#include "id-pool.h"
>  #include "netdev-linux.h"
>  #include "netdev-offload.h"
>  #include "netdev-provider.h"
> @@ -61,6 +63,7 @@
>  #include "packets.h"
>  #include "random.h"
>  #include "sset.h"
> +#include "tc.h"
>  #include "timeval.h"
>  #include "unaligned.h"
>  #include "util.h"
> @@ -4363,12 +4366,32 @@ dpif_netlink_meter_del(struct dpif *dpif, 
> ofproto_meter_id meter_id,
>      err  = dpif_netlink_meter_get_stats(dpif, meter_id, stats,
>                                          max_bands, OVS_METER_CMD_DEL);
>      if (!err && netdev_is_flow_api_enabled()) {
> -        meter_offload_del(meter_id, stats);
> +        return meter_offload_del(meter_id, stats);
>      }
>
>      return err;
>  }
>
> +static void
> +dpif_netlink_meter_revalidate(struct dpif *dpif_ OVS_UNUSED,
> +                              struct hmap *meter_map)
> +{
> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +    ofproto_meter_id meter_id;
> +    struct id_node *id_node;
> +
> +    HMAP_FOR_EACH_POP (id_node, node, meter_map) {
> +        meter_id.uint32 = id_node->id;
> +
> +        if (meter_offload_del(meter_id, NULL) == EPERM) {
> +            id_hmap_add(meter_map, meter_id.uint32);
> +        } else {
> +            VLOG_DBG_RL(&rl, "Delete meter %u in datapath success",
> +                        meter_id.uint32);
> +        }
> +    }
> +}
> +
>  static bool
>  probe_broken_meters__(struct dpif *dpif)
>  {
> @@ -4588,6 +4611,7 @@ const struct dpif_class dpif_netlink_class = {
>      dpif_netlink_meter_set,
>      dpif_netlink_meter_get,
>      dpif_netlink_meter_del,
> +    dpif_netlink_meter_revalidate,
>      NULL,                       /* bond_add */
>      NULL,                       /* bond_del */
>      NULL,                       /* bond_stats_get */
> diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
> index 12477a24feee..794fbc21686a 100644
> --- a/lib/dpif-provider.h
> +++ b/lib/dpif-provider.h
> @@ -631,6 +631,10 @@ struct dpif_class {
>      int (*meter_del)(struct dpif *, ofproto_meter_id meter_id,
>                       struct ofputil_meter_stats *, uint16_t n_bands);
>
> +    /* Checks unneeded meters from 'dpif' and removes them. They may
> +     * be caused by deleting in-use meters. */
> +    void (*meter_revalidate)(struct dpif *, struct hmap *meter_map);
> +

I guess this is not really a revalidate action, it's more of a cleanup.
So maybe also call it something like meter_cleanup(), or 
meter_garbage_collection.

Or maybe even better, why not use the existing periodic 'bool (*run)(struct 
dpif *dpif)' callback and handle it there?

>      /* Adds a bond with 'bond_id' and the member-map to 'dpif'. */
>      int (*bond_add)(struct dpif *dpif, uint32_t bond_id,
>                      odp_port_t *member_map);
> diff --git a/lib/dpif.c b/lib/dpif.c
> index fe4db83fbfee..c8718239117d 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -2028,6 +2028,13 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id 
> meter_id,
>      return error;
>  }
>
> +void
> +dpif_meter_revalidate(struct dpif *dpif, struct hmap *meter_map){
> +    if (dpif->dpif_class->meter_revalidate) {
> +        dpif->dpif_class->meter_revalidate(dpif, meter_map);
> +    }
> +}
> +
>  int
>  dpif_bond_add(struct dpif *dpif, uint32_t bond_id, odp_port_t *member_map)
>  {
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 6cb4dae6d8d7..e181e4ee1d6c 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -378,6 +378,7 @@
>
>  #include "dpdk.h"
>  #include "dp-packet.h"
> +#include "id-pool.h"
>  #include "netdev.h"
>  #include "openflow/openflow.h"
>  #include "openvswitch/ofp-meter.h"
> @@ -905,6 +906,7 @@ int dpif_meter_get(const struct dpif *, ofproto_meter_id 
> meter_id,
>                     struct ofputil_meter_stats *, uint16_t n_bands);
>  int dpif_meter_del(struct dpif *, ofproto_meter_id meter_id,
>                     struct ofputil_meter_stats *, uint16_t n_bands);
> +void dpif_meter_revalidate(struct dpif *dpif, struct hmap *meter_map);
>
>  /* Bonding. */
>
> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> index 15d1c36aa04e..f87a5e05e202 100644
> --- a/lib/netdev-offload-tc.c
> +++ b/lib/netdev-offload-tc.c
> @@ -2980,8 +2980,11 @@ meter_tc_del_policer(ofproto_meter_id meter_id,
>                          police_index, meter_id.uint32, ovs_strerror(err));
>          } else {
>              meter_free_police_index(police_index);
> +            /* Do not remove the mapping between meter_id and police_index
> +             * until this meter is deleted successfully in datapath. This
> +             * mapping will be used in meter deletion in revalidator. */
> +            meter_id_remove(meter_id.uint32);

This approach might have problem, what if the policy gets deleted, and re-added 
with a new configuration?
The datapath flows are not cleared, and the add will fail as the meter_id is 
still in use.

Guess we need to keep the police index as this is tc specific, the meter_id is 
global.

>          }
> -        meter_id_remove(meter_id.uint32);
>      }
>
>      return err;
> diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
> index 4592262bd34e..0769f796e5b7 100644
> --- a/lib/netdev-offload.c
> +++ b/lib/netdev-offload.c
> @@ -241,19 +241,21 @@ int
>  meter_offload_del(ofproto_meter_id meter_id, struct ofputil_meter_stats 
> *stats)
>  {
>      struct netdev_registered_flow_api *rfa;
> +    int ret = 0;
>
>      CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
>          if (rfa->flow_api->meter_del) {
> -            int ret = rfa->flow_api->meter_del(meter_id, stats);
> -            if (ret) {
> +            int err = rfa->flow_api->meter_del(meter_id, stats);
> +            if (err) {
>                  VLOG_DBG_RL(&rl, "Failed deleting meter %u for flow api %s, "
>                              "error %d", meter_id.uint32, rfa->flow_api->type,
>                              ret);
> +                ret = err;
>              }
>          }
>      }
>
> -    return 0;
> +    return ret;
>  }
>
>  int
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 31ac02d116fc..b802c9c7f13a 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -2828,6 +2828,12 @@ revalidate(struct revalidator *revalidator)
>          }
>          ovsrcu_quiesce();
>      }
> +    /* When fail_meter_hmap is not empty, revalidate to delete meters in this
> +     * hashmap. */
> +    if (hmap_count(&udpif->backer->fail_meter_hmap)) {
> +        dpif_meter_revalidate(udpif->dpif, &udpif->backer->fail_meter_hmap);

This could be removed with the general run() implementation. But in general we 
should not have this hmap, just call the callback. See below.

> +    }
> +
>      dpif_flow_dump_thread_destroy(dump_thread);
>      ofpbuf_uninit(&odp_actions);
>  }
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index f87e27a8cd7f..591af5503765 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -835,6 +835,7 @@ open_dpif_backer(const char *type, struct dpif_backer 
> **backerp)
>      dpif_meter_get_features(backer->dpif, &features);
>      if (features.max_meters) {
>          backer->meter_ids = id_pool_create(0, features.max_meters);
> +        hmap_init(&backer->fail_meter_hmap);
>      } else {
>          backer->meter_ids = NULL;
>      }
> @@ -6790,8 +6791,15 @@ static void
>  free_meter_id(struct free_meter_id_args *args)
>  {
>      struct ofproto_dpif *ofproto = args->ofproto;
> +    int err;
> +
> +    err = dpif_meter_del(ofproto->backer->dpif, args->meter_id, NULL, 0);
> +    /* Add fail deleted meter to fail_meter_hmap and waiting to be deleted in
> +     * revalidator. */
> +    if (err == EPERM) {
> +        id_hmap_add(&ofproto->backer->fail_meter_hmap, 
> args->meter_id.uint32);
> +    }

I think we should not track any of this in the ofproto layer. As this is a dpif 
problem, it should be completely hidden in the dpif layer. This should be easy 
if you remove the dependency on meter_id as suggested above.

>
> -    dpif_meter_del(ofproto->backer->dpif, args->meter_id, NULL, 0);
>      id_pool_free_id(ofproto->backer->meter_ids, args->meter_id.uint32);
>      free(args);
>  }
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index d8e0cd37ac5b..befc68d13baa 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -61,6 +61,7 @@ struct ofproto_dpif;
>  struct uuid;
>  struct xlate_cache;
>  struct xlate_ctx;
> +struct id_pool;
>
>  /* Number of implemented OpenFlow tables. */
>  enum { N_TABLES = 255 };
> @@ -260,6 +261,8 @@ struct dpif_backer {
>
>      /* Meter. */
>      struct id_pool *meter_ids;     /* Datapath meter allocation. */
> +    struct hmap fail_meter_hmap;   /* Hashmap for meter with failed deletion
> +                                    * in datapath */
>
>      /* Connection tracking. */
>      struct id_pool *tp_ids;             /* Datapath timeout policy id
> -- 
> 2.30.2

Please add a test to 'make check-offloads'.

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

Reply via email to