diff --git a/lib/automake.mk b/lib/automake.mk
index 46f869a33..16087031f 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -201,6 +201,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/nx-match.h \
 	lib/object-collection.c \
 	lib/object-collection.h \
+	lib/odp-execute-private.c \
+	lib/odp-execute-private.h \
 	lib/odp-execute.c \
 	lib/odp-execute.h \
 	lib/odp-util.c \
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 69d7ec26e..277e0d6c3 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1598,6 +1598,8 @@ create_dpif_netdev(struct dp_netdev *dp)
     dpif->dp = dp;
     dpif->last_port_seq = seq_read(dp->port_seq);
 
+    odp_execute_init();
+
     return &dpif->dpif;
 }
 
diff --git a/lib/odp-execute-private.c b/lib/odp-execute-private.c
new file mode 100644
index 000000000..6441c491c
--- /dev/null
+++ b/lib/odp-execute-private.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 Intel.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "dpdk.h"
+
+#include "openvswitch/vlog.h"
+#include "odp-execute-private.h"
+#include "odp-netlink.h"
+#include "dp-packet.h"
+#include "odp-util.h"
+
+
+int32_t action_autoval_init(struct odp_execute_action_impl *self);
+VLOG_DEFINE_THIS_MODULE(odp_execute_private);
+
+static struct odp_execute_action_impl action_impls[] = {
+    [ACTION_IMPL_SCALAR] = {
+        .available = 1,
+        .name = "scalar",
+        .probe = NULL,
+        .init_func = NULL,
+    },
+};
+
+static void
+action_impl_copy_funcs(struct odp_execute_action_impl *to,
+                       const struct odp_execute_action_impl *from)
+{
+    for (uint32_t i = 0; i < __OVS_KEY_ATTR_MAX; i++) {
+        atomic_uintptr_t *func = (void *) &to->funcs[i];
+        atomic_store_relaxed(func, (uintptr_t) from->funcs[i]);
+    }
+}
+
+void
+odp_execute_action_init(void)
+{
+    /* Call probe on each impl, and cache the result. */
+    for (int i = 0; i < ACTION_IMPL_MAX; i++) {
+        bool avail = true;
+        if (action_impls[i].probe) {
+            /* Return zero is success, non-zero means error. */
+            avail = (action_impls[i].probe() == 0);
+        }
+        VLOG_INFO("Action implementation %s (available: %s)\n",
+                  action_impls[i].name, avail ? "available" : "not available");
+        action_impls[i].available = avail;
+    }
+
+    uint32_t i;
+    for (i = 0; i < ACTION_IMPL_MAX; i++) {
+        /* Each impl's function array is initialized to reflect the scalar
+         * implementation. This simplifies adding optimized implementations,
+         * as the autovalidator can always compare all actions.
+         *
+         * Below copies the scalar functions to all other implementations.
+         */
+        if (i != ACTION_IMPL_SCALAR) {
+            action_impl_copy_funcs(&action_impls[i],
+                                   &action_impls[ACTION_IMPL_SCALAR]);
+        }
+
+        if (action_impls[i].init_func) {
+            action_impls[i].init_func(&action_impls[i]);
+        }
+    }
+}
diff --git a/lib/odp-execute-private.h b/lib/odp-execute-private.h
new file mode 100644
index 000000000..c2e86bbee
--- /dev/null
+++ b/lib/odp-execute-private.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2021 Intel.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ODP_EXTRACT_PRIVATE
+#define ODP_EXTRACT_PRIVATE 1
+
+#include "odp-execute.h"
+
+/* For __OVS_KEY_ATTR_MAX. */
+#include "odp-netlink.h"
+#include "dp-packet.h"
+#include "ovs-atomic.h"
+
+/* Forward declaration for typedef. */
+struct odp_execute_action_impl;
+
+/* Typedef for an initialization function that can initialize each
+ * implementation, checking requirements such as CPU ISA.
+ */
+typedef int32_t (*odp_execute_action_init_func)
+                    (struct odp_execute_action_impl *self);
+
+/* Probe function is used to detect if this CPU has the ISA required
+ * to run the optimized action implementation.
+ * returns one on successful probe.
+ * returns negative errno on failure.
+ */
+typedef int (*odp_execute_action_probe)(void);
+
+/* Structure represents an implementation of the odp actions. */
+struct odp_execute_action_impl {
+    /* When set, the CPU ISA required for this implementation is available
+     * and the implementation can be used.
+     */
+    bool available;
+
+    /* Name of the implementation. */
+    const char *name;
+
+    /* Probe function is used to detect if this CPU has the ISA required
+     * to run the optimized miniflow implementation. It is optional and
+     * if it is not used, then it must be null.
+     */
+    odp_execute_action_probe probe;
+
+    /* Called to check requirements and if usable, initializes the
+     * implementation for use.
+     */
+    odp_execute_action_init_func init_func;
+
+    /* An array of callback functions, one for each action. */
+    ATOMIC(odp_execute_cb) funcs[__OVS_KEY_ATTR_MAX];
+};
+
+/* Order of Actions implementations. */
+enum odp_execute_action_impl_idx {
+    ACTION_IMPL_SCALAR,
+    /* 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.
+     */
+
+    ACTION_IMPL_MAX,
+};
+
+/* Index to start verifying implementations from. */
+BUILD_ASSERT_DECL(ACTION_IMPL_SCALAR == 0);
+
+/* 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
+ * initializing the scalar/generic codepath.
+ */
+void odp_execute_action_init(void);
+
+/* Update the current active functions to those requested in name. */
+void odp_execute_action_get(struct ds *name);
+int32_t odp_execute_action_set(const char *name,
+                               struct odp_execute_action_impl *active);
+
+/* Init function for the scalar implementation. Calls into the odp-execute.c
+ * file, and initializes the function pointers for optimized action types.
+ */
+int32_t odp_action_scalar_init(struct odp_execute_action_impl *self);
+
+#endif /* ODP_EXTRACT_PRIVATE */
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 6eeda2a61..49dfa2a74 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -17,6 +17,7 @@
 
 #include <config.h>
 #include "odp-execute.h"
+#include "odp-execute-private.h"
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -830,6 +831,23 @@ requires_datapath_assistance(const struct nlattr *a)
     return false;
 }
 
+/* The active function pointers on the datapath. ISA optimized implementations
+ * are enabled by plugging them into this static arary, which is consulted when
+ * applying actions on the datapath.
+ */
+static struct odp_execute_action_impl actions_active_impl;
+
+void
+odp_execute_init(void)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    if (ovsthread_once_start(&once)) {
+        odp_execute_action_init();
+        ovsthread_once_done(&once);
+    }
+}
+
+
 /* Executes all of the 'actions_len' bytes of datapath actions in 'actions' on
  * the packets in 'batch'.  If 'steal' is true, possibly modifies and
  * definitely free the packets in 'batch', otherwise leaves 'batch' unchanged.
@@ -855,13 +873,12 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
         int type = nl_attr_type(a);
         bool last_action = (left <= NLA_ALIGN(a->nla_len));
+        /* Allow 'dp_execute_action' to steal the packet data if we do
+         * not need it any more. */
+        bool should_steal = steal && last_action;
 
         if (requires_datapath_assistance(a)) {
             if (dp_execute_action) {
-                /* Allow 'dp_execute_action' to steal the packet data if we do
-                 * not need it any more. */
-                bool should_steal = steal && last_action;
-
                 dp_execute_action(dp, batch, a, should_steal);
 
                 if (last_action || dp_packet_batch_is_empty(batch)) {
@@ -876,8 +893,20 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             continue;
         }
 
-        switch ((enum ovs_action_attr) type) {
+        /* If type is set in the active actions implementation, call the
+         * function-pointer and an continue to the next action.
+         */
+        enum ovs_action_attr attr_type = (enum ovs_action_attr) type;
+        if (actions_active_impl.funcs[attr_type]) {
+            actions_active_impl.funcs[attr_type](NULL, batch, a, should_steal);
+            continue;
+        }
+
+        /* If the action was not handled by the active function pointers above,
+         * process them by switching on the type below.
+         */
 
+        switch (attr_type) {
         case OVS_ACTION_ATTR_HASH: {
             const struct ovs_action_hash *hash_act = nl_attr_get(a);
 
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index a3578a575..c4f5303e7 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -28,6 +28,10 @@ struct dp_packet;
 struct pkt_metadata;
 struct dp_packet_batch;
 
+
+/* Called once at initialization time. */
+void odp_execute_init(void);
+
 typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
                                const struct nlattr *action, bool should_steal);
 
