@@ -19,6 +19,8 @@ Post-v2.16.0
* Add support for DPDK 21.11.
* Forbid use of DPDK multiprocess feature.
* Add support for running threads on cores >= RTE_MAX_LCORE.
+ * Add actions auto-validator function to compare different actions
+ implementations against default implementation.
- Python:
* For SSL support, the use of the pyOpenSSL library has been replaced
with the native 'ssl' module.
@@ -506,3 +506,26 @@ dp_packet_resize_l2(struct dp_packet *b, int increment)
dp_packet_adjust_layer_offset(&b->l2_5_ofs, increment);
return dp_packet_data(b);
}
+
+bool
+dp_packet_compare_and_log(struct dp_packet *good, struct dp_packet *test,
+ struct ds *err_str)
+{
+ if ((good->l2_pad_size != test->l2_pad_size) ||
+ (good->l2_5_ofs != test->l2_5_ofs) ||
+ (good->l3_ofs != test->l3_ofs) ||
+ (good->l4_ofs != test->l4_ofs)) {
+ ds_put_format(err_str, "Autovalidation packet offsets failed"
+ "\n");
+ ds_put_format(err_str, "Good offsets: l2_pad_size %u,"
+ " l2_5_ofs : %u l3_ofs %u, l4_ofs %u\n",
+ good->l2_pad_size, good->l2_5_ofs,
+ good->l3_ofs, good->l4_ofs);
+ ds_put_format(err_str, "Test offsets: l2_pad_size %u,"
+ " l2_5_ofs : %u l3_ofs %u, l4_ofs %u\n",
+ test->l2_pad_size, test->l2_5_ofs,
+ test->l3_ofs, test->l4_ofs);
+ return false;
+ }
+ return true;
+}
@@ -236,6 +236,11 @@ void *dp_packet_steal_data(struct dp_packet *);
static inline bool dp_packet_equal(const struct dp_packet *,
const struct dp_packet *);
+
+bool dp_packet_compare_and_log(struct dp_packet *good,
+ struct dp_packet *test,
+ struct ds *err_str);
+
/* Frees memory that 'b' points to, as well as 'b' itself. */
static inline void
@@ -30,8 +30,16 @@
int32_t action_autoval_init(struct odp_execute_action_impl *self);
VLOG_DEFINE_THIS_MODULE(odp_execute_private);
static uint32_t active_action_impl_index;
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
static struct odp_execute_action_impl action_impls[] = {
+ [ACTION_IMPL_AUTOVALIDATOR] = {
+ .available = 1,
+ .name = "autovalidator",
+ .probe = NULL,
+ .init_func = action_autoval_init,
+ },
+
[ACTION_IMPL_SCALAR] = {
.available = 1,
.name = "scalar",
@@ -99,3 +107,94 @@ odp_execute_action_init(void)
}
}
}
+
+/* Init sequence required to be scalar first to pick up the default scalar
+* implementations, allowing over-riding of the optimized functions later.
+*/
+BUILD_ASSERT_DECL(ACTION_IMPL_SCALAR == 0);
+BUILD_ASSERT_DECL(ACTION_IMPL_AUTOVALIDATOR == 1);
+
+/* Loop over packets, and validate each one for the given action. */
+static void
+action_autoval_generic(void *dp OVS_UNUSED, struct dp_packet_batch *batch,
+ const struct nlattr *a, bool should_steal)
+{
+ uint32_t failed = 0;
+
+ int type = nl_attr_type(a);
+ enum ovs_action_attr attr_type = (enum ovs_action_attr) type;
+
+ struct odp_execute_action_impl *scalar = &action_impls[ACTION_IMPL_SCALAR];
+
+ struct dp_packet_batch good_batch;
+ dp_packet_batch_clone(&good_batch, batch);
+
+ scalar->funcs[attr_type](NULL, &good_batch, a, should_steal);
+
+ for (uint32_t impl = ACTION_IMPL_BEGIN; impl < ACTION_IMPL_MAX; impl++) {
+ /* Clone original batch and execute implementation under test. */
+ struct dp_packet_batch test_batch;
+ dp_packet_batch_clone(&test_batch, batch);
+ action_impls[impl].funcs[attr_type](NULL, &test_batch, a,
+ should_steal);
+
+ /* Loop over implementations, checking each one. */
+ for (uint32_t pidx = 0; pidx < batch->count; pidx++) {
+ struct dp_packet *good_pkt = good_batch.packets[pidx];
+ struct dp_packet *test_pkt = test_batch.packets[pidx];
+
+ struct ds log_msg = DS_EMPTY_INITIALIZER;
+
+ /* Compare packet length and payload contents. */
+ bool eq = dp_packet_equal(good_pkt, test_pkt);
+
+ if (!eq) {
+ ds_put_format(&log_msg, "Packet: %d\nAction : ", pidx);
+ format_odp_actions(&log_msg, a, a->nla_len, NULL);
+ ds_put_format(&log_msg, "\nGood hex:\n");
+ ds_put_hex_dump(&log_msg, dp_packet_data(good_pkt),
+ dp_packet_size(good_pkt), 0, false);
+ ds_put_format(&log_msg, "Test hex:\n");
+ ds_put_hex_dump(&log_msg, dp_packet_data(test_pkt),
+ dp_packet_size(test_pkt), 0, false);
+
+ failed = 1;
+ }
+
+ /* Compare offsets and RSS */
+ if (!dp_packet_compare_and_log(good_pkt, test_pkt, &log_msg)) {
+ failed = 1;
+ }
+
+ uint32_t good_hash = dp_packet_get_rss_hash(good_pkt);
+ uint32_t test_hash = dp_packet_get_rss_hash(test_pkt);
+
+ if (good_hash != test_hash) {
+ ds_put_format(&log_msg, "Autovalidation rss hash failed"
+ "\n");
+ ds_put_format(&log_msg, "Good RSS hash : %u\n", good_hash);
+ ds_put_format(&log_msg, "Test RSS hash : %u\n", test_hash);
+
+ failed = 1;
+ }
+
+ if (failed) {
+ VLOG_ERR_RL(&rl, "\nAutovalidation failed details:\n%s",
+ ds_cstr(&log_msg));
+ }
+ }
+ dp_packet_delete_batch(&test_batch, 1);
+ }
+ dp_packet_delete_batch(&good_batch, 1);
+
+ /* Apply the action to the original batch for continued processing. */
+ scalar->funcs[attr_type](NULL, batch, a, should_steal);
+}
+
+int32_t
+action_autoval_init(struct odp_execute_action_impl *self)
+{
+ self->funcs[OVS_ACTION_ATTR_POP_VLAN] = action_autoval_generic;
+
+ return 0;
+}
@@ -68,6 +68,7 @@ struct odp_execute_action_impl {
/* Order of Actions implementations. */
enum odp_execute_action_impl_idx {
ACTION_IMPL_SCALAR,
+ ACTION_IMPL_AUTOVALIDATOR,
/* See ACTION_IMPL_BEGIN below, for "first to-be-validated" impl.
* Do not change the autovalidator position in this list without updating
* the define below.
@@ -78,6 +79,8 @@ enum odp_execute_action_impl_idx {
/* Index to start verifying implementations from. */
BUILD_ASSERT_DECL(ACTION_IMPL_SCALAR == 0);
+BUILD_ASSERT_DECL(ACTION_IMPL_AUTOVALIDATOR == 1);
+#define ACTION_IMPL_BEGIN (ACTION_IMPL_AUTOVALIDATOR + 1)
/* Odp execute init handles setting up the state of the actions functions at
* initialization time. It cannot return errors, as it must always succeed in