+
+/* Sets the ofport column of 'if_cfg' to 'ofport'. */
+static void
+iface_set_ofport(const struct ovsrec_interface *if_cfg, int64_t ofport)
+{
+ if (if_cfg) {
+ ovsrec_interface_set_ofport(if_cfg, &ofport, 1);
+ }
+}
+
+/* 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]);
+ }
+}
+
+/* Creates 'keys' and 'values' arrays from 'shash'.
+ *
+ * Sets 'keys' and 'values' to heap allocated arrays representing the key-value
+ * pairs in 'shash'. The caller takes ownership of 'keys' and 'values'. They
+ * are populated with with strings taken directly from 'shash' and thus have
+ * the same ownership of the key-value pairs in shash.
+ */
+static void
+shash_to_ovs_idl_map(struct shash *shash,
+ char ***keys, char ***values, size_t *n)
+{
+ size_t i, count;
+ char **k, **v;
+ struct shash_node *sn;
+
+ count = shash_count(shash);
+
+ k = xmalloc(count * sizeof *k);
+ v = xmalloc(count * sizeof *v);
+
+ i = 0;
+ SHASH_FOR_EACH(sn, shash) {
+ k[i] = sn->name;
+ v[i] = sn->data;
+ i++;
+ }
+
+ *n = count;
+ *keys = k;
+ *values = v;
+}
+
+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_carrier(struct iface *iface)
+{
+ bool carrier = iface_get_carrier(iface);
+ if (carrier == iface->up) {
+ return;
+ }
+
+ iface->up = carrier;
+ if (iface->port->lacp) {
+ lacp_slave_carrier_changed(iface->port->lacp, iface);
+ }
+}
+
+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);
+ }
+ }
+}
+
+static void
+iface_update_cfm(struct iface *iface)
+{
+ size_t i;
+ struct cfm cfm;
+ uint16_t *remote_mps;
+ struct ovsrec_monitor *mon;
+ uint8_t maid[CCM_MAID_LEN];
+
+ mon = iface->cfg->monitor;
+
+ if (!mon) {
+ ofproto_iface_clear_cfm(iface->port->bridge->ofproto, iface->dp_ifidx);
+ return;
+ }
+
+ if (!cfm_generate_maid(mon->md_name, mon->ma_name, maid)) {
+ VLOG_WARN("interface %s: Failed to generate MAID.", iface->name);
+ return;
+ }
+
+ cfm.mpid = mon->mpid;
+ cfm.interval = mon->interval ? *mon->interval : 1000;
+
+ memcpy(cfm.maid, maid, sizeof cfm.maid);
+
+ remote_mps = xzalloc(mon->n_remote_mps * sizeof *remote_mps);
+ for(i = 0; i < mon->n_remote_mps; i++) {
+ remote_mps[i] = mon->remote_mps[i]->mpid;
+ }
+
+ ofproto_iface_set_cfm(iface->port->bridge->ofproto, iface->dp_ifidx,
+ &cfm, remote_mps, mon->n_remote_mps);
+ free(remote_mps);
+}
+
+/* Read carrier or miimon status directly from 'iface''s netdev, according to
+ * how 'iface''s port is configured.
+ *
+ * Returns true if 'iface' is up, false otherwise. */
+static bool
+iface_get_carrier(const struct iface *iface)
+{
+ return (iface->port->monitor
+ ? netdev_get_carrier(iface->netdev)
+ : netdev_get_miimon(iface->netdev));
+}