+
+/* Adds the 'n' key-value pairs in 'keys' in 'values' to 'shash'.
+ *
+ * The value strings in '*shash' are taken directly from values[], not copied,
+ * so the caller should not modify or free them. */
+static void
+shash_from_ovs_idl_map(char **keys, char **values, size_t n,
+ struct shash *shash)
+{
+ size_t i;
+
+ shash_init(shash);
+ for (i = 0; i < n; i++) {
+ shash_add(shash, keys[i], values[i]);
+ }
+}
+
+struct iface_delete_queues_cbdata {
+ struct netdev *netdev;
+ const struct ovsdb_datum *queues;
+};
+
+static bool
+queue_ids_include(const struct ovsdb_datum *queues, int64_t target)
+{
+ union ovsdb_atom atom;
+
+ atom.integer = target;
+ return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER) != UINT_MAX;
+}
+
+static void
+iface_delete_queues(unsigned int queue_id,
+ const struct shash *details OVS_UNUSED, void *cbdata_)
+{
+ struct iface_delete_queues_cbdata *cbdata = cbdata_;
+
+ if (!queue_ids_include(cbdata->queues, queue_id)) {
+ netdev_delete_queue(cbdata->netdev, queue_id);
+ }
+}
+
+static void
+iface_update_qos(struct iface *iface, const struct ovsrec_qos *qos)
+{
+ if (!qos || qos->type[0] == '\0') {
+ netdev_set_qos(iface->netdev, NULL, NULL);
+ } else {
+ struct iface_delete_queues_cbdata cbdata;
+ struct shash details;
+ size_t i;
+
+ /* Configure top-level Qos for 'iface'. */
+ shash_from_ovs_idl_map(qos->key_other_config, qos->value_other_config,
+ qos->n_other_config, &details);
+ netdev_set_qos(iface->netdev, qos->type, &details);
+ shash_destroy(&details);
+
+ /* Deconfigure queues that were deleted. */
+ cbdata.netdev = iface->netdev;
+ cbdata.queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER,
+ OVSDB_TYPE_UUID);
+ netdev_dump_queues(iface->netdev, iface_delete_queues, &cbdata);
+
+ /* Configure queues for 'iface'. */
+ for (i = 0; i < qos->n_queues; i++) {
+ const struct ovsrec_queue *queue = qos->value_queues[i];
+ unsigned int queue_id = qos->key_queues[i];
+
+ shash_from_ovs_idl_map(queue->key_other_config,
+ queue->value_other_config,
+ queue->n_other_config, &details);
+ netdev_set_queue(iface->netdev, queue_id, &details);
+ shash_destroy(&details);
+ }
+ }
+}