Multiversioning: Device (tx) function constructor

Change-Id: I39f87ca161c891fb22462a23188982fef7c3243f
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
diff --git a/src/plugins/avf/avf.h b/src/plugins/avf/avf.h
index 3ee8015..1dc5d96 100644
--- a/src/plugins/avf/avf.h
+++ b/src/plugins/avf/avf.h
@@ -216,8 +216,6 @@
 
 extern vlib_node_registration_t avf_input_node;
 extern vnet_device_class_t avf_device_class;
-uword avf_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
-			vlib_frame_t * frame);
 
 /* format.c */
 format_function_t format_avf_device;
diff --git a/src/plugins/avf/device.c b/src/plugins/avf/device.c
index a6af902..94b5ca4 100644
--- a/src/plugins/avf/device.c
+++ b/src/plugins/avf/device.c
@@ -1299,7 +1299,6 @@
 VNET_DEVICE_CLASS (avf_device_class,) =
 {
   .name = "Adaptive Virtual Function (AVF) interface",
-  .tx_function = avf_interface_tx,
   .format_device = format_avf_device,
   .format_device_name = format_avf_device_name,
   .admin_up_down_function = avf_interface_admin_up_down,
diff --git a/src/plugins/avf/output.c b/src/plugins/avf/output.c
index 68420a7..2001c11 100644
--- a/src/plugins/avf/output.c
+++ b/src/plugins/avf/output.c
@@ -34,10 +34,9 @@
   return d->qword[1] & 0x0f;
 }
 
-uword
-CLIB_MULTIARCH_FN (avf_interface_tx) (vlib_main_t * vm,
-				      vlib_node_runtime_t * node,
-				      vlib_frame_t * frame)
+VNET_DEVICE_CLASS_TX_FN (avf_device_class) (vlib_main_t * vm,
+					    vlib_node_runtime_t * node,
+					    vlib_frame_t * frame)
 {
   avf_main_t *am = &avf_main;
   vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
@@ -159,21 +158,6 @@
   return frame->n_vectors - n_left;
 }
 
-#ifndef CLIB_MARCH_VARIANT
-#if __x86_64__
-vlib_node_function_t __clib_weak avf_interface_tx_avx512;
-vlib_node_function_t __clib_weak avf_interface_tx_avx2;
-static void __clib_constructor
-avf_interface_tx_multiarch_select (void)
-{
-  if (avf_interface_tx_avx512 && clib_cpu_supports_avx512f ())
-    avf_device_class.tx_function = avf_interface_tx_avx512;
-  else if (avf_interface_tx_avx2 && clib_cpu_supports_avx2 ())
-    avf_device_class.tx_function = avf_interface_tx_avx2;
-}
-#endif
-#endif
-
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/plugins/dpdk/device/device.c b/src/plugins/dpdk/device/device.c
index d5ab258..8776e09 100644
--- a/src/plugins/dpdk/device/device.c
+++ b/src/plugins/dpdk/device/device.c
@@ -327,10 +327,9 @@
  * node. It first copies packets on the frame to a per-thread arrays
  * containing the rte_mbuf pointers.
  */
-uword
-CLIB_MULTIARCH_FN (dpdk_interface_tx) (vlib_main_t * vm,
-				       vlib_node_runtime_t * node,
-				       vlib_frame_t * f)
+VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
+					     vlib_node_runtime_t * node,
+					     vlib_frame_t * f)
 {
   dpdk_main_t *dm = &dpdk_main;
   vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
@@ -663,7 +662,6 @@
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS (dpdk_device_class) = {
   .name = "dpdk",
-  .tx_function = dpdk_interface_tx,
   .tx_function_n_errors = DPDK_TX_FUNC_N_ERROR,
   .tx_function_error_strings = dpdk_tx_func_error_strings,
   .format_device_name = format_dpdk_device_name,
@@ -679,18 +677,6 @@
 };
 /* *INDENT-ON* */
 
-#if __x86_64__
-vlib_node_function_t __clib_weak dpdk_interface_tx_avx512;
-vlib_node_function_t __clib_weak dpdk_interface_tx_avx2;
-static void __clib_constructor
-dpdk_interface_tx_multiarch_select (void)
-{
-  if (dpdk_interface_tx_avx512 && clib_cpu_supports_avx512f ())
-    dpdk_device_class.tx_function = dpdk_interface_tx_avx512;
-  else if (dpdk_interface_tx_avx2 && clib_cpu_supports_avx2 ())
-    dpdk_device_class.tx_function = dpdk_interface_tx_avx2;
-}
-#endif
 #endif
 
 #define UP_DOWN_FLAG_EVENT 1
diff --git a/src/plugins/memif/device.c b/src/plugins/memif/device.c
index 5c8eb3f..f86d692 100644
--- a/src/plugins/memif/device.c
+++ b/src/plugins/memif/device.c
@@ -400,10 +400,9 @@
   return frame->n_vectors;
 }
 
-uword
-CLIB_MULTIARCH_FN (memif_interface_tx) (vlib_main_t * vm,
-					vlib_node_runtime_t * node,
-					vlib_frame_t * frame)
+VNET_DEVICE_CLASS_TX_FN (memif_device_class) (vlib_main_t * vm,
+					      vlib_node_runtime_t * node,
+					      vlib_frame_t * frame)
 {
   memif_main_t *nm = &memif_main;
   vnet_interface_output_runtime_t *rund = (void *) node->runtime_data;
@@ -504,7 +503,6 @@
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS (memif_device_class) = {
   .name = "memif",
-  .tx_function = memif_interface_tx,
   .format_device_name = format_memif_device_name,
   .format_device = format_memif_device,
   .format_tx_trace = format_memif_tx_trace,
@@ -517,18 +515,6 @@
   .rx_mode_change_function = memif_interface_rx_mode_change,
 };
 
-#if __x86_64__
-vlib_node_function_t __clib_weak memif_interface_tx_avx512;
-vlib_node_function_t __clib_weak memif_interface_tx_avx2;
-static void __clib_constructor
-memif_interface_tx_multiarch_select (void)
-{
-  if (memif_interface_tx_avx512 && clib_cpu_supports_avx512f ())
-    memif_device_class.tx_function = memif_interface_tx_avx512;
-  else if (memif_interface_tx_avx2 && clib_cpu_supports_avx2 ())
-    memif_device_class.tx_function = memif_interface_tx_avx2;
-}
-#endif
 #endif
 
 /* *INDENT-ON* */
diff --git a/src/vnet/devices/virtio/vhost_user_output.c b/src/vnet/devices/virtio/vhost_user_output.c
index b415837..9c65f24 100644
--- a/src/vnet/devices/virtio/vhost_user_output.c
+++ b/src/vnet/devices/virtio/vhost_user_output.c
@@ -223,11 +223,9 @@
   return 0;
 }
 
-
-uword
-CLIB_MULTIARCH_FN (vhost_user_tx) (vlib_main_t * vm,
-				   vlib_node_runtime_t * node,
-				   vlib_frame_t * frame)
+VNET_DEVICE_CLASS_TX_FN (vhost_user_device_class) (vlib_main_t * vm,
+						   vlib_node_runtime_t *
+						   node, vlib_frame_t * frame)
 {
   u32 *buffers = vlib_frame_args (frame);
   u32 n_left = frame->n_vectors;
@@ -636,7 +634,6 @@
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS (vhost_user_device_class) = {
   .name = "vhost-user",
-  .tx_function = vhost_user_tx,
   .tx_function_n_errors = VHOST_USER_TX_FUNC_N_ERROR,
   .tx_function_error_strings = vhost_user_tx_func_error_strings,
   .format_device_name = format_vhost_user_interface_name,
@@ -646,18 +643,6 @@
   .format_tx_trace = format_vhost_trace,
 };
 
-#if __x86_64__
-vlib_node_function_t __clib_weak vhost_user_tx_avx512;
-vlib_node_function_t __clib_weak vhost_user_tx_avx2;
-static void __clib_constructor
-vhost_user_tx_multiarch_select (void)
-{
-  if (vhost_user_tx_avx512 && clib_cpu_supports_avx512f ())
-    vhost_user_device_class.tx_function = vhost_user_tx_avx512;
-  else if (vhost_user_tx_avx2 && clib_cpu_supports_avx2 ())
-    vhost_user_device_class.tx_function = vhost_user_tx_avx2;
-}
-#endif
 #endif
 
 /* *INDENT-ON* */
diff --git a/src/vnet/interface.c b/src/vnet/interface.c
index ca4dc49..1e69ccb 100644
--- a/src/vnet/interface.c
+++ b/src/vnet/interface.c
@@ -1359,6 +1359,27 @@
       {
 	c->index = vec_len (im->device_classes);
 	hash_set_mem (im->device_class_by_name, c->name, c->index);
+
+	if (c->tx_fn_registrations)
+	  {
+	    vlib_node_fn_registration_t *fnr = c->tx_fn_registrations;
+	    int priority = -1;
+
+	    /* to avoid confusion, please remove ".tx_function" statiement
+	       from VNET_DEVICE_CLASS() if using function candidates */
+	    ASSERT (c->tx_function == 0);
+
+	    while (fnr)
+	      {
+		if (fnr->priority > priority)
+		  {
+		    priority = fnr->priority;
+		    c->tx_function = fnr->function;
+		  }
+		fnr = fnr->next_registration;
+	      }
+	  }
+
 	vec_add1 (im->device_classes, c[0]);
 	c = c->next_class_registration;
       }
diff --git a/src/vnet/interface.h b/src/vnet/interface.h
index d869b0c..e9adfe8 100644
--- a/src/vnet/interface.h
+++ b/src/vnet/interface.h
@@ -195,6 +195,9 @@
   /* Transmit function. */
   vlib_node_function_t *tx_function;
 
+  /* Transmit function candidate registration with priority */
+  vlib_node_fn_registration_t *tx_fn_registrations;
+
   /* Error strings indexed by error code for this node. */
   char **tx_function_error_strings;
 
@@ -265,6 +268,24 @@
 }                                                                       \
 __VA_ARGS__ vnet_device_class_t x
 
+#define VNET_DEVICE_CLASS_TX_FN(devclass)				\
+uword CLIB_MARCH_SFX (devclass##_tx_fn)();				\
+static vlib_node_fn_registration_t					\
+  CLIB_MARCH_SFX(devclass##_tx_fn_registration) =			\
+  { .function = &CLIB_MARCH_SFX (devclass##_tx_fn), };			\
+									\
+static void __clib_constructor						\
+CLIB_MARCH_SFX (devclass##_tx_fn_multiarch_register) (void)		\
+{									\
+  extern vnet_device_class_t devclass;					\
+  vlib_node_fn_registration_t *r;					\
+  r = &CLIB_MARCH_SFX (devclass##_tx_fn_registration);			\
+  r->priority = CLIB_MARCH_FN_PRIORITY();				\
+  r->next_registration = devclass.tx_fn_registrations;			\
+  devclass.tx_fn_registrations = r;					\
+}									\
+uword CLIB_CPU_OPTIMIZED CLIB_MARCH_SFX (devclass##_tx_fn)
+
 #define VLIB_DEVICE_TX_FUNCTION_CLONE_TEMPLATE(arch, fn, tgt)		\
   uword									\
   __attribute__ ((flatten))						\