On Thu, Apr 13, 2023 at 10:45 AM Lorenzo Bianconi
<lorenzo.bianc...@redhat.com> wrote:
>
> Rework OVN QoS implementation in order to configure it through OVS QoS
> table instead of running tc command directly bypassing OVS.
>
> https://bugzilla.redhat.com/show_bug.cgi?id=2129742
> Tested-by: Rodolfo Alonso <ralon...@redhat.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com>
> ---
>  controller/binding.c        | 421 +++++++++++++++++++-----------------
>  controller/ovn-controller.c |   6 +
>  tests/system-ovn.at         |  67 +++++-
>  3 files changed, 285 insertions(+), 209 deletions(-)
>
> diff --git a/controller/binding.c b/controller/binding.c
> index cde59ba99..1ac44b915 100644
> --- a/controller/binding.c
> +++ b/controller/binding.c
> @@ -111,6 +111,7 @@ binding_wait(void)
>
>  struct qos_queue {
>      struct hmap_node node;
> +    char *network;
>      uint32_t queue_id;
>      uint32_t min_rate;
>      uint32_t max_rate;
> @@ -148,213 +149,248 @@ static void update_lport_tracking(const struct 
> sbrec_port_binding *pb,
>                                    bool claimed);
>
>  static void
> -get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
> +get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map,
> +               struct smap *egress_ifaces)
>  {
>      uint32_t min_rate = smap_get_int(&pb->options, "qos_min_rate", 0);
>      uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
>      uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
>      uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0);
> +    const char *network = smap_get(egress_ifaces, pb->logical_port);
>
> -    if ((!min_rate && !max_rate && !burst) || !queue_id) {
> +    if ((!min_rate && !max_rate && !burst) || !queue_id || !network) {
>          /* Qos is not configured for this port. */
>          return;
>      }
>
>      struct qos_queue *node = xzalloc(sizeof *node);
>      hmap_insert(queue_map, &node->node, hash_int(queue_id, 0));
> +    node->network = xstrdup(network);
>      node->min_rate = min_rate;
>      node->max_rate = max_rate;
>      node->burst = burst;
>      node->queue_id = queue_id;
>  }
>
> -static const struct ovsrec_qos *
> -get_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_qos_table *qos_table)
> +static const char *
> +get_qos_egress_interface_name(struct shash *bridge_mappings,
> +                              const char *network)
>  {
> -    const struct ovsrec_qos *qos;
> -    OVSREC_QOS_TABLE_FOR_EACH (qos, qos_table) {
> -        if (!strcmp(qos->type, "linux-noop")) {
> -            return qos;
> -        }
> -    }
> -
> -    if (!ovs_idl_txn) {
> +    struct ovsrec_bridge *br_ln = shash_find_data(bridge_mappings, network);
> +    if (!br_ln) {
>          return NULL;
>      }
> -    qos = ovsrec_qos_insert(ovs_idl_txn);
> -    ovsrec_qos_set_type(qos, "linux-noop");
> -    return qos;
> -}
>
> -static bool
> -set_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_port_table *port_table,
> -             const struct ovsrec_qos_table *qos_table,
> -             struct smap *egress_ifaces)
> -{
> -    if (!ovs_idl_txn) {
> -        return false;
> -    }
> +    /* Add egress-ifaces from the connected bridge */
> +    for (size_t i = 0; i < br_ln->n_ports; i++) {
> +        const struct ovsrec_port *port_rec = br_ln->ports[i];
>
> -    const struct ovsrec_qos *noop_qos = get_noop_qos(ovs_idl_txn, qos_table);
> -    if (!noop_qos) {
> -        return false;
> -    }
> +        for (size_t j = 0; j < port_rec->n_interfaces; j++) {
> +            const struct ovsrec_interface *iface_rec;
>
> -    const struct ovsrec_port *port;
> -    size_t count = 0;
> +            iface_rec = port_rec->interfaces[j];
> +            if (smap_get(&iface_rec->external_ids, "iface-id")) {
> +                continue;
> +            }
>
> -    OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
> -        struct smap_node *node;
> -        SMAP_FOR_EACH (node, egress_ifaces) {
> -            if (node->value && !strcmp(node->value, port->name)) {
> -                ovsrec_port_set_qos(port, noop_qos);
> -                count++;
> +            bool is_egress_iface = smap_get_bool(&iface_rec->external_ids,
> +                                                 "ovn-egress-iface", false);
> +            if (is_egress_iface) {
> +                return iface_rec->name;
>              }
>          }
> -        if (smap_count(egress_ifaces) == count) {
> -            break;
> -        }
>      }
> -    return true;
> +
> +    return NULL;
>  }
>
> +/* 34359738360 == (2^32 - 1) * 8.  netdev_set_qos() doesn't support
> + * 64-bit rate netlink attributes, so the maximum value is 2^32 - 1
> + * bytes. The 'max-rate' config option is in bits, so multiplying by 8.
> + * Without setting max-rate the reported link speed will be used, which
> + * can be unrecognized for certain NICs or reported too low for virtual
> + * interfaces. */
> +#define OVN_QOS_MAX_RATE    34359738360
>  static void
> -set_qos_type(struct netdev *netdev, const char *type)
> +add_ovs_qos_table_entry(struct ovsdb_idl_txn *ovs_idl_txn,
> +                        const struct ovsrec_port_table *port_table,
> +                        struct shash *bridge_mappings,
> +                        struct qos_queue *q)
>  {
> -    /* 34359738360 == (2^32 - 1) * 8.  netdev_set_qos() doesn't support
> -     * 64-bit rate netlink attributes, so the maximum value is 2^32 - 1 
> bytes.
> -     * The 'max-rate' config option is in bits, so multiplying by 8.
> -     * Without setting max-rate the reported link speed will be used, which
> -     * can be unrecognized for certain NICs or reported too low for virtual
> -     * interfaces. */
> -    const struct smap conf = SMAP_CONST1(&conf, "max-rate", "34359738360");
> -    int error = netdev_set_qos(netdev, type, &conf);
> -    if (error) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "%s: could not set qdisc type \"%s\" (%s)",
> -                     netdev_get_name(netdev), type, ovs_strerror(error));
> +    const char *port_name =
> +        get_qos_egress_interface_name(bridge_mappings, q->network);
> +    if (!port_name) {
> +        return;
>      }
> -}
>
> -static void
> -setup_qos(const char *egress_iface, struct hmap *queue_map)
> -{
> -    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -    struct netdev *netdev_phy;
> +    const struct ovsrec_port *port = NULL, *iter;
> +    OVSREC_PORT_TABLE_FOR_EACH (iter, port_table) {
> +        if (!strcmp(iter->name, port_name)) {
> +            port = iter;
> +            break;
> +        }
> +    }

I think you can make get_qos_egress_interface_name return a port
record instead of its name and then you won't need to iterate over the
table again.

>
> -    if (!egress_iface) {
> -        /* Queues cannot be configured. */
> +    if (!port) {
>          return;
>      }
>
> -    int error = netdev_open(egress_iface, NULL, &netdev_phy);
> -    if (error) {
> -        VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)",
> -                     egress_iface, ovs_strerror(error));
> -        return;
> +    struct smap other_config = SMAP_INITIALIZER(&other_config);
> +    const struct ovsrec_qos *qos = port->qos;
> +    if (!qos) {
> +        qos = ovsrec_qos_insert(ovs_idl_txn);
> +        ovsrec_qos_set_type(qos, OVN_QOS_TYPE);
> +        ovsrec_port_set_qos(port, qos);
> +        smap_add_format(&other_config, "max-rate", "%ld", OVN_QOS_MAX_RATE);
> +        ovsrec_qos_set_other_config(qos, &other_config);
> +        smap_clear(&other_config);
>      }
>
> -    /* Check current qdisc. */
> -    const char *qdisc_type;
> -    struct smap qdisc_details;
> +    struct ovsrec_queue *queue;
> +    size_t i;
> +    for (i = 0; i < qos->n_queues; i++) {
> +        queue = qos->value_queues[i];
> +        if (qos->key_queues[i] != q->queue_id) {
> +            continue;
> +        }
> +
> +        uint32_t max_rate = smap_get_int(&queue->other_config, "max-rate", 
> 0);
> +        uint32_t min_rate = smap_get_int(&queue->other_config, "min-rate", 
> 0);
> +        uint32_t burst = smap_get_int(&queue->other_config, "burst", 0);
>
> -    smap_init(&qdisc_details);
> -    if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 ||
> -        qdisc_type[0] == '\0') {
> -        smap_destroy(&qdisc_details);
> -        netdev_close(netdev_phy);
> -        /* Qos is not supported. */
> +        if (max_rate != q->max_rate || min_rate != q->min_rate ||
> +            burst != q->burst) {
> +            break;
> +        }
> +        /* queue already configured. */
>          return;
>      }
> -    smap_destroy(&qdisc_details);
>
> -    /* If we're not actually being requested to do any QoS:
> -     *
> -     *     - If the current qdisc type is OVN_QOS_TYPE, then we clear the 
> qdisc
> -     *       type to "".  Otherwise, it's possible that our own leftover 
> qdisc
> -     *       settings could cause strange behavior on egress.  Also, QoS is
> -     *       expensive and may waste CPU time even if it's not really in use.
> -     *
> -     *       OVN isn't the only software that can configure qdiscs, and
> -     *       physical interfaces are shared resources, so there is some risk 
> in
> -     *       this strategy: we could disrupt some other program's QoS.
> -     *       Probably, to entirely avoid this possibility we would need to 
> add
> -     *       a configuration setting.
> -     *
> -     *     - Otherwise leave the qdisc alone. */
> -    if (hmap_is_empty(queue_map)) {
> -        if (!strcmp(qdisc_type, OVN_QOS_TYPE)) {
> -            set_qos_type(netdev_phy, "");
> -        }
> -        netdev_close(netdev_phy);
> -        return;
> +    if (i == qos->n_queues) {
> +        queue = ovsrec_queue_insert(ovs_idl_txn);
> +        ovsrec_qos_update_queues_setkey(qos, q->queue_id, queue);
>      }
>
> -    /* Configure qdisc. */
> -    if (strcmp(qdisc_type, OVN_QOS_TYPE)) {
> -        set_qos_type(netdev_phy, OVN_QOS_TYPE);
> -    }
> -
> -    /* Check and delete if needed. */
> -    struct netdev_queue_dump dump;
> -    unsigned int queue_id;
> -    struct smap queue_details;
> -    struct qos_queue *sb_info;
> -    struct hmap consistent_queues;
> -
> -    smap_init(&queue_details);
> -    hmap_init(&consistent_queues);
> -    NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) {
> -        bool is_queue_needed = false;
> -
> -        HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0),
> -                                 queue_map) {
> -            is_queue_needed = true;
> -            if (sb_info->min_rate ==
> -                smap_get_int(&queue_details, "min-rate", 0)
> -                && sb_info->max_rate ==
> -                smap_get_int(&queue_details, "max-rate", 0)
> -                && sb_info->burst ==
> -                smap_get_int(&queue_details, "burst", 0)) {
> -                /* This queue is consistent. */
> -                hmap_insert(&consistent_queues, &sb_info->node,
> -                            hash_int(queue_id, 0));
> +    smap_add_format(&other_config, "max-rate", "%d", q->max_rate);
> +    smap_add_format(&other_config, "min-rate", "%d", q->min_rate);
> +    smap_add_format(&other_config, "burst", "%d", q->burst);
> +    ovsrec_queue_verify_other_config(queue);
> +    ovsrec_queue_set_other_config(queue, &other_config);
> +    smap_destroy(&other_config);
> +}
> +
> +static void
> +remove_stale_ovs_qos_entry(const struct ovsrec_port_table *port_table,

remove_stale_ovs_qos_entrIES?


> +                           const struct ovsrec_qos_table *qos_table,
> +                           const struct sbrec_port_binding_table *pb_table,
> +                           struct shash *network_mappings,
> +                           struct smap *egress_ifaces)
> +{
> +    const struct ovsrec_qos *qos, *qos_next;
> +    OVSREC_QOS_TABLE_FOR_EACH_SAFE (qos, qos_next, qos_table) {
> +        const struct ovsrec_port *port = NULL, *iter;
> +        OVSREC_PORT_TABLE_FOR_EACH (iter, port_table) {
> +            if (iter->qos == qos) {
> +                port = iter;
>                  break;
>              }
>          }
> +        if (!port) {
> +            continue;
> +        }
> +
> +        int n_queue_deleted = 0, n_queues = qos->n_queues;
> +        for (size_t i = 0; i < n_queues; i++) {
> +            struct ovsrec_queue *queue = qos->value_queues[i];
> +            if (!queue) {
> +                continue;
> +            }
> +
> +            bool stale = true;
> +            const struct sbrec_port_binding *pb;
> +            SBREC_PORT_BINDING_TABLE_FOR_EACH (pb, pb_table) {
> +                const char *network = smap_get(
> +                        egress_ifaces, pb->logical_port);
> +                if (!network) {
> +                    continue;
> +                }
> +
> +                uint32_t queue_id = smap_get_int(&pb->options,
> +                                                 "qdisc_queue_id", 0);

nit: extract queue_id after potentially "continuing" on wrong name not matching.



> +                const char *port_name = shash_find_data(
> +                        network_mappings, network);
> +                if (!port_name) {
> +                    continue;
> +                }
> +
> +                if (!strcmp(port->name, port_name) &&
> +                    queue_id == qos->key_queues[i]) {
> +                    stale = false;
> +                    break;
> +                }
> +            }
> +
> +            if (stale) {
> +                ovsrec_qos_update_queues_delkey(qos, qos->key_queues[i]);
> +                ovsrec_queue_delete(queue);
> +                n_queue_deleted++;
> +            }
> +        }
>
> -        if (!is_queue_needed) {
> -            error = netdev_delete_queue(netdev_phy, queue_id);
> -            if (error) {
> -                VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)",
> -                             egress_iface, queue_id, ovs_strerror(error));
> +        if (n_queue_deleted && n_queue_deleted == n_queues) {
> +            if (port) {
> +                ovsrec_port_set_qos(port, NULL);
>              }
> +            ovsrec_qos_delete(qos);
>          }
>      }
> +}
>
> -    /* Create/Update queues. */
> -    HMAP_FOR_EACH (sb_info, node, queue_map) {
> -        if (hmap_contains(&consistent_queues, &sb_info->node)) {
> -            hmap_remove(&consistent_queues, &sb_info->node);
> +static void
> +configure_ovs_qos(struct hmap *queue_map,
> +                  struct ovsdb_idl_txn *ovs_idl_txn,
> +                  const struct ovsrec_port_table *port_table,
> +                  const struct ovsrec_qos_table *qos_table,
> +                  const struct sbrec_port_binding_table *pb_table,
> +                  struct shash *bridge_mappings,
> +                  struct smap *egress_ifaces)
> +
> +{
> +    if (!ovs_idl_txn) {
> +        return;
> +    }
> +
> +    if (smap_is_empty(egress_ifaces)) {
> +        return;
> +    }
> +
> +    struct shash pb_network_mappings = 
> SHASH_INITIALIZER(&pb_network_mappings);
> +    const struct sbrec_port_binding *pb;
> +    SBREC_PORT_BINDING_TABLE_FOR_EACH (pb, pb_table) {
> +        const char *network = smap_get(egress_ifaces, pb->logical_port);
> +        if (!network) {
>              continue;
>          }
>
> -        smap_clear(&queue_details);
> -        smap_add_format(&queue_details, "min-rate", "%d", sb_info->min_rate);
> -        smap_add_format(&queue_details, "max-rate", "%d", sb_info->max_rate);
> -        smap_add_format(&queue_details, "burst", "%d", sb_info->burst);
> -        error = netdev_set_queue(netdev_phy, sb_info->queue_id,
> -                                 &queue_details);
> -        if (error) {
> -            VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)",
> -                         egress_iface, sb_info->queue_id, 
> ovs_strerror(error));
> +        const char *port_name =
> +            get_qos_egress_interface_name(bridge_mappings, network);
> +        if (port_name) {
> +            shash_add(&pb_network_mappings, network, port_name);
>          }
>      }
> -    smap_destroy(&queue_details);
> -    hmap_destroy(&consistent_queues);
> -    netdev_close(netdev_phy);
> +
> +    /* Remove stale QoS entries. */
> +    remove_stale_ovs_qos_entry(port_table, qos_table, pb_table,
> +                               &pb_network_mappings, egress_ifaces);
> +
> +    struct qos_queue *q;
> +    HMAP_FOR_EACH (q, node, queue_map) {
> +        /* Add new QoS entries. */
> +        add_ovs_qos_table_entry(ovs_idl_txn, port_table,
> +                                bridge_mappings, q);
> +    }
> +
> +    shash_destroy(&pb_network_mappings);
>  }
>
>  static void
> @@ -362,6 +398,7 @@ destroy_qos_map(struct hmap *qos_map)
>  {
>      struct qos_queue *qos_queue;
>      HMAP_FOR_EACH_POP (qos_queue, node, qos_map) {
> +        free(qos_queue->network);
>          free(qos_queue);
>      }
>
> @@ -433,7 +470,7 @@ add_localnet_egress_interface_mappings(
>                  continue;
>              }
>              smap_replace(egress_ifaces, port_binding->logical_port,
> -                         iface_rec->name);
> +                         network);
>          }
>      }
>  }
> @@ -1460,7 +1497,7 @@ consider_vif_lport_(const struct sbrec_port_binding *pb,
>                                             b_ctx_out->tracked_dp_bindings);
>              }
>              if (b_lport->lbinding->iface && qos_map && 
> b_ctx_in->ovs_idl_txn) {
> -                get_qos_params(pb, qos_map);
> +                get_qos_params(pb, qos_map, b_ctx_out->egress_ifaces);
>              }
>          } else {
>              /* We could, but can't claim the lport. */
> @@ -1789,7 +1826,7 @@ consider_localnet_lport(const struct sbrec_port_binding 
> *pb,
>      update_local_lports(pb->logical_port, b_ctx_out);
>
>      if (qos_map && b_ctx_in->ovs_idl_txn) {
> -        get_qos_params(pb, qos_map);
> +        get_qos_params(pb, qos_map, b_ctx_out->egress_ifaces);
>      }
>
>      update_related_lport(pb, b_ctx_out);
> @@ -1912,9 +1949,6 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct 
> binding_ctx_out *b_ctx_out)
>          build_local_bindings(b_ctx_in, b_ctx_out);
>      }
>
> -    struct hmap *qos_map_ptr =
> -        smap_is_empty(b_ctx_out->egress_ifaces) ? NULL : &qos_map;
> -
>      struct ovs_list localnet_lports = OVS_LIST_INITIALIZER(&localnet_lports);
>      struct ovs_list external_lports = OVS_LIST_INITIALIZER(&external_lports);
>      struct ovs_list multichassis_ports = OVS_LIST_INITIALIZER(
> @@ -1951,7 +1985,7 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct 
> binding_ctx_out *b_ctx_out)
>              break;
>
>          case LP_VIF:
> -            consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL, qos_map_ptr);
> +            consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL, &qos_map);
>              if (pb->additional_chassis) {
>                  struct lport *multichassis_lport = xmalloc(
>                      sizeof *multichassis_lport);
> @@ -1962,11 +1996,11 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct 
> binding_ctx_out *b_ctx_out)
>              break;
>
>          case LP_CONTAINER:
> -            consider_container_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
> +            consider_container_lport(pb, b_ctx_in, b_ctx_out, &qos_map);
>              break;
>
>          case LP_VIRTUAL:
> -            consider_virtual_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
> +            consider_virtual_lport(pb, b_ctx_in, b_ctx_out, &qos_map);
>              break;
>
>          case LP_L2GATEWAY:
> @@ -2044,18 +2078,14 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct 
> binding_ctx_out *b_ctx_out)
>          free(multichassis_lport);
>      }
>
> -    shash_destroy(&bridge_mappings);
> -
> -    if (!smap_is_empty(b_ctx_out->egress_ifaces)
> -        && set_noop_qos(b_ctx_in->ovs_idl_txn, b_ctx_in->port_table,
> -                        b_ctx_in->qos_table, b_ctx_out->egress_ifaces)) {
> -        const struct smap_node *entry;
> -        SMAP_FOR_EACH (entry, b_ctx_out->egress_ifaces) {
> -            setup_qos(entry->value, &qos_map);
> -        }
> -    }
> +    configure_ovs_qos(&qos_map, b_ctx_in->ovs_idl_txn,
> +                      b_ctx_in->port_table, b_ctx_in->qos_table,
> +                      b_ctx_in->port_binding_table,
> +                      &bridge_mappings,
> +                      b_ctx_out->egress_ifaces);
>
>      destroy_qos_map(&qos_map);
> +    shash_destroy(&bridge_mappings);
>
>      cleanup_claimed_port_timestamps();
>  }
> @@ -2441,18 +2471,6 @@ binding_handle_ovs_interface_changes(struct 
> binding_ctx_in *b_ctx_in,
>              break;
>          }
>
> -        if (smap_get(&iface_rec->external_ids, "ovn-egress-iface")) {
> -            handled = false;
> -            break;
> -        }
> -
> -        struct smap_node *node;
> -        SMAP_FOR_EACH (node, b_ctx_out->egress_ifaces) {
> -            if (node->value && !strcmp(node->value, iface_rec->name)) {
> -                return false;
> -            }
> -        }
> -
>          const char *iface_id = smap_get(&iface_rec->external_ids, 
> "iface-id");
>          const char *old_iface_id = smap_get(b_ctx_out->local_iface_ids,
>                                              iface_rec->name);
> @@ -2495,8 +2513,6 @@ binding_handle_ovs_interface_changes(struct 
> binding_ctx_in *b_ctx_in,
>      }
>
>      struct hmap qos_map = HMAP_INITIALIZER(&qos_map);
> -    struct hmap *qos_map_ptr =
> -        smap_is_empty(b_ctx_out->egress_ifaces) ? NULL : &qos_map;
>
>      /*
>       * We consider an OVS interface for claiming if the following
> @@ -2526,21 +2542,23 @@ binding_handle_ovs_interface_changes(struct 
> binding_ctx_in *b_ctx_in,
>          if (iface_id && ofport > 0 &&
>                  is_iface_in_int_bridge(iface_rec, b_ctx_in->br_int)) {
>              handled = consider_iface_claim(iface_rec, iface_id, b_ctx_in,
> -                                           b_ctx_out, qos_map_ptr);
> +                                           b_ctx_out, &qos_map);
>              if (!handled) {
>                  break;
>              }
>          }
>      }
>
> -    if (handled && qos_map_ptr && set_noop_qos(b_ctx_in->ovs_idl_txn,
> -                                               b_ctx_in->port_table,
> -                                               b_ctx_in->qos_table,
> -                                               b_ctx_out->egress_ifaces)) {
> -        const struct smap_node *entry;
> -        SMAP_FOR_EACH (entry, b_ctx_out->egress_ifaces) {
> -            setup_qos(entry->value, &qos_map);
> -        }
> +    if (handled) {
> +        struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
> +        add_ovs_bridge_mappings(b_ctx_in->ovs_table, b_ctx_in->bridge_table,
> +                                &bridge_mappings);
> +        configure_ovs_qos(&qos_map, b_ctx_in->ovs_idl_txn,
> +                          b_ctx_in->port_table, b_ctx_in->qos_table,
> +                          b_ctx_in->port_binding_table,
> +                          &bridge_mappings,
> +                          b_ctx_out->egress_ifaces);
> +        shash_destroy(&bridge_mappings);
>      }
>
>      destroy_qos_map(&qos_map);
> @@ -2979,8 +2997,6 @@ delete_done:
>      }
>
>      struct hmap qos_map = HMAP_INITIALIZER(&qos_map);
> -    struct hmap *qos_map_ptr =
> -        smap_is_empty(b_ctx_out->egress_ifaces) ? NULL : &qos_map;
>
>      SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb,
>                                                 b_ctx_in->port_binding_table) 
> {
> @@ -2993,7 +3009,7 @@ delete_done:
>              update_ld_peers(pb, b_ctx_out->local_datapaths);
>          }
>
> -        handled = handle_updated_port(b_ctx_in, b_ctx_out, pb, qos_map_ptr);
> +        handled = handle_updated_port(b_ctx_in, b_ctx_out, pb, &qos_map);
>          if (!handled) {
>              break;
>          }
> @@ -3010,7 +3026,7 @@ delete_done:
>              sset_find_and_delete(b_ctx_out->postponed_ports, port_name);
>              continue;
>          }
> -        handled = handle_updated_port(b_ctx_in, b_ctx_out, pb, qos_map_ptr);
> +        handled = handle_updated_port(b_ctx_in, b_ctx_out, pb, &qos_map);
>          if (!handled) {
>              break;
>          }
> @@ -3053,17 +3069,14 @@ delete_done:
>              }
>              sbrec_port_binding_index_destroy_row(target);
>          }
> -        shash_destroy(&bridge_mappings);
> -    }
>
> -    if (handled && qos_map_ptr && set_noop_qos(b_ctx_in->ovs_idl_txn,
> -                                               b_ctx_in->port_table,
> -                                               b_ctx_in->qos_table,
> -                                               b_ctx_out->egress_ifaces)) {
> -        const struct smap_node *entry;
> -        SMAP_FOR_EACH (entry, b_ctx_out->egress_ifaces) {
> -            setup_qos(entry->value, &qos_map);
> -        }
> +        configure_ovs_qos(&qos_map, b_ctx_in->ovs_idl_txn,
> +                          b_ctx_in->port_table, b_ctx_in->qos_table,
> +                          b_ctx_in->port_binding_table,
> +                          &bridge_mappings,
> +                          b_ctx_out->egress_ifaces);
> +
> +        shash_destroy(&bridge_mappings);
>      }
>
>      destroy_qos_map(&qos_map);
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index d172a1f4f..4bc1baf93 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -1060,6 +1060,11 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>      ovsdb_idl_add_table(ovs_idl, &ovsrec_table_datapath);
>      ovsdb_idl_add_column(ovs_idl, &ovsrec_datapath_col_capabilities);
>      ovsdb_idl_add_table(ovs_idl, &ovsrec_table_flow_sample_collector_set);
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_other_config);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_queues);
> +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_queue);
> +    ovsdb_idl_add_column(ovs_idl, &ovsrec_queue_col_other_config);
>
>      chassis_register_ovs_idl(ovs_idl);
>      encaps_register_ovs_idl(ovs_idl);
> @@ -1074,6 +1079,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
> +    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
>      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 96d1c295e..3e84b6dcb 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -6533,8 +6533,10 @@ ovn_start
>  OVS_TRAFFIC_VSWITCHD_START()
>
>  ADD_BR([br-int])
> +ADD_BR([br-public])
>  ADD_BR([br-ext])
>
> +ovs-ofctl add-flow br-public action=normal
>  ovs-ofctl add-flow br-ext action=normal
>  # Set external-ids in br-int needed for ovn-controller
>  ovs-vsctl \
> @@ -6554,32 +6556,87 @@ ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", 
> "f0:00:00:01:02:03")
>  ovn-nbctl lsp-add sw0 sw01 \
>      -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2"
>
> +ovn-nbctl ls-add sw1
> +
> +ADD_NAMESPACES(sw11)
> +ADD_VETH(sw11, sw11, br-int, "192.168.4.2/24", "f0:00:00:01:04:03")
> +ovn-nbctl lsp-add sw1 sw11 \
> +    -- lsp-set-addresses sw11 "f0:00:00:01:04:03 192.168.4.2"
> +
>  ADD_NAMESPACES(public)
> -ADD_VETH(public, public, br-ext, "192.168.2.2/24", "f0:00:00:01:02:05")
> +ADD_VETH(public, public, br-public, "192.168.2.2/24", "f0:00:00:01:02:05")
> +AT_CHECK([ovs-vsctl remove interface ovs-public external-ids 
> iface-id=public])
>
> -AT_CHECK([ovs-vsctl set Open_vSwitch . 
> external-ids:ovn-bridge-mappings=phynet:br-ext])
> +ADD_NAMESPACES(ext)
> +ADD_VETH(ext, ext, br-ext, "192.168.3.2/24", "f0:00:00:01:02:06")
> +AT_CHECK([ovs-vsctl remove interface ovs-ext external-ids iface-id=ext])
> +
> +AT_CHECK([ovs-vsctl set Open_vSwitch . 
> external-ids:ovn-bridge-mappings=public:br-public,ext:br-ext])
>  ovn-nbctl lsp-add sw0 public \
>          -- lsp-set-addresses public unknown \
>          -- lsp-set-type public localnet \
> -        -- lsp-set-options public network_name=phynet
> +        -- lsp-set-options public network_name=public
> +
> +ovn-nbctl lsp-add sw1 ext \
> +        -- lsp-set-addresses ext unknown \
> +        -- lsp-set-type ext localnet \
> +        -- lsp-set-options ext network_name=ext
>
>  AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_min_rate=200000])
>  AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_max_rate=300000])
>  AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_burst=3000000])
>  AT_CHECK([ovs-vsctl set interface ovs-public 
> external-ids:ovn-egress-iface=true])
> +
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_min_rate=400000])
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_max_rate=600000])
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_burst=6000000])
> +AT_CHECK([ovs-vsctl set interface ovs-ext 
> external-ids:ovn-egress-iface=true])
> +
>  OVS_WAIT_UNTIL([tc qdisc show | grep -q 'htb 1: dev ovs-public'])
>  OVS_WAIT_UNTIL([tc class show dev ovs-public | \
>                  grep -q 'class htb .* rate 200Kbit ceil 300Kbit burst 
> 375000b cburst 375000b'])
>
> +OVS_WAIT_UNTIL([tc qdisc show | grep -q 'htb 1: dev ovs-ext'])
> +OVS_WAIT_UNTIL([tc class show dev ovs-ext | \
> +                grep -q 'class htb .* rate 400Kbit ceil 600Kbit burst 
> 750000b cburst 750000b'])
>
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_min_rate=200000])
>  AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_max_rate=300000])
> +
>  OVS_WAIT_UNTIL([tc class show dev ovs-public | \
> -                grep -q 'class htb .* rate 200Kbit ceil 34359Mbit burst 
> 375000b .*'])
> +                grep -q 'class htb .* rate 12Kbit ceil 34359Mbit burst 
> 375000b cburst 373662b'])
>
> -AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_min_rate=200000])
>  AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_burst=3000000])
>  OVS_WAIT_UNTIL([test "$(tc qdisc show | grep 'htb 1: dev ovs-public')" = ""])
>
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_max_rate=800000])
> +OVS_WAIT_UNTIL([tc class show dev ovs-ext | \
> +                grep -q 'class htb .* rate 400Kbit ceil 800Kbit burst 
> 750000b cburst 750000b'])
> +
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_min_rate=400000])
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_max_rate=800000])
> +AT_CHECK([ovn-nbctl set Logical_Switch_Port public 
> options:qos_burst=6000000])
> +
> +OVS_WAIT_UNTIL([tc qdisc show | grep -q 'htb 1: dev ovs-public'])
> +OVS_WAIT_UNTIL([tc class show dev ovs-public | \
> +                grep -q 'class htb .* rate 400Kbit ceil 800Kbit burst 
> 750000b cburst 750000b'])
> +
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port ext options 
> qos_min_rate=400000])
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port ext options 
> qos_max_rate=800000])
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port ext options 
> qos_burst=6000000])
> +
> +OVS_WAIT_UNTIL([test "$(tc qdisc show | grep 'htb 1: dev ovs-ext')" = ""])
> +
> +OVS_WAIT_UNTIL([tc qdisc show | grep -q 'htb 1: dev ovs-public'])
> +OVS_WAIT_UNTIL([tc class show dev ovs-public | \
> +                grep -q 'class htb .* rate 400Kbit ceil 800Kbit burst 
> 750000b cburst 750000b'])
> +
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_min_rate=400000])
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_max_rate=800000])
> +AT_CHECK([ovn-nbctl remove Logical_Switch_Port public options 
> qos_burst=6000000])
> +
> +OVS_WAIT_UNTIL([test "$(tc qdisc show | grep 'htb 1: dev ovs-public')" = ""])
> +
>  kill $(pidof ovn-controller)
>
>  as ovn-sb
> --
> 2.39.2
>

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

Reply via email to