/src/openvswitch/lib/dpif-netdev-private-dpif.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021 Intel Corporation. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | |
19 | | #include "dpif-netdev-private-dpif.h" |
20 | | #include "dpif-netdev-private-thread.h" |
21 | | |
22 | | #include <errno.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include "cpu.h" |
26 | | #include "openvswitch/dynamic-string.h" |
27 | | #include "openvswitch/vlog.h" |
28 | | #include "util.h" |
29 | | |
30 | | VLOG_DEFINE_THIS_MODULE(dpif_netdev_impl); |
31 | | #define DPIF_NETDEV_IMPL_AVX512_CHECK (__x86_64__ && HAVE_AVX512F \ |
32 | | && HAVE_LD_AVX512_GOOD && __SSE4_2__) |
33 | | |
34 | | enum dpif_netdev_impl_info_idx { |
35 | | DPIF_NETDEV_IMPL_SCALAR, |
36 | | DPIF_NETDEV_IMPL_AVX512 |
37 | | }; |
38 | | |
39 | | #if DPIF_NETDEV_IMPL_AVX512_CHECK |
40 | | static int32_t |
41 | | dp_netdev_input_outer_avx512_probe(void) |
42 | | { |
43 | | if (!cpu_has_isa(OVS_CPU_ISA_X86_AVX512F) |
44 | | || !cpu_has_isa(OVS_CPU_ISA_X86_BMI2)) { |
45 | | return -ENOTSUP; |
46 | | } |
47 | | |
48 | | return 0; |
49 | | } |
50 | | #endif |
51 | | |
52 | | /* Actual list of implementations goes here. */ |
53 | | static struct dpif_netdev_impl_info_t dpif_impls[] = { |
54 | | /* The default scalar C code implementation. */ |
55 | | [DPIF_NETDEV_IMPL_SCALAR] = { .input_func = dp_netdev_input, |
56 | | .probe = NULL, |
57 | | .name = "dpif_scalar", }, |
58 | | |
59 | | #if DPIF_NETDEV_IMPL_AVX512_CHECK |
60 | | /* Only available on x86_64 bit builds with SSE 4.2 used for OVS core. */ |
61 | | [DPIF_NETDEV_IMPL_AVX512] = { .input_func = dp_netdev_input_outer_avx512, |
62 | | .probe = dp_netdev_input_outer_avx512_probe, |
63 | | .name = "dpif_avx512", }, |
64 | | #endif |
65 | | }; |
66 | | |
67 | | static dp_netdev_input_func default_dpif_func; |
68 | | |
69 | | dp_netdev_input_func |
70 | | dp_netdev_impl_get_default(void) |
71 | 0 | { |
72 | | /* For the first call, this will be NULL. Compute the compile time default. |
73 | | */ |
74 | 0 | if (!default_dpif_func) { |
75 | 0 | int dpif_idx = DPIF_NETDEV_IMPL_SCALAR; |
76 | | |
77 | | /* Configure-time overriding to run test suite on all implementations. */ |
78 | | #if DPIF_NETDEV_IMPL_AVX512_CHECK |
79 | | #ifdef DPIF_AVX512_DEFAULT |
80 | | dp_netdev_input_func_probe probe; |
81 | | |
82 | | /* Check if the compiled default is compatible. */ |
83 | | probe = dpif_impls[DPIF_NETDEV_IMPL_AVX512].probe; |
84 | | if (!probe || !probe()) { |
85 | | dpif_idx = DPIF_NETDEV_IMPL_AVX512; |
86 | | } |
87 | | #endif |
88 | | #endif |
89 | |
|
90 | 0 | VLOG_INFO("Default DPIF implementation is %s.\n", |
91 | 0 | dpif_impls[dpif_idx].name); |
92 | 0 | default_dpif_func = dpif_impls[dpif_idx].input_func; |
93 | 0 | } |
94 | |
|
95 | 0 | return default_dpif_func; |
96 | 0 | } |
97 | | |
98 | | void |
99 | | dp_netdev_impl_get(struct ds *reply, struct dp_netdev_pmd_thread **pmd_list, |
100 | | size_t n) |
101 | 0 | { |
102 | | /* Add all dpif functions to reply string. */ |
103 | 0 | ds_put_cstr(reply, "Available DPIF implementations:\n"); |
104 | |
|
105 | 0 | for (uint32_t i = 0; i < ARRAY_SIZE(dpif_impls); i++) { |
106 | 0 | ds_put_format(reply, " %s (pmds: ", dpif_impls[i].name); |
107 | |
|
108 | 0 | for (size_t j = 0; j < n; j++) { |
109 | 0 | struct dp_netdev_pmd_thread *pmd = pmd_list[j]; |
110 | 0 | if (pmd->core_id == NON_PMD_CORE_ID) { |
111 | 0 | continue; |
112 | 0 | } |
113 | | |
114 | 0 | if (pmd->netdev_input_func == dpif_impls[i].input_func) { |
115 | 0 | ds_put_format(reply, "%u,", pmd->core_id); |
116 | 0 | } |
117 | 0 | } |
118 | |
|
119 | 0 | ds_chomp(reply, ','); |
120 | |
|
121 | 0 | if (ds_last(reply) == ' ') { |
122 | 0 | ds_put_cstr(reply, "none"); |
123 | 0 | } |
124 | |
|
125 | 0 | ds_put_cstr(reply, ")\n"); |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | /* This function checks all available DPIF implementations, and selects the |
130 | | * returns the function pointer to the one requested by "name". |
131 | | */ |
132 | | static int32_t |
133 | | dp_netdev_impl_get_by_name(const char *name, dp_netdev_input_func *out_func) |
134 | 0 | { |
135 | 0 | ovs_assert(name); |
136 | 0 | ovs_assert(out_func); |
137 | |
|
138 | 0 | uint32_t i; |
139 | |
|
140 | 0 | for (i = 0; i < ARRAY_SIZE(dpif_impls); i++) { |
141 | 0 | if (strcmp(dpif_impls[i].name, name) == 0) { |
142 | | /* Probe function is optional - so check it is set before exec. */ |
143 | 0 | if (dpif_impls[i].probe) { |
144 | 0 | int probe_err = dpif_impls[i].probe(); |
145 | 0 | if (probe_err) { |
146 | 0 | *out_func = NULL; |
147 | 0 | return probe_err; |
148 | 0 | } |
149 | 0 | } |
150 | 0 | *out_func = dpif_impls[i].input_func; |
151 | 0 | return 0; |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | 0 | return -EINVAL; |
156 | 0 | } |
157 | | |
158 | | int32_t |
159 | | dp_netdev_impl_set_default_by_name(const char *name) |
160 | 0 | { |
161 | 0 | dp_netdev_input_func new_default; |
162 | |
|
163 | 0 | int32_t err = dp_netdev_impl_get_by_name(name, &new_default); |
164 | |
|
165 | 0 | if (!err) { |
166 | 0 | default_dpif_func = new_default; |
167 | 0 | } |
168 | |
|
169 | 0 | return err; |
170 | |
|
171 | 0 | } |