@@ -44,7 +44,7 @@ using std::string;
4444namespace {
4545
4646constexpr int required_major_version = 2 ;
47- constexpr int max_minor_version = 23 ;
47+ constexpr int max_minor_version = 24 ;
4848// not needed for now
4949// constexpr int min_minor_version = 0;
5050
@@ -1575,6 +1575,57 @@ std::vector<MatchKeyParam> parse_match_key(
15751575
15761576} // namespace
15771577
1578+ void
1579+ P4Objects::check_next_nodes (const Json::Value &cfg_next_nodes,
1580+ const Json::Value &cfg_actions,
1581+ const std::string &table_name,
1582+ bool *next_is_hit_miss) {
1583+ // For each table, the value of its key "next_tables" must be an
1584+ // object with one of the following sets of keys:
1585+ // (a) The set of keys must include "__HIT__" and "__MISS__", but no
1586+ // others. This is how a P4 table is implemented if, where it
1587+ // is applied, it uses "t1.apply().hit" or "t1.apply().miss"
1588+ // conditions to control which code is executed next.
1589+ // (b) The set of keys must include each action name of the table
1590+ // exactly once, but no others. This is how a P4 table is
1591+ // implemented if where it is applied, it uses
1592+ // "t1.apply().action_run" and a switch statement to control
1593+ // which code is executed next. It is also used by the p4c BMv2
1594+ // backend for tables that use none of .hit, .miss, and
1595+ // .action_run, and always execute the same code next regardless
1596+ // of hit, miss, or which action the table executed. In that
1597+ // case, every action will have the same next node to execute
1598+ // regardless of the action.
1599+ int num_next_nodes = cfg_next_nodes.size ();
1600+ bool next_has_hit = cfg_next_nodes.isMember (" __HIT__" );
1601+ bool next_has_miss = cfg_next_nodes.isMember (" __MISS__" );
1602+ if (next_has_hit || next_has_miss) {
1603+ if (next_has_hit && next_has_miss && (num_next_nodes == 2 )) {
1604+ *next_is_hit_miss = true ;
1605+ } else {
1606+ throw json_exception (
1607+ EFormat () << " Table '" << table_name << " ' has one"
1608+ << " of keys '__HIT__' and '__MISS__' in 'next_tables'"
1609+ << " but either it does not have both of them,"
1610+ << " or it has other keys that should not be there." ,
1611+ cfg_next_nodes);
1612+ }
1613+ } else {
1614+ *next_is_hit_miss = false ;
1615+ int num_actions = cfg_actions.size ();
1616+ // The check that each action name is a key in cfg_next_nodes is
1617+ // done near where check_next_nodes is called, to avoid
1618+ // duplicating here the code that calculates action_name.
1619+ if (num_next_nodes != num_actions) {
1620+ throw json_exception (
1621+ EFormat () << " Table '" << table_name << " ' should have exactly "
1622+ << num_actions << " keys, one for each table action, but found "
1623+ << num_next_nodes << " keys." ,
1624+ cfg_next_nodes);
1625+ }
1626+ }
1627+ }
1628+
15781629void
15791630P4Objects::init_pipelines (const Json::Value &cfg_root,
15801631 LookupStructureFactory *lookup_factory,
@@ -1818,6 +1869,9 @@ P4Objects::init_pipelines(const Json::Value &cfg_root,
18181869 std::string actions_key = cfg_table.isMember (" action_ids" ) ? " action_ids"
18191870 : " actions" ;
18201871 const Json::Value &cfg_actions = cfg_table[actions_key];
1872+ bool next_is_hit_miss = false ;
1873+ check_next_nodes (cfg_next_nodes, cfg_actions, table_name,
1874+ &next_is_hit_miss);
18211875 for (const auto &cfg_action : cfg_actions) {
18221876 p4object_id_t action_id = 0 ;
18231877 string action_name = " " ;
@@ -1831,19 +1885,24 @@ P4Objects::init_pipelines(const Json::Value &cfg_root,
18311885 action = get_one_action_with_name (action_name); assert (action);
18321886 action_id = action->get_id ();
18331887 }
1834-
1888+ if (!next_is_hit_miss && !cfg_next_nodes.isMember (action_name)) {
1889+ throw json_exception (
1890+ EFormat () << " Table '" << table_name << " ' should have key"
1891+ << " for action '" << action_name
1892+ << " ' in its 'next_tables' object." ,
1893+ cfg_next_nodes);
1894+ }
18351895 const Json::Value &cfg_next_node = cfg_next_nodes[action_name];
18361896 const ControlFlowNode *next_node = get_next_node (cfg_next_node);
18371897 table->set_next_node (action_id, next_node);
18381898 add_action_to_table (table_name, action_name, action);
18391899 if (act_prof_name != " " )
18401900 add_action_to_act_prof (act_prof_name, action_name, action);
18411901 }
1842-
1843- if (cfg_next_nodes.isMember (" __HIT__" ))
1844- table->set_next_node_hit (get_next_node (cfg_next_nodes[" __HIT__" ]));
1845- if (cfg_next_nodes.isMember (" __MISS__" ))
1846- table->set_next_node_miss (get_next_node (cfg_next_nodes[" __MISS__" ]));
1902+ if (next_is_hit_miss) {
1903+ table->set_next_node_hit (get_next_node (cfg_next_nodes[" __HIT__" ]));
1904+ table->set_next_node_miss (get_next_node (cfg_next_nodes[" __MISS__" ]));
1905+ }
18471906
18481907 if (cfg_table.isMember (" base_default_next" )) {
18491908 table->set_next_node_miss_default (
@@ -1888,6 +1947,11 @@ P4Objects::init_pipelines(const Json::Value &cfg_root,
18881947
18891948 table->set_default_default_entry (action, std::move (adata),
18901949 is_action_entry_const);
1950+ } else {
1951+ throw json_exception (
1952+ EFormat () << " 'default_entry' of table '" << table_name
1953+ << " ' should have key 'action_data'" ,
1954+ cfg_default_entry);
18911955 }
18921956 }
18931957
@@ -1951,9 +2015,14 @@ P4Objects::init_pipelines(const Json::Value &cfg_root,
19512015 auto conditional_name = cfg_conditional[" name" ].asString ();
19522016 auto conditional = get_conditional (conditional_name);
19532017
2018+ if (!cfg_conditional.isMember (" true_next" ) &&
2019+ !cfg_conditional.isMember (" false_next" )) {
2020+ throw json_exception (" conditional must have either or both of the"
2021+ " keys 'true_next' and 'false_next'." ,
2022+ cfg_conditional);
2023+ }
19542024 const auto &cfg_true_next = cfg_conditional[" true_next" ];
19552025 const auto &cfg_false_next = cfg_conditional[" false_next" ];
1956-
19572026 if (!cfg_true_next.isNull ()) {
19582027 auto next_node = get_control_node_cfg (cfg_true_next.asString ());
19592028 conditional->set_next_node_if_true (next_node);
0 commit comments