The incremental processing engine might stop a run before the en_runtime_data node is processed. In such cases the ed_runtime_data fields might contain pointers to already deleted SB records. For example, if a port binding corresponding to a patch port is removed from the SB database and the incremental processing engine aborts before the en_runtime_data node is processed then the corresponding local_datapath hashtable entry in ed_runtime_data is stale and will store a pointer to the already freed sbrec_port_binding record.
This will cause invalid memory accesses in various places (e.g., pinctrl_run() -> prepare_ipv6_ras()). To fix the issue we introduce the engine_get_data() API which must be called in order to safely access internal node data. If the node is in state EN_STALE or EN_ABORTED, engine_get_data() returns NULL as the references might be stale. This commit also adds an "is_valid()" method to engine nodes to allow users to override the default behavior of determining if data is valid in a node (e.g., for the ct-zones node the data is always safe to access). Also, all interactions with node data outside inc-proc-eng.c are now performed through APIs and never by directly accessing the node->data field. This makes it easier to ensure that we don't access invalid (stale) data. CC: Han Zhou <[email protected]> Fixes: ca278d98a4f5 ("ovn-controller: Initial use of incremental engine - quiet mode.") Signed-off-by: Dumitru Ceara <[email protected]> --- v8: - First two patches were applied to master, so resending the last patch in the series as standalone patch. - Address Han's comments: - Remove internal_data from engine_node. - Use the newly added engine_get_data() to make sure we access valid data outside the incremental processing engine. - Remove data storage outside the nodes and have the init() callbacks allocate and initialize required memory. - Also, for better data encapsulations: - Remove all references of engine_node->data from ovn-controller.c and use inc-proc-eng APIs to access the data. - Change all init/cleanup/run/change_handlers to use data supplied as argument. - Use the newly added engine_get_input_data api to access input node data. - At init time, use engine_get_internal_data() to initialize the callback arguments for the unix cmd handlers and to initialize ofctrl. --- controller/ovn-controller.c | 457 +++++++++++++++++++++++--------------------- lib/inc-proc-eng.c | 59 +++++- lib/inc-proc-eng.h | 115 ++++++++--- 3 files changed, 370 insertions(+), 261 deletions(-) diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 64c44c9..5874776 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -739,26 +739,25 @@ struct ed_type_ofctrl_is_connected { bool connected; }; -static void -en_ofctrl_is_connected_init(struct engine_node *node) +static void * +en_ofctrl_is_connected_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_ofctrl_is_connected *data = - (struct ed_type_ofctrl_is_connected *)node->data; - data->connected = false; + struct ed_type_ofctrl_is_connected *data = xzalloc(sizeof *data); + return data; } static void -en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED) +en_ofctrl_is_connected_cleanup(void *data OVS_UNUSED) { } static void -en_ofctrl_is_connected_run(struct engine_node *node) +en_ofctrl_is_connected_run(struct engine_node *node, void *data) { - struct ed_type_ofctrl_is_connected *data = - (struct ed_type_ofctrl_is_connected *)node->data; - if (data->connected != ofctrl_is_connected()) { - data->connected = !data->connected; + struct ed_type_ofctrl_is_connected *of_data = data; + if (of_data->connected != ofctrl_is_connected()) { + of_data->connected = !of_data->connected; engine_set_node_state(node, EN_UPDATED); return; } @@ -773,21 +772,24 @@ struct ed_type_addr_sets { struct sset updated; }; -static void -en_addr_sets_init(struct engine_node *node) +static void * +en_addr_sets_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; + struct ed_type_addr_sets *as = xzalloc(sizeof *as); + shash_init(&as->addr_sets); as->change_tracked = false; sset_init(&as->new); sset_init(&as->deleted); sset_init(&as->updated); + return as; } static void -en_addr_sets_cleanup(struct engine_node *node) +en_addr_sets_cleanup(void *data) { - struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; + struct ed_type_addr_sets *as = data; expr_const_sets_destroy(&as->addr_sets); shash_destroy(&as->addr_sets); sset_destroy(&as->new); @@ -796,9 +798,9 @@ en_addr_sets_cleanup(struct engine_node *node) } static void -en_addr_sets_run(struct engine_node *node) +en_addr_sets_run(struct engine_node *node, void *data) { - struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; + struct ed_type_addr_sets *as = data; sset_clear(&as->new); sset_clear(&as->deleted); @@ -816,9 +818,9 @@ en_addr_sets_run(struct engine_node *node) } static bool -addr_sets_sb_address_set_handler(struct engine_node *node) +addr_sets_sb_address_set_handler(struct engine_node *node, void *data) { - struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data; + struct ed_type_addr_sets *as = data; sset_clear(&as->new); sset_clear(&as->deleted); @@ -850,21 +852,24 @@ struct ed_type_port_groups{ struct sset updated; }; -static void -en_port_groups_init(struct engine_node *node) +static void * +en_port_groups_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; + struct ed_type_port_groups *pg = xzalloc(sizeof *pg); + shash_init(&pg->port_groups); pg->change_tracked = false; sset_init(&pg->new); sset_init(&pg->deleted); sset_init(&pg->updated); + return pg; } static void -en_port_groups_cleanup(struct engine_node *node) +en_port_groups_cleanup(void *data) { - struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; + struct ed_type_port_groups *pg = data; expr_const_sets_destroy(&pg->port_groups); shash_destroy(&pg->port_groups); sset_destroy(&pg->new); @@ -873,9 +878,9 @@ en_port_groups_cleanup(struct engine_node *node) } static void -en_port_groups_run(struct engine_node *node) +en_port_groups_run(struct engine_node *node, void *data) { - struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; + struct ed_type_port_groups *pg = data; sset_clear(&pg->new); sset_clear(&pg->deleted); @@ -893,9 +898,9 @@ en_port_groups_run(struct engine_node *node) } static bool -port_groups_sb_port_group_handler(struct engine_node *node) +port_groups_sb_port_group_handler(struct engine_node *node, void *data) { - struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data; + struct ed_type_port_groups *pg = data; sset_clear(&pg->new); sset_clear(&pg->deleted); @@ -936,47 +941,46 @@ struct ed_type_runtime_data { struct sset active_tunnels; }; -static void -en_runtime_data_init(struct engine_node *node) +static void * +en_runtime_data_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)node->data; + struct ed_type_runtime_data *data = xzalloc(sizeof *data); hmap_init(&data->local_datapaths); sset_init(&data->local_lports); sset_init(&data->local_lport_ids); sset_init(&data->active_tunnels); + return data; } static void -en_runtime_data_cleanup(struct engine_node *node) +en_runtime_data_cleanup(void *data) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)node->data; + struct ed_type_runtime_data *rt_data = data; - sset_destroy(&data->local_lports); - sset_destroy(&data->local_lport_ids); - sset_destroy(&data->active_tunnels); + sset_destroy(&rt_data->local_lports); + sset_destroy(&rt_data->local_lport_ids); + sset_destroy(&rt_data->active_tunnels); struct local_datapath *cur_node, *next_node; HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, - &data->local_datapaths) { + &rt_data->local_datapaths) { free(cur_node->peer_ports); free(cur_node->ports); - hmap_remove(&data->local_datapaths, &cur_node->hmap_node); + hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node); free(cur_node); } - hmap_destroy(&data->local_datapaths); + hmap_destroy(&rt_data->local_datapaths); } static void -en_runtime_data_run(struct engine_node *node) +en_runtime_data_run(struct engine_node *node, void *data) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)node->data; - struct hmap *local_datapaths = &data->local_datapaths; - struct sset *local_lports = &data->local_lports; - struct sset *local_lport_ids = &data->local_lport_ids; - struct sset *active_tunnels = &data->active_tunnels; + struct ed_type_runtime_data *rt_data = data; + struct hmap *local_datapaths = &rt_data->local_datapaths; + struct sset *local_lports = &rt_data->local_lports; + struct sset *local_lport_ids = &rt_data->local_lport_ids; + struct sset *active_tunnels = &rt_data->active_tunnels; static bool first_run = true; if (first_run) { @@ -1020,8 +1024,7 @@ en_runtime_data_run(struct engine_node *node) ovs_assert(chassis); struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected = - (struct ed_type_ofctrl_is_connected *)engine_get_input( - "ofctrl_is_connected", node)->data; + engine_get_input_data("ofctrl_is_connected", node); if (ed_ofctrl_is_connected->connected) { /* Calculate the active tunnels only if have an an active * OpenFlow connection to br-int. @@ -1075,12 +1078,11 @@ en_runtime_data_run(struct engine_node *node) } static bool -runtime_data_sb_port_binding_handler(struct engine_node *node) +runtime_data_sb_port_binding_handler(struct engine_node *node, void *data) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)node->data; - struct sset *local_lports = &data->local_lports; - struct sset *active_tunnels = &data->active_tunnels; + struct ed_type_runtime_data *rt_data = data; + struct sset *local_lports = &rt_data->local_lports; + struct sset *active_tunnels = &rt_data->active_tunnels; struct ovsrec_open_vswitch_table *ovs_table = (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( @@ -1119,10 +1121,10 @@ struct ed_type_ct_zones { struct simap current; }; -static void -en_ct_zones_init(struct engine_node *node) +static void * +en_ct_zones_init(struct engine_node *node, struct engine_arg *arg OVS_UNUSED) { - struct ed_type_ct_zones *data = node->data; + struct ed_type_ct_zones *data = xzalloc(sizeof *data); struct ovsrec_open_vswitch_table *ovs_table = (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( engine_get_input("OVS_open_vswitch", node)); @@ -1136,56 +1138,63 @@ en_ct_zones_init(struct engine_node *node) memset(data->bitmap, 0, sizeof data->bitmap); bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */ restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap); + return data; } static void -en_ct_zones_cleanup(struct engine_node *node) +en_ct_zones_cleanup(void *data) { - struct ed_type_ct_zones *data = node->data; + struct ed_type_ct_zones *ct_zones_data = data; - simap_destroy(&data->current); - shash_destroy(&data->pending); + simap_destroy(&ct_zones_data->current); + shash_destroy(&ct_zones_data->pending); } static void -en_ct_zones_run(struct engine_node *node) +en_ct_zones_run(struct engine_node *node, void *data) { - struct ed_type_ct_zones *data = node->data; + struct ed_type_ct_zones *ct_zones_data = data; struct ed_type_runtime_data *rt_data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; + engine_get_input_data("runtime_data", node); update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths, - &data->current, data->bitmap, &data->pending); + &ct_zones_data->current, ct_zones_data->bitmap, + &ct_zones_data->pending); engine_set_node_state(node, EN_UPDATED); } +/* The data in the ct_zones node is always valid (i.e., no stale pointers). */ +static bool +en_ct_zones_is_valid(struct engine_node *node OVS_UNUSED) +{ + return true; +} + struct ed_type_mff_ovn_geneve { enum mf_field_id mff_ovn_geneve; }; -static void -en_mff_ovn_geneve_init(struct engine_node *node) +static void * +en_mff_ovn_geneve_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_mff_ovn_geneve *data = - (struct ed_type_mff_ovn_geneve *)node->data; - data->mff_ovn_geneve = 0; + struct ed_type_mff_ovn_geneve *data = xzalloc(sizeof *data); + return data; } static void -en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED) +en_mff_ovn_geneve_cleanup(void *data OVS_UNUSED) { } static void -en_mff_ovn_geneve_run(struct engine_node *node) +en_mff_ovn_geneve_run(struct engine_node *node, void *data) { - struct ed_type_mff_ovn_geneve *data = - (struct ed_type_mff_ovn_geneve *)node->data; + struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = data; enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id(); - if (data->mff_ovn_geneve != mff_ovn_geneve) { - data->mff_ovn_geneve = mff_ovn_geneve; + if (ed_mff_ovn_geneve->mff_ovn_geneve != mff_ovn_geneve) { + ed_mff_ovn_geneve->mff_ovn_geneve = mff_ovn_geneve; engine_set_node_state(node, EN_UPDATED); return; } @@ -1205,48 +1214,46 @@ struct ed_type_flow_output { struct lflow_resource_ref lflow_resource_ref; }; -static void -en_flow_output_init(struct engine_node *node) +static void * +en_flow_output_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) { - struct ed_type_flow_output *data = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *data = xzalloc(sizeof *data); + ovn_desired_flow_table_init(&data->flow_table); ovn_extend_table_init(&data->group_table); ovn_extend_table_init(&data->meter_table); data->conj_id_ofs = 1; lflow_resource_init(&data->lflow_resource_ref); + return data; } static void -en_flow_output_cleanup(struct engine_node *node) +en_flow_output_cleanup(void *data) { - struct ed_type_flow_output *data = - (struct ed_type_flow_output *)node->data; - ovn_desired_flow_table_destroy(&data->flow_table); - ovn_extend_table_destroy(&data->group_table); - ovn_extend_table_destroy(&data->meter_table); - lflow_resource_destroy(&data->lflow_resource_ref); + struct ed_type_flow_output *flow_output_data = data; + ovn_desired_flow_table_destroy(&flow_output_data->flow_table); + ovn_extend_table_destroy(&flow_output_data->group_table); + ovn_extend_table_destroy(&flow_output_data->meter_table); + lflow_resource_destroy(&flow_output_data->lflow_resource_ref); } static void -en_flow_output_run(struct engine_node *node) +en_flow_output_run(struct engine_node *node, void *data) { struct ed_type_runtime_data *rt_data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; + engine_get_input_data("runtime_data", node); struct hmap *local_datapaths = &rt_data->local_datapaths; struct sset *local_lports = &rt_data->local_lports; struct sset *local_lport_ids = &rt_data->local_lport_ids; struct sset *active_tunnels = &rt_data->active_tunnels; struct ed_type_ct_zones *ct_zones_data = - (struct ed_type_ct_zones *)engine_get_input( - "ct_zones", node)->data; + engine_get_input_data("ct_zones", node); struct simap *ct_zones = &ct_zones_data->current; struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = - (struct ed_type_mff_ovn_geneve *)engine_get_input( - "mff_ovn_geneve", node)->data; + engine_get_input_data("mff_ovn_geneve", node); enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; struct ovsrec_open_vswitch_table *ovs_table = @@ -1263,12 +1270,11 @@ en_flow_output_run(struct engine_node *node) engine_get_input("SB_chassis", node), "name"); struct ed_type_addr_sets *as_data = - (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; + engine_get_input_data("addr_sets", node); struct shash *addr_sets = &as_data->addr_sets; struct ed_type_port_groups *pg_data = - (struct ed_type_port_groups *)engine_get_input( - "port_groups", node)->data; + engine_get_input_data("port_groups", node); struct shash *port_groups = &pg_data->port_groups; const struct sbrec_chassis *chassis = NULL; @@ -1278,8 +1284,7 @@ en_flow_output_run(struct engine_node *node) ovs_assert(br_int && chassis); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; struct ovn_extend_table *group_table = &fo->group_table; struct ovn_extend_table *meter_table = &fo->meter_table; @@ -1359,21 +1364,19 @@ en_flow_output_run(struct engine_node *node) } static bool -flow_output_sb_logical_flow_handler(struct engine_node *node) -{ - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; - struct hmap *local_datapaths = &data->local_datapaths; - struct sset *local_lport_ids = &data->local_lport_ids; - struct sset *active_tunnels = &data->active_tunnels; +flow_output_sb_logical_flow_handler(struct engine_node *node, void *data) +{ + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + struct hmap *local_datapaths = &rt_data->local_datapaths; + struct sset *local_lport_ids = &rt_data->local_lport_ids; + struct sset *active_tunnels = &rt_data->active_tunnels; struct ed_type_addr_sets *as_data = - (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; + engine_get_input_data("addr_sets", node); struct shash *addr_sets = &as_data->addr_sets; struct ed_type_port_groups *pg_data = - (struct ed_type_port_groups *)engine_get_input( - "port_groups", node)->data; + engine_get_input_data("port_groups", node); struct shash *port_groups = &pg_data->port_groups; struct ovsrec_open_vswitch_table *ovs_table = @@ -1397,8 +1400,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node) ovs_assert(br_int && chassis); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; struct ovn_extend_table *group_table = &fo->group_table; struct ovn_extend_table *meter_table = &fo->meter_table; @@ -1442,7 +1444,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node) } static bool -flow_output_sb_mac_binding_handler(struct engine_node *node) +flow_output_sb_mac_binding_handler(struct engine_node *node, void *data) { struct ovsdb_idl_index *sbrec_port_binding_by_name = engine_ovsdb_node_get_index( @@ -1453,8 +1455,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node) (struct sbrec_mac_binding_table *)EN_OVSDB_GET( engine_get_input("SB_mac_binding", node)); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; lflow_handle_changed_neighbors(sbrec_port_binding_by_name, @@ -1465,22 +1466,19 @@ flow_output_sb_mac_binding_handler(struct engine_node *node) } static bool -flow_output_sb_port_binding_handler(struct engine_node *node) +flow_output_sb_port_binding_handler(struct engine_node *node, void *data) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; - struct hmap *local_datapaths = &data->local_datapaths; - struct sset *active_tunnels = &data->active_tunnels; + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + struct hmap *local_datapaths = &rt_data->local_datapaths; + struct sset *active_tunnels = &rt_data->active_tunnels; struct ed_type_ct_zones *ct_zones_data = - (struct ed_type_ct_zones *)engine_get_input( - "ct_zones", node)->data; + engine_get_input_data("ct_zones", node); struct simap *ct_zones = &ct_zones_data->current; struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = - (struct ed_type_mff_ovn_geneve *)engine_get_input( - "mff_ovn_geneve", node)->data; + engine_get_input_data("mff_ovn_geneve", node); enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; struct ovsrec_open_vswitch_table *ovs_table = @@ -1502,8 +1500,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node) } ovs_assert(br_int && chassis); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; struct ovsdb_idl_index *sbrec_port_binding_by_name = @@ -1573,21 +1570,18 @@ flow_output_sb_port_binding_handler(struct engine_node *node) } static bool -flow_output_sb_multicast_group_handler(struct engine_node *node) +flow_output_sb_multicast_group_handler(struct engine_node *node, void *data) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; - struct hmap *local_datapaths = &data->local_datapaths; + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + struct hmap *local_datapaths = &rt_data->local_datapaths; struct ed_type_ct_zones *ct_zones_data = - (struct ed_type_ct_zones *)engine_get_input( - "ct_zones", node)->data; + engine_get_input_data("ct_zones", node); struct simap *ct_zones = &ct_zones_data->current; struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = - (struct ed_type_mff_ovn_geneve *)engine_get_input( - "mff_ovn_geneve", node)->data; + engine_get_input_data("mff_ovn_geneve", node); enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve; struct ovsrec_open_vswitch_table *ovs_table = @@ -1609,8 +1603,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) } ovs_assert(br_int && chassis); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; struct sbrec_multicast_group_table *multicast_group_table = @@ -1627,23 +1620,21 @@ flow_output_sb_multicast_group_handler(struct engine_node *node) } static bool -_flow_output_resource_ref_handler(struct engine_node *node, - enum ref_type ref_type) +_flow_output_resource_ref_handler(struct engine_node *node, void *data, + enum ref_type ref_type) { - struct ed_type_runtime_data *data = - (struct ed_type_runtime_data *)engine_get_input( - "runtime_data", node)->data; - struct hmap *local_datapaths = &data->local_datapaths; - struct sset *local_lport_ids = &data->local_lport_ids; - struct sset *active_tunnels = &data->active_tunnels; + struct ed_type_runtime_data *rt_data = + engine_get_input_data("runtime_data", node); + struct hmap *local_datapaths = &rt_data->local_datapaths; + struct sset *local_lport_ids = &rt_data->local_lport_ids; + struct sset *active_tunnels = &rt_data->active_tunnels; struct ed_type_addr_sets *as_data = - (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data; + engine_get_input_data("addr_sets", node); struct shash *addr_sets = &as_data->addr_sets; struct ed_type_port_groups *pg_data = - (struct ed_type_port_groups *)engine_get_input( - "port_groups", node)->data; + engine_get_input_data("port_groups", node); struct shash *port_groups = &pg_data->port_groups; struct ovsrec_open_vswitch_table *ovs_table = @@ -1666,8 +1657,7 @@ _flow_output_resource_ref_handler(struct engine_node *node, ovs_assert(br_int && chassis); - struct ed_type_flow_output *fo = - (struct ed_type_flow_output *)node->data; + struct ed_type_flow_output *fo = data; struct ovn_desired_flow_table *flow_table = &fo->flow_table; struct ovn_extend_table *group_table = &fo->group_table; struct ovn_extend_table *meter_table = &fo->meter_table; @@ -1774,15 +1764,15 @@ _flow_output_resource_ref_handler(struct engine_node *node, } static bool -flow_output_addr_sets_handler(struct engine_node *node) +flow_output_addr_sets_handler(struct engine_node *node, void *data) { - return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET); + return _flow_output_resource_ref_handler(node, data, REF_TYPE_ADDRSET); } static bool -flow_output_port_groups_handler(struct engine_node *node) +flow_output_port_groups_handler(struct engine_node *node, void *data) { - return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP); + return _flow_output_resource_ref_handler(node, data, REF_TYPE_PORTGROUP); } struct ovn_controller_exit_args { @@ -1892,15 +1882,7 @@ main(int argc, char *argv[]) stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS); /* Define inc-proc-engine nodes. */ - struct ed_type_ct_zones ed_ct_zones; - struct ed_type_runtime_data ed_runtime_data; - struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve; - struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected; - struct ed_type_flow_output ed_flow_output; - struct ed_type_addr_sets ed_addr_sets; - struct ed_type_port_groups ed_port_groups; - - ENGINE_NODE(ct_zones, "ct_zones"); + ENGINE_NODE_CUSTOM_DATA(ct_zones, "ct_zones"); ENGINE_NODE(runtime_data, "runtime_data"); ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve"); ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected"); @@ -1916,18 +1898,6 @@ main(int argc, char *argv[]) OVS_NODES #undef OVS_NODE - engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name); - engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath", - sbrec_multicast_group_by_name_datapath); - engine_ovsdb_node_add_index(&en_sb_port_binding, "name", - sbrec_port_binding_by_name); - engine_ovsdb_node_add_index(&en_sb_port_binding, "key", - sbrec_port_binding_by_key); - engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath", - sbrec_port_binding_by_datapath); - engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key", - sbrec_datapath_binding_by_key); - /* Add dependencies between inc-proc-engine nodes. */ engine_add_input(&en_addr_sets, &en_sb_address_set, @@ -1976,20 +1946,45 @@ main(int argc, char *argv[]) engine_add_input(&en_runtime_data, &en_sb_port_binding, runtime_data_sb_port_binding_handler); - engine_init(&en_flow_output); + struct engine_arg engine_arg = { + .sb_idl = ovnsb_idl_loop.idl, + .ovs_idl = ovs_idl_loop.idl, + }; + engine_init(&en_flow_output, &engine_arg); - ofctrl_init(&ed_flow_output.group_table, - &ed_flow_output.meter_table, + engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name); + engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath", + sbrec_multicast_group_by_name_datapath); + engine_ovsdb_node_add_index(&en_sb_port_binding, "name", + sbrec_port_binding_by_name); + engine_ovsdb_node_add_index(&en_sb_port_binding, "key", + sbrec_port_binding_by_key); + engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath", + sbrec_port_binding_by_datapath); + engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key", + sbrec_datapath_binding_by_key); + + struct ed_type_flow_output *flow_output_data = + engine_get_internal_data(&en_flow_output); + struct ed_type_ct_zones *ct_zones_data = + engine_get_internal_data(&en_ct_zones); + struct ed_type_runtime_data *runtime_data = NULL; + + ofctrl_init(&flow_output_data->group_table, + &flow_output_data->meter_table, get_ofctrl_probe_interval(ovs_idl_loop.idl)); unixctl_command_register("group-table-list", "", 0, 0, - group_table_list, &ed_flow_output.group_table); + group_table_list, + &flow_output_data->group_table); unixctl_command_register("meter-table-list", "", 0, 0, - meter_table_list, &ed_flow_output.meter_table); + meter_table_list, + &flow_output_data->meter_table); unixctl_command_register("ct-zone-list", "", 0, 0, - ct_zone_list, &ed_ct_zones.current); + ct_zone_list, + &ct_zones_data->current); struct pending_pkt pending_pkt = { .conn = NULL }; unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, @@ -2065,7 +2060,10 @@ main(int argc, char *argv[]) } if (br_int) { - ofctrl_run(br_int, &ed_ct_zones.pending); + ct_zones_data = engine_get_data(&en_ct_zones); + if (ct_zones_data) { + ofctrl_run(br_int, &ct_zones_data->pending); + } if (chassis) { patch_run(ovs_idl_txn, @@ -2105,41 +2103,50 @@ main(int argc, char *argv[]) } stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, time_msec()); + ct_zones_data = engine_get_data(&en_ct_zones); if (ovs_idl_txn) { - commit_ct_zones(br_int, &ed_ct_zones.pending); + if (ct_zones_data) { + commit_ct_zones(br_int, &ct_zones_data->pending); + } bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl), br_int, chassis, sbrec_ha_chassis_group_table_get( ovnsb_idl_loop.idl), sbrec_sb_global_table_get(ovnsb_idl_loop.idl)); } - ofctrl_put(&ed_flow_output.flow_table, - &ed_ct_zones.pending, - sbrec_meter_table_get(ovnsb_idl_loop.idl), - get_nb_cfg(sbrec_sb_global_table_get( - ovnsb_idl_loop.idl)), - engine_node_changed(&en_flow_output)); - pinctrl_run(ovnsb_idl_txn, - sbrec_datapath_binding_by_key, - sbrec_port_binding_by_datapath, - sbrec_port_binding_by_key, - sbrec_port_binding_by_name, - sbrec_mac_binding_by_lport_ip, - sbrec_igmp_group, - sbrec_ip_multicast, - sbrec_dns_table_get(ovnsb_idl_loop.idl), - sbrec_controller_event_table_get( - ovnsb_idl_loop.idl), - sbrec_service_monitor_table_get( - ovnsb_idl_loop.idl), - br_int, chassis, - &ed_runtime_data.local_datapaths, - &ed_runtime_data.active_tunnels); - if (engine_node_changed(&en_runtime_data)) { - update_sb_monitors(ovnsb_idl_loop.idl, chassis, - &ed_runtime_data.local_lports, - &ed_runtime_data.local_datapaths); + flow_output_data = engine_get_data(&en_flow_output); + if (flow_output_data && ct_zones_data) { + ofctrl_put(&flow_output_data->flow_table, + &ct_zones_data->pending, + sbrec_meter_table_get(ovnsb_idl_loop.idl), + get_nb_cfg(sbrec_sb_global_table_get( + ovnsb_idl_loop.idl)), + engine_node_changed(&en_flow_output)); + } + runtime_data = engine_get_data(&en_runtime_data); + if (runtime_data) { + pinctrl_run(ovnsb_idl_txn, + sbrec_datapath_binding_by_key, + sbrec_port_binding_by_datapath, + sbrec_port_binding_by_key, + sbrec_port_binding_by_name, + sbrec_mac_binding_by_lport_ip, + sbrec_igmp_group, + sbrec_ip_multicast, + sbrec_dns_table_get(ovnsb_idl_loop.idl), + sbrec_controller_event_table_get( + ovnsb_idl_loop.idl), + sbrec_service_monitor_table_get( + ovnsb_idl_loop.idl), + br_int, chassis, + &runtime_data->local_datapaths, + &runtime_data->active_tunnels); + if (engine_node_changed(&en_runtime_data)) { + update_sb_monitors(ovnsb_idl_loop.idl, chassis, + &runtime_data->local_lports, + &runtime_data->local_datapaths); + } } } @@ -2174,9 +2181,13 @@ main(int argc, char *argv[]) if (pending_pkt.conn) { - if (br_int && chassis) { + struct ed_type_addr_sets *as_data = + engine_get_data(&en_addr_sets); + struct ed_type_port_groups *pg_data = + engine_get_data(&en_port_groups); + if (br_int && chassis && as_data && pg_data) { char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s, - &ed_addr_sets.addr_sets, &ed_port_groups.port_groups); + &as_data->addr_sets, &pg_data->port_groups); if (error) { unixctl_command_reply_error(pending_pkt.conn, error); free(error); @@ -2214,12 +2225,16 @@ main(int argc, char *argv[]) } if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) { - struct shash_node *iter, *iter_next; - SHASH_FOR_EACH_SAFE (iter, iter_next, &ed_ct_zones.pending) { - struct ct_zone_pending_entry *ctzpe = iter->data; - if (ctzpe->state == CT_ZONE_DB_SENT) { - shash_delete(&ed_ct_zones.pending, iter); - free(ctzpe); + ct_zones_data = engine_get_data(&en_ct_zones); + if (ct_zones_data) { + struct shash_node *iter, *iter_next; + SHASH_FOR_EACH_SAFE (iter, iter_next, + &ct_zones_data->pending) { + struct ct_zone_pending_entry *ctzpe = iter->data; + if (ctzpe->state == CT_ZONE_DB_SENT) { + shash_delete(&ct_zones_data->pending, iter); + free(ctzpe); + } } } } diff --git a/lib/inc-proc-eng.c b/lib/inc-proc-eng.c index 59b5cac..9b1479a 100644 --- a/lib/inc-proc-eng.c +++ b/lib/inc-proc-eng.c @@ -103,13 +103,16 @@ engine_get_nodes(struct engine_node *node, size_t *n_count) } void -engine_init(struct engine_node *node) +engine_init(struct engine_node *node, struct engine_arg *arg) { engine_nodes = engine_get_nodes(node, &engine_n_nodes); for (size_t i = 0; i < engine_n_nodes; i++) { if (engine_nodes[i]->init) { - engine_nodes[i]->init(engine_nodes[i]); + engine_nodes[i]->data = + engine_nodes[i]->init(engine_nodes[i], arg); + } else { + engine_nodes[i]->data = NULL; } } } @@ -119,8 +122,9 @@ engine_cleanup(void) { for (size_t i = 0; i < engine_n_nodes; i++) { if (engine_nodes[i]->cleanup) { - engine_nodes[i]->cleanup(engine_nodes[i]); + engine_nodes[i]->cleanup(engine_nodes[i]->data); } + free(engine_nodes[i]->data); } free(engine_nodes); engine_nodes = NULL; @@ -140,9 +144,16 @@ engine_get_input(const char *input_name, struct engine_node *node) return NULL; } +void * +engine_get_input_data(const char *input_name, struct engine_node *node) +{ + struct engine_node *input_node = engine_get_input(input_name, node); + return engine_get_data(input_node); +} + void engine_add_input(struct engine_node *node, struct engine_node *input, - bool (*change_handler)(struct engine_node *)) + bool (*change_handler)(struct engine_node *, void *)) { ovs_assert(node->n_inputs < ENGINE_MAX_INPUT); node->inputs[node->n_inputs].node = input; @@ -153,7 +164,7 @@ engine_add_input(struct engine_node *node, struct engine_node *input, struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *node, const char *name) { - struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data; + struct ed_type_ovsdb_table *ed = node->data; for (size_t i = 0; i < ed->n_indexes; i++) { if (!strcmp(ed->indexes[i].name, name)) { return ed->indexes[i].index; @@ -167,7 +178,7 @@ void engine_ovsdb_node_add_index(struct engine_node *node, const char *name, struct ovsdb_idl_index *index) { - struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data; + struct ed_type_ovsdb_table *ed = node->data; ovs_assert(ed->n_indexes < ENGINE_MAX_OVSDB_INDEX); ed->indexes[ed->n_indexes].name = name; @@ -192,6 +203,19 @@ engine_set_node_state_at(struct engine_node *node, node->state = state; } +static bool +engine_node_valid(struct engine_node *node) +{ + if (node->state == EN_UPDATED || node->state == EN_VALID) { + return true; + } + + if (node->is_valid) { + return node->is_valid(node); + } + return false; +} + bool engine_node_changed(struct engine_node *node) { @@ -215,6 +239,21 @@ engine_aborted(void) return engine_run_aborted; } +void * +engine_get_data(struct engine_node *node) +{ + if (engine_node_valid(node)) { + return node->data; + } + return NULL; +} + +void * +engine_get_internal_data(struct engine_node *node) +{ + return node->data; +} + void engine_init_run(void) { @@ -240,7 +279,7 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed) } /* Run the node handler which might change state. */ - node->run(node); + node->run(node, node->data); } /* Return true if the node could be computed, false otherwise. */ @@ -256,7 +295,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed) /* If the input change can't be handled incrementally, run * the node handler. */ - if (!node->inputs[i].change_handler(node)) { + if (!node->inputs[i].change_handler(node, node->data)) { VLOG_DBG("node: %s, can't handle change for input %s, " "fall back to recompute", node->name, node->inputs[i].node->name); @@ -273,7 +312,7 @@ engine_run_node(struct engine_node *node, bool recompute_allowed) { if (!node->n_inputs) { /* Run the node handler which might change state. */ - node->run(node); + node->run(node, node->data); return; } @@ -345,7 +384,7 @@ engine_need_run(void) continue; } - engine_nodes[i]->run(engine_nodes[i]); + engine_nodes[i]->run(engine_nodes[i], engine_nodes[i]->data); VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name, engine_node_state_name[engine_nodes[i]->state]); if (engine_nodes[i]->state == EN_UPDATED) { diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h index bb8df07..5b92971 100644 --- a/lib/inc-proc-eng.h +++ b/lib/inc-proc-eng.h @@ -68,6 +68,12 @@ struct engine_context { struct ovsdb_idl_txn *ovnsb_idl_txn; }; +/* Arguments to be passed to the engine at engine_init(). */ +struct engine_arg { + struct ovsdb_idl *sb_idl; + struct ovsdb_idl *ovs_idl; +}; + struct engine_node; struct engine_node_input { @@ -79,7 +85,7 @@ struct engine_node_input { * - true: if change can be handled * - false: if change cannot be handled (indicating full recompute needed) */ - bool (*change_handler)(struct engine_node *node); + bool (*change_handler)(struct engine_node *node, void *data); }; enum engine_node_state { @@ -106,30 +112,42 @@ struct engine_node { /* Inputs of this node. */ struct engine_node_input inputs[ENGINE_MAX_INPUT]; - /* Data of this node. It is vague and interpreted by the related functions. - * The content of the data should be changed only by the change_handlers - * and run() function of the current node. Users should ensure that the - * data is read-only in change-handlers of the nodes that depends on this - * node. */ + /* A pointer to node internal data. The data is safely accessible to + * users through the engine_get_data() API. For special cases, when the + * data is known to be valid (e.g., at init time), users can also call + * engine_get_internal_data(). + */ void *data; /* State of the node after the last engine run. */ enum engine_node_state state; - /* Method to initialize data. It may be NULL. */ - void (*init)(struct engine_node *); + /* Method to allocate and initialize node data. It may be NULL. + * The user supplied argument 'arg' is passed from the call to + * engine_init(). + */ + void *(*init)(struct engine_node *node, struct engine_arg *arg); /* Method to clean up data. It may be NULL. */ - void (*cleanup)(struct engine_node *); + void (*cleanup)(void *data); /* Fully processes all inputs of this node and regenerates the data - * of this node */ - void (*run)(struct engine_node *); + * of this node. The pointer to the node's data is passed as argument. + */ + void (*run)(struct engine_node *node, void *data); + + /* Method to validate if the 'internal_data' is valid. This allows users + * to customize when 'data' can be used (e.g., even if the node + * hasn't been refreshed in the last iteration, if 'data' + * doesn't store pointers to DB records it's still safe to use). + */ + bool (*is_valid)(struct engine_node *); }; /* Initialize the data for the engine nodes. It calls each node's - * init() method if not NULL. It should be called before the main loop. */ -void engine_init(struct engine_node *node); + * init() method if not NULL passing the user supplied 'arg'. + * It should be called before the main loop. */ +void engine_init(struct engine_node *node, struct engine_arg *arg); /* Initialize the engine nodes for a new run. It should be called in the * main processing loop before every potential engine_run(). @@ -155,12 +173,15 @@ bool engine_need_run(void); struct engine_node * engine_get_input(const char *input_name, struct engine_node *); +/* Get the data from the input node with <name> for <node> */ +void *engine_get_input_data(const char *input_name, struct engine_node *); + /* Add an input (dependency) for <node>, with corresponding change_handler, * which can be NULL. If the change_handler is NULL, the engine will not * be able to process the change incrementally, and will fall back to call * the run method to recompute. */ void engine_add_input(struct engine_node *node, struct engine_node *input, - bool (*change_handler)(struct engine_node *)); + bool (*change_handler)(struct engine_node *, void *)); /* Force the engine to recompute everything if set to true. It is used * in circumstances when we are not sure there is change or not, or @@ -185,6 +206,25 @@ bool engine_has_run(void); /* Returns true if during the last engine run we had to abort processing. */ bool engine_aborted(void); +/* Return a pointer to node data accessible for users outside the processing + * engine. If the node data is not valid (e.g., last engine_run() failed or + * didn't happen), the node's is_valid() method is used to determine if the + * data can be safely accessed. If it's not the case, the function returns + * NULL. + * The content of the data should be changed only by the change_handlers + * and run() function of the current node. Users should ensure that the + * data is read-only in change-handlers of the nodes that depends on this + * node. + */ +void *engine_get_data(struct engine_node *node); + +/* Return a pointer to node data *without* performing any sanity checks on + * the state of the node. This may be used only in specific cases when data + * is guaranteed to be valid, e.g., immediately after initialization and + * before the first engine_run(). + */ +void *engine_get_internal_data(struct engine_node *node); + /* Set the state of the node and log changes. */ #define engine_set_node_state(node, state) \ engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR) @@ -201,30 +241,42 @@ struct ed_type_ovsdb_table { }; #define EN_OVSDB_GET(NODE) \ - (((struct ed_type_ovsdb_table *)NODE->data)->table) + (((struct ed_type_ovsdb_table *)(NODE)->data)->table) struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *, const char *name); +/* Adds an OVSDB IDL index to the node. This should be called only after + * engine_init() as the index is stored in the node data. + */ void engine_ovsdb_node_add_index(struct engine_node *, const char *name, struct ovsdb_idl_index *); /* Macro to define an engine node. */ -#define ENGINE_NODE(NAME, NAME_STR) \ +#define ENGINE_NODE_DEF(NAME, NAME_STR) \ struct engine_node en_##NAME = { \ .name = NAME_STR, \ - .data = &ed_##NAME, \ + .data = NULL, \ .state = EN_STALE, \ .init = en_##NAME##_init, \ .run = en_##NAME##_run, \ .cleanup = en_##NAME##_cleanup, \ + .is_valid = en_##NAME##_is_valid, \ }; +#define ENGINE_NODE_CUSTOM_DATA(NAME, NAME_STR) \ + ENGINE_NODE_DEF(NAME, NAME_STR) + +#define ENGINE_NODE(NAME, NAME_STR) \ + static bool (*en_##NAME##_is_valid)(struct engine_node *node) = NULL; \ + ENGINE_NODE_DEF(NAME, NAME_STR) + /* Macro to define member functions of an engine node which represents * a table of OVSDB */ #define ENGINE_FUNC_OVSDB(DB_NAME, TBL_NAME) \ static void \ -en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \ +en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node, \ + void *data OVS_UNUSED) \ { \ const struct DB_NAME##rec_##TBL_NAME##_table *table = \ EN_OVSDB_GET(node); \ @@ -234,10 +286,18 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \ } \ engine_set_node_state(node, EN_VALID); \ } \ -static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \ - = NULL; \ -static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \ - = NULL; +static void *en_##DB_NAME##_##TBL_NAME##_init( \ + struct engine_node *node OVS_UNUSED, \ + struct engine_arg *arg) \ +{ \ + struct ovsdb_idl *idl = arg->DB_NAME##_idl; \ + struct ed_type_ovsdb_table *data = xzalloc(sizeof *data); \ + data->table = DB_NAME##rec_##TBL_NAME##_table_get(idl); \ + return data; \ +} \ +static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \ +{ \ +} /* Macro to define member functions of an engine node which represents * a table of OVN SB DB */ @@ -250,21 +310,16 @@ static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \ ENGINE_FUNC_OVSDB(ovs, TBL_NAME) /* Macro to define an engine node which represents a table of OVSDB */ -#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR, IDL) \ - struct ed_type_ovsdb_table ed_##DB_NAME##_##TBL_NAME; \ - memset(&ed_##DB_NAME##_##TBL_NAME, 0, sizeof ed_##DB_NAME##_##TBL_NAME); \ - ovs_assert(IDL); \ - ed_##DB_NAME##_##TBL_NAME.table = \ - DB_NAME##rec_##TBL_NAME##_table_get(IDL); \ +#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR) \ ENGINE_NODE(DB_NAME##_##TBL_NAME, DB_NAME_STR"_"TBL_NAME_STR) /* Macro to define an engine node which represents a table of OVN SB DB */ #define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \ - ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR, ovnsb_idl_loop.idl); + ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR); /* Macro to define an engine node which represents a table of open_vswitch * DB */ #define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \ - ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR, ovs_idl_loop.idl); + ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR); #endif /* lib/inc-proc-eng.h */ -- 1.8.3.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
