/src/openvswitch/lib/dpif-offload.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2025 Red Hat, Inc. |
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 | | #include <errno.h> |
19 | | |
20 | | #include "dpif-offload.h" |
21 | | #include "dpif-offload-provider.h" |
22 | | #include "dpif-provider.h" |
23 | | #include "netdev-provider.h" |
24 | | #include "unixctl.h" |
25 | | #include "util.h" |
26 | | #include "vswitch-idl.h" |
27 | | |
28 | | #include "openvswitch/dynamic-string.h" |
29 | | #include "openvswitch/shash.h" |
30 | | #include "openvswitch/vlog.h" |
31 | | |
32 | | VLOG_DEFINE_THIS_MODULE(dpif_offload); |
33 | | |
34 | | static struct vlog_rate_limit rl_dbg = VLOG_RATE_LIMIT_INIT(100, 100); |
35 | | |
36 | | static struct ovs_mutex dpif_offload_mutex = OVS_MUTEX_INITIALIZER; |
37 | | static struct shash dpif_offload_classes \ |
38 | | OVS_GUARDED_BY(dpif_offload_mutex) = \ |
39 | | SHASH_INITIALIZER(&dpif_offload_classes); |
40 | | static struct shash dpif_offload_providers \ |
41 | | OVS_GUARDED_BY(dpif_offload_mutex) = \ |
42 | | SHASH_INITIALIZER(&dpif_offload_providers); |
43 | | |
44 | | static const struct dpif_offload_class *base_dpif_offload_classes[] = { |
45 | | #if defined(__linux__) |
46 | | &dpif_offload_tc_class, |
47 | | #endif |
48 | | #ifdef DPDK_NETDEV |
49 | | &dpif_offload_dpdk_class, |
50 | | #endif |
51 | | /* While adding a new offload class to this structure make sure to also |
52 | | * update the dpif_offload_provider_priority_list below. */ |
53 | | &dpif_offload_dummy_class, |
54 | | &dpif_offload_dummy_x_class, |
55 | | }; |
56 | | |
57 | 0 | #define TOTAL_PROVIDERS ARRAY_SIZE(base_dpif_offload_classes) |
58 | 0 | #define DEFAULT_PROVIDER_PRIORITY_LIST "tc,dpdk,dummy,dummy_x" |
59 | | |
60 | | static char *dpif_offload_provider_priority_list = NULL; |
61 | | static atomic_bool offload_global_enabled = false; |
62 | | static atomic_bool offload_rebalance_policy = false; |
63 | | static struct smap iface_order_cfg = SMAP_INITIALIZER(&iface_order_cfg); |
64 | | |
65 | | static int |
66 | | dpif_offload_register_provider__(const struct dpif_offload_class *class) |
67 | | OVS_REQUIRES(dpif_offload_mutex) |
68 | 0 | { |
69 | 0 | int error; |
70 | |
|
71 | 0 | if (shash_find(&dpif_offload_classes, class->type)) { |
72 | 0 | VLOG_WARN("attempted to register duplicate dpif offload class: %s", |
73 | 0 | class->type); |
74 | 0 | return EEXIST; |
75 | 0 | } |
76 | | |
77 | 0 | if (!class->supported_dpif_types) { |
78 | 0 | VLOG_WARN("attempted to register a dpif offload class without any " |
79 | 0 | "supported dpif types: %s", class->type); |
80 | 0 | return EINVAL; |
81 | 0 | } |
82 | | |
83 | 0 | error = class->init ? class->init() : 0; |
84 | 0 | if (error) { |
85 | 0 | VLOG_WARN("failed to initialize %s dpif offload class: %s", |
86 | 0 | class->type, ovs_strerror(error)); |
87 | 0 | return error; |
88 | 0 | } |
89 | | |
90 | 0 | shash_add(&dpif_offload_classes, class->type, class); |
91 | 0 | return 0; |
92 | 0 | } |
93 | | |
94 | | static int |
95 | | dpif_offload_register_provider(const struct dpif_offload_class *class) |
96 | 0 | { |
97 | 0 | int error; |
98 | |
|
99 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
100 | 0 | error = dpif_offload_register_provider__(class); |
101 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
102 | |
|
103 | 0 | return error; |
104 | 0 | } |
105 | | |
106 | | static void |
107 | | dpif_offload_show_classes(struct unixctl_conn *conn, int argc OVS_UNUSED, |
108 | | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) |
109 | 0 | { |
110 | 0 | const struct shash_node **list; |
111 | 0 | struct ds ds; |
112 | |
|
113 | 0 | ds_init(&ds); |
114 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
115 | |
|
116 | 0 | list = shash_sort(&dpif_offload_classes); |
117 | 0 | for (size_t i = 0; i < shash_count(&dpif_offload_classes); i++) { |
118 | 0 | const struct dpif_offload_class *class = list[i]->data; |
119 | |
|
120 | 0 | if (i == 0) { |
121 | 0 | ds_put_cstr(&ds, "Offload Class Supported dpif class(es)\n"); |
122 | 0 | ds_put_cstr(&ds, "---------------- ------------------------\n"); |
123 | 0 | } |
124 | |
|
125 | 0 | ds_put_format(&ds, "%-16s ", list[i]->name); |
126 | |
|
127 | 0 | for (size_t j = 0; class->supported_dpif_types[j] != NULL; j++) { |
128 | 0 | ds_put_format(&ds, "%*s%s\n", j == 0 ? 0 : 18, "", |
129 | 0 | class->supported_dpif_types[j]); |
130 | 0 | } |
131 | 0 | } |
132 | |
|
133 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
134 | 0 | free(list); |
135 | |
|
136 | 0 | unixctl_command_reply(conn, ds_cstr(&ds)); |
137 | 0 | ds_destroy(&ds); |
138 | 0 | } |
139 | | |
140 | | void |
141 | | dpif_offload_module_init(void) |
142 | 0 | { |
143 | 0 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
144 | |
|
145 | 0 | if (!ovsthread_once_start(&once)) { |
146 | 0 | return; |
147 | 0 | } |
148 | | |
149 | 0 | if (!dpif_offload_provider_priority_list) { |
150 | 0 | dpif_offload_provider_priority_list = |
151 | 0 | xstrdup(DEFAULT_PROVIDER_PRIORITY_LIST); |
152 | 0 | } |
153 | |
|
154 | 0 | unixctl_command_register("dpif/offload/classes", NULL, 0, 0, |
155 | 0 | dpif_offload_show_classes, NULL); |
156 | |
|
157 | 0 | for (int i = 0; i < ARRAY_SIZE(base_dpif_offload_classes); i++) { |
158 | 0 | ovs_assert(base_dpif_offload_classes[i]->open |
159 | 0 | && base_dpif_offload_classes[i]->close |
160 | 0 | && base_dpif_offload_classes[i]->can_offload |
161 | 0 | && base_dpif_offload_classes[i]->port_add |
162 | 0 | && base_dpif_offload_classes[i]->port_del |
163 | 0 | && base_dpif_offload_classes[i]->get_netdev); |
164 | |
|
165 | 0 | ovs_assert((base_dpif_offload_classes[i]->flow_dump_create && |
166 | 0 | base_dpif_offload_classes[i]->flow_dump_next && |
167 | 0 | base_dpif_offload_classes[i]->flow_dump_destroy && |
168 | 0 | base_dpif_offload_classes[i]->flow_dump_thread_create && |
169 | 0 | base_dpif_offload_classes[i]->flow_dump_thread_destroy) || |
170 | 0 | (!base_dpif_offload_classes[i]->flow_dump_create && |
171 | 0 | !base_dpif_offload_classes[i]->flow_dump_next && |
172 | 0 | !base_dpif_offload_classes[i]->flow_dump_destroy && |
173 | 0 | !base_dpif_offload_classes[i]->flow_dump_thread_create && |
174 | 0 | !base_dpif_offload_classes[i]->flow_dump_thread_destroy)); |
175 | |
|
176 | 0 | dpif_offload_register_provider(base_dpif_offload_classes[i]); |
177 | 0 | } |
178 | |
|
179 | 0 | ovsthread_once_done(&once); |
180 | 0 | } |
181 | | |
182 | | static struct dpif_offload_provider_collection* |
183 | | dpif_get_offload_provider_collection(const struct dpif *dpif) |
184 | 0 | { |
185 | 0 | return ovsrcu_get(struct dpif_offload_provider_collection *, |
186 | 0 | &dpif->offload_provider_collection); |
187 | 0 | } |
188 | | |
189 | | static struct dpif_offload_provider_collection* |
190 | | dpif_get_offload_provider_collection_with_ref(const struct dpif *dpif) |
191 | 0 | { |
192 | 0 | struct dpif_offload_provider_collection *collection; |
193 | |
|
194 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
195 | |
|
196 | 0 | collection = ovsrcu_get(struct dpif_offload_provider_collection *, |
197 | 0 | &dpif->offload_provider_collection); |
198 | |
|
199 | 0 | if (!collection || !ovs_refcount_try_ref_rcu(&collection->ref_cnt)) { |
200 | 0 | collection = NULL; |
201 | 0 | } |
202 | |
|
203 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
204 | |
|
205 | 0 | return collection; |
206 | 0 | } |
207 | | |
208 | | static void |
209 | | dpif_attach_offload_provider_collection( |
210 | | struct dpif *dpif, struct dpif_offload_provider_collection *collection) |
211 | | OVS_REQUIRES(dpif_offload_mutex) |
212 | 0 | { |
213 | | /* When called, 'collection' should still have a refcount > 0, which is |
214 | | * guaranteed by holding the lock from the shash lookup up to this point. |
215 | | * If, for any reason, the refcount is not > 0, ovs_refcount_ref() will |
216 | | * assert. */ |
217 | 0 | ovs_refcount_ref(&collection->ref_cnt); |
218 | 0 | ovsrcu_set(&dpif->offload_provider_collection, collection); |
219 | 0 | } |
220 | | |
221 | | static int |
222 | | provider_collection_add(struct dpif_offload_provider_collection *collection, |
223 | | struct dpif_offload *offload) |
224 | 0 | { |
225 | 0 | struct ovs_list *providers_list = &collection->list; |
226 | 0 | struct dpif_offload *offload_entry; |
227 | |
|
228 | 0 | ovs_assert(collection); |
229 | |
|
230 | 0 | LIST_FOR_EACH (offload_entry, dpif_list_node, providers_list) { |
231 | 0 | if (offload_entry == offload || !strcmp(offload->name, |
232 | 0 | offload_entry->name)) { |
233 | 0 | return EEXIST; |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | 0 | ovs_list_push_back(providers_list, &offload->dpif_list_node); |
238 | 0 | return 0; |
239 | 0 | } |
240 | | |
241 | | static void |
242 | | move_provider_to_collection( |
243 | | struct dpif_offload_provider_collection *collection, |
244 | | struct dpif_offload *offload) |
245 | | OVS_REQUIRES(dpif_offload_mutex) |
246 | 0 | { |
247 | 0 | int error; |
248 | |
|
249 | 0 | ovs_list_remove(&offload->dpif_list_node); |
250 | 0 | error = provider_collection_add(collection, offload); |
251 | 0 | if (error) { |
252 | 0 | VLOG_WARN("failed to add dpif offload provider %s to %s: %s", |
253 | 0 | dpif_offload_type(offload), collection->dpif_name, |
254 | 0 | ovs_strerror(error)); |
255 | |
|
256 | 0 | offload->class->close(offload); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | static int |
261 | | dpif_attach_new_offload_provider_collection(struct dpif *dpif) |
262 | | OVS_REQUIRES(dpif_offload_mutex) |
263 | 0 | { |
264 | 0 | struct ovs_list provider_list = OVS_LIST_INITIALIZER(&provider_list); |
265 | 0 | const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif)); |
266 | 0 | struct dpif_offload_provider_collection *collection; |
267 | 0 | struct dpif_offload *offload; |
268 | 0 | struct shash_node *node; |
269 | 0 | char *tokens, *saveptr; |
270 | | |
271 | | /* Allocate and attach collection to dpif. */ |
272 | 0 | collection = xmalloc(sizeof *collection); |
273 | 0 | collection->dpif_name = xstrdup(dpif_name(dpif)); |
274 | 0 | ovs_mutex_init_recursive(&collection->mutex); |
275 | 0 | ovs_refcount_init(&collection->ref_cnt); |
276 | 0 | ovs_list_init(&collection->list); |
277 | 0 | shash_add(&dpif_offload_providers, collection->dpif_name, collection); |
278 | | |
279 | | /* Open all the providers supporting this dpif type. */ |
280 | 0 | SHASH_FOR_EACH (node, &dpif_offload_classes) { |
281 | 0 | const struct dpif_offload_class *class = node->data; |
282 | |
|
283 | 0 | for (size_t i = 0; class->supported_dpif_types[i] != NULL; i++) { |
284 | 0 | if (!strcmp(class->supported_dpif_types[i], dpif_type_str)) { |
285 | 0 | int error = class->open(class, dpif, &offload); |
286 | |
|
287 | 0 | if (error) { |
288 | 0 | VLOG_WARN("failed to initialize dpif offload provider " |
289 | 0 | "%s for %s: %s", |
290 | 0 | class->type, dpif_name(dpif), |
291 | 0 | ovs_strerror(error)); |
292 | 0 | } else { |
293 | 0 | ovs_list_push_back(&provider_list, |
294 | 0 | &offload->dpif_list_node); |
295 | 0 | } |
296 | 0 | break; |
297 | 0 | } |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | /* Attach all the providers based on the priority list. */ |
302 | 0 | tokens = xstrdup(dpif_offload_provider_priority_list); |
303 | |
|
304 | 0 | for (char *name = strtok_r(tokens, ",", &saveptr); |
305 | 0 | name; |
306 | 0 | name = strtok_r(NULL, ",", &saveptr)) { |
307 | |
|
308 | 0 | LIST_FOR_EACH_SAFE (offload, dpif_list_node, &provider_list) { |
309 | 0 | if (strcmp(name, offload->class->type)) { |
310 | 0 | continue; |
311 | 0 | } |
312 | | |
313 | 0 | move_provider_to_collection(collection, offload); |
314 | 0 | break; |
315 | 0 | } |
316 | 0 | } |
317 | 0 | free(tokens); |
318 | | |
319 | | /* Add remaining entries in order. */ |
320 | 0 | LIST_FOR_EACH_SAFE (offload, dpif_list_node, &provider_list) { |
321 | 0 | move_provider_to_collection(collection, offload); |
322 | 0 | } |
323 | | |
324 | | /* Attach the new collection to the dpif. */ |
325 | 0 | ovsrcu_set(&dpif->offload_provider_collection, collection); |
326 | |
|
327 | 0 | return 0; |
328 | 0 | } |
329 | | |
330 | | /* This function returns 0 if a new provider set was attached to the dpif, |
331 | | * returns EEXIST if an existing set of providers was attached, and |
332 | | * returns a negative error code on error. */ |
333 | | int |
334 | | dpif_attach_offload_providers(struct dpif *dpif) |
335 | 0 | { |
336 | 0 | struct dpif_offload_provider_collection *collection; |
337 | 0 | int rc = EEXIST; |
338 | |
|
339 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
340 | |
|
341 | 0 | collection = shash_find_data(&dpif_offload_providers, dpif_name(dpif)); |
342 | 0 | if (collection) { |
343 | 0 | dpif_attach_offload_provider_collection(dpif, collection); |
344 | 0 | } else { |
345 | 0 | rc = dpif_attach_new_offload_provider_collection(dpif); |
346 | 0 | } |
347 | |
|
348 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
349 | 0 | return rc; |
350 | 0 | } |
351 | | |
352 | | static void |
353 | | provider_collection_free_rcu( |
354 | | struct dpif_offload_provider_collection *collection) |
355 | 0 | { |
356 | 0 | struct dpif_offload *offload_entry; |
357 | | |
358 | | /* We need to use the safe variant here as we removed the entry, and the |
359 | | * close API will free() it. */ |
360 | 0 | LIST_FOR_EACH_SAFE (offload_entry, dpif_list_node, &collection->list) { |
361 | 0 | ovs_list_remove(&offload_entry->dpif_list_node); |
362 | 0 | offload_entry->class->close(offload_entry); |
363 | 0 | } |
364 | | |
365 | | /* Free remaining resources. */ |
366 | 0 | ovs_mutex_destroy(&collection->mutex); |
367 | 0 | free(collection->dpif_name); |
368 | 0 | free(collection); |
369 | 0 | } |
370 | | |
371 | | static void |
372 | | dpif_offload_unref_collection( |
373 | | struct dpif_offload_provider_collection *collection) |
374 | 0 | { |
375 | 0 | if (!collection) { |
376 | 0 | return; |
377 | 0 | } |
378 | | |
379 | | /* Take dpif_offload_mutex so that, if collection->ref_cnt falls to |
380 | | * zero, we can't get a new reference to 'collection' through the |
381 | | * 'dpif_offload_providers' shash. */ |
382 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
383 | 0 | if (ovs_refcount_unref_relaxed(&collection->ref_cnt) == 1) { |
384 | 0 | shash_find_and_delete(&dpif_offload_providers, |
385 | 0 | collection->dpif_name); |
386 | 0 | ovsrcu_postpone(provider_collection_free_rcu, collection); |
387 | 0 | } |
388 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
389 | 0 | } |
390 | | |
391 | | void |
392 | | dpif_detach_offload_providers(struct dpif *dpif) |
393 | 0 | { |
394 | 0 | struct dpif_offload_provider_collection *collection; |
395 | |
|
396 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
397 | 0 | if (collection) { |
398 | 0 | dpif_offload_unref_collection(collection); |
399 | 0 | ovsrcu_set(&dpif->offload_provider_collection, NULL); |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | void |
404 | | dpif_offload_set_config(struct dpif *dpif, const struct smap *other_cfg) |
405 | 0 | { |
406 | 0 | struct dpif_offload_provider_collection *collection; |
407 | 0 | struct dpif_offload *offload; |
408 | |
|
409 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
410 | |
|
411 | 0 | if (!collection) { |
412 | 0 | return; |
413 | 0 | } |
414 | | |
415 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
416 | 0 | if (offload->class->set_config) { |
417 | 0 | offload->class->set_config(offload, other_cfg); |
418 | 0 | } |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | |
423 | | void |
424 | | dpif_offload_init(struct dpif_offload *offload, |
425 | | const struct dpif_offload_class *class, |
426 | | struct dpif *dpif) |
427 | 0 | { |
428 | 0 | ovs_assert(offload && class && dpif); |
429 | |
|
430 | 0 | offload->class = class; |
431 | 0 | offload->name = xasprintf("%s[%s]", class->type, dpif_name(dpif)); |
432 | 0 | offload->ports = dpif_offload_port_mgr_init(); |
433 | 0 | } |
434 | | |
435 | | void |
436 | | dpif_offload_destroy(struct dpif_offload *offload) |
437 | 0 | { |
438 | 0 | ovs_assert(offload); |
439 | |
|
440 | 0 | dpif_offload_port_mgr_destroy(offload); |
441 | 0 | free(offload->name); |
442 | 0 | } |
443 | | |
444 | | const char * |
445 | | dpif_offload_name(const struct dpif_offload *offload) |
446 | 0 | { |
447 | 0 | return offload->name; |
448 | 0 | } |
449 | | |
450 | | const char * |
451 | | dpif_offload_type(const struct dpif_offload *offload) |
452 | 0 | { |
453 | 0 | return offload->class->type; |
454 | 0 | } |
455 | | |
456 | | bool |
457 | | dpif_offload_get_debug(const struct dpif_offload *offload, struct ds *ds, |
458 | | struct json *json) |
459 | 0 | { |
460 | 0 | if (!offload->class->get_debug) { |
461 | 0 | return false; |
462 | 0 | } |
463 | | |
464 | 0 | offload->class->get_debug(offload, ds, json); |
465 | 0 | return true; |
466 | 0 | } |
467 | | |
468 | | int |
469 | | dpif_offload_stats_get(struct dpif *dpif, struct netdev_custom_stats **stats_, |
470 | | size_t *n_stats) |
471 | 0 | { |
472 | 0 | struct dpif_offload_provider_collection *collection; |
473 | 0 | struct netdev_custom_stats *stats; |
474 | 0 | struct dpif_offload *offload; |
475 | 0 | size_t n_providers; |
476 | 0 | int error = 0; |
477 | |
|
478 | 0 | *n_stats = 0; |
479 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
480 | 0 | if (!collection || !dpif_offload_enabled()) { |
481 | 0 | *stats_ = NULL; |
482 | 0 | return 0; |
483 | 0 | } |
484 | | |
485 | 0 | n_providers = ovs_list_size(&collection->list); |
486 | 0 | stats = xcalloc(n_providers, sizeof(struct netdev_custom_stats)); |
487 | |
|
488 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
489 | 0 | if (!offload->class->get_global_stats) { |
490 | 0 | continue; |
491 | 0 | } |
492 | | |
493 | 0 | error = offload->class->get_global_stats(offload, &stats[(*n_stats)]); |
494 | 0 | if (error) { |
495 | 0 | for (int i = 0; i < *n_stats; i++) { |
496 | 0 | netdev_free_custom_stats_counters(&stats[i]); |
497 | 0 | } |
498 | 0 | *n_stats = 0; |
499 | 0 | free(stats); |
500 | 0 | stats = NULL; |
501 | 0 | break; |
502 | 0 | } |
503 | | |
504 | 0 | (*n_stats)++; |
505 | 0 | } |
506 | |
|
507 | 0 | *stats_ = stats; |
508 | 0 | return error; |
509 | 0 | } |
510 | | |
511 | | bool |
512 | | dpif_offload_enabled(void) |
513 | 0 | { |
514 | 0 | bool enabled; |
515 | |
|
516 | 0 | atomic_read_relaxed(&offload_global_enabled, &enabled); |
517 | 0 | return enabled; |
518 | 0 | } |
519 | | |
520 | | bool |
521 | | dpif_offload_rebalance_policy_enabled(void) |
522 | 0 | { |
523 | 0 | bool enabled; |
524 | |
|
525 | 0 | atomic_read_relaxed(&offload_rebalance_policy, &enabled); |
526 | 0 | return enabled; |
527 | 0 | } |
528 | | |
529 | | void |
530 | | dpif_offload_set_netdev_offload(struct netdev *netdev, |
531 | | struct dpif_offload *offload) |
532 | 0 | { |
533 | 0 | ovsrcu_set(&netdev->dpif_offload, offload); |
534 | 0 | } |
535 | | |
536 | | static bool |
537 | | dpif_offload_try_port_add(struct dpif_offload *offload, struct netdev *netdev, |
538 | | odp_port_t port_no) |
539 | 0 | { |
540 | 0 | if (offload->class->can_offload(offload, netdev)) { |
541 | 0 | int err = offload->class->port_add(offload, netdev, port_no); |
542 | |
|
543 | 0 | if (!err) { |
544 | 0 | VLOG_DBG("netdev %s added to dpif-offload provider %s", |
545 | 0 | netdev_get_name(netdev), dpif_offload_name(offload)); |
546 | 0 | return true; |
547 | 0 | } else { |
548 | 0 | VLOG_ERR("Failed adding netdev %s to dpif-offload provider " |
549 | 0 | "%s, error %s", |
550 | 0 | netdev_get_name(netdev), dpif_offload_name(offload), |
551 | 0 | ovs_strerror(err)); |
552 | 0 | } |
553 | 0 | } else { |
554 | 0 | VLOG_DBG("netdev %s failed can_offload for dpif-offload provider %s", |
555 | 0 | netdev_get_name(netdev), dpif_offload_name(offload)); |
556 | 0 | } |
557 | 0 | return false; |
558 | 0 | } |
559 | | |
560 | | void |
561 | | dpif_offload_port_add(struct dpif *dpif, struct netdev *netdev, |
562 | | odp_port_t port_no) |
563 | 0 | { |
564 | 0 | struct dpif_offload_provider_collection *collection; |
565 | 0 | const char *port_priority = smap_get(&iface_order_cfg, |
566 | 0 | netdev_get_name(netdev)); |
567 | 0 | struct dpif_offload *offload; |
568 | |
|
569 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
570 | 0 | if (!collection) { |
571 | 0 | return; |
572 | 0 | } |
573 | | |
574 | 0 | if (port_priority) { |
575 | 0 | char *tokens = xstrdup(port_priority); |
576 | 0 | char *saveptr; |
577 | |
|
578 | 0 | VLOG_DBG("for netdev %s using port priority %s", |
579 | 0 | netdev_get_name(netdev), port_priority); |
580 | |
|
581 | 0 | for (char *name = strtok_r(tokens, ",", &saveptr); |
582 | 0 | name; |
583 | 0 | name = strtok_r(NULL, ",", &saveptr)) { |
584 | 0 | bool provider_added = false; |
585 | |
|
586 | 0 | if (!strcmp("none", name)) { |
587 | 0 | break; |
588 | 0 | } |
589 | | |
590 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
591 | 0 | if (!strcmp(name, offload->class->type)) { |
592 | 0 | provider_added = dpif_offload_try_port_add(offload, netdev, |
593 | 0 | port_no); |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | } |
597 | |
|
598 | 0 | if (provider_added) { |
599 | 0 | break; |
600 | 0 | } |
601 | 0 | } |
602 | 0 | free(tokens); |
603 | 0 | } else { |
604 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
605 | 0 | if (dpif_offload_try_port_add(offload, netdev, port_no)) { |
606 | 0 | break; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | void |
613 | | dpif_offload_port_del(struct dpif *dpif, odp_port_t port_no) |
614 | 0 | { |
615 | 0 | struct dpif_offload_provider_collection *collection; |
616 | 0 | struct dpif_offload *offload; |
617 | |
|
618 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
619 | 0 | if (!collection) { |
620 | 0 | return; |
621 | 0 | } |
622 | | |
623 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
624 | 0 | int err = offload->class->port_del(offload, port_no); |
625 | |
|
626 | 0 | if (err) { |
627 | 0 | VLOG_ERR("Failed deleting port_no %d from dpif-offload provider " |
628 | 0 | "%s, error %s", port_no, dpif_offload_name(offload), |
629 | 0 | ovs_strerror(err)); |
630 | 0 | } |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | | void |
635 | | dpif_offload_port_set_config(struct dpif *dpif, odp_port_t port_no, |
636 | | const struct smap *cfg) |
637 | 0 | { |
638 | 0 | struct dpif_offload_provider_collection *collection; |
639 | 0 | struct dpif_offload *offload; |
640 | |
|
641 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
642 | 0 | if (!collection) { |
643 | 0 | return; |
644 | 0 | } |
645 | | |
646 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
647 | 0 | if (offload->class->port_set_config) { |
648 | 0 | offload->class->port_set_config(offload, port_no, cfg); |
649 | 0 | } |
650 | 0 | } |
651 | 0 | } |
652 | | |
653 | | struct dpif_offload * |
654 | | dpif_offload_port_offloaded_by(const struct dpif *dpif, odp_port_t port_no) |
655 | 0 | { |
656 | 0 | struct dpif_offload_provider_collection *collection; |
657 | 0 | struct dpif_offload *offload, *offload_return = NULL; |
658 | |
|
659 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
660 | 0 | if (!collection || !dpif_offload_enabled()) { |
661 | 0 | return NULL; |
662 | 0 | } |
663 | | |
664 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
665 | 0 | if (offload->class->get_netdev(offload, port_no)) { |
666 | 0 | offload_return = offload; |
667 | 0 | break; |
668 | 0 | } |
669 | 0 | } |
670 | |
|
671 | 0 | return offload_return; |
672 | 0 | } |
673 | | |
674 | | struct dpif_offload_dump_state { |
675 | | const struct dpif_offload_provider_collection *collection; |
676 | | struct dpif_offload *entry; |
677 | | int error; |
678 | | }; |
679 | | |
680 | | void |
681 | | dpif_offload_dump_start(const struct dpif *dpif, void **statep) |
682 | 0 | { |
683 | 0 | struct dpif_offload_dump_state *state; |
684 | |
|
685 | 0 | state = xzalloc(sizeof *state); |
686 | 0 | state->collection = dpif_get_offload_provider_collection_with_ref(dpif); |
687 | 0 | if (!state->collection) { |
688 | 0 | state->error = EIDRM; |
689 | 0 | } |
690 | |
|
691 | 0 | *statep = state; |
692 | 0 | } |
693 | | |
694 | | bool |
695 | | dpif_offload_dump_next(void *state_, struct dpif_offload **offload) |
696 | 0 | { |
697 | 0 | struct dpif_offload_dump_state *state = state_; |
698 | |
|
699 | 0 | if (!offload || !state || state->error || !state->collection) { |
700 | 0 | return false; |
701 | 0 | } |
702 | | |
703 | 0 | if (state->entry) { |
704 | 0 | struct dpif_offload *entry = state->entry; |
705 | |
|
706 | 0 | LIST_FOR_EACH_CONTINUE (entry, dpif_list_node, |
707 | 0 | &state->collection->list) { |
708 | 0 | state->entry = entry; |
709 | 0 | *offload = entry; |
710 | 0 | return true; |
711 | 0 | } |
712 | | |
713 | 0 | state->error = EOF; |
714 | 0 | } else { |
715 | | /* Get the first entry in the list. */ |
716 | 0 | struct dpif_offload *entry; |
717 | |
|
718 | 0 | LIST_FOR_EACH (entry, dpif_list_node, &state->collection->list) { |
719 | 0 | break; |
720 | 0 | } |
721 | |
|
722 | 0 | if (entry) { |
723 | 0 | state->entry = entry; |
724 | 0 | *offload = entry; |
725 | 0 | } else { |
726 | 0 | state->error = EOF; |
727 | 0 | } |
728 | 0 | } |
729 | | |
730 | 0 | return !state->error; |
731 | 0 | } |
732 | | |
733 | | int |
734 | | dpif_offload_dump_done(void *state_) |
735 | 0 | { |
736 | 0 | struct dpif_offload_dump_state *state = state_; |
737 | 0 | int error; |
738 | |
|
739 | 0 | dpif_offload_unref_collection( |
740 | 0 | CONST_CAST(struct dpif_offload_provider_collection *, |
741 | 0 | state->collection)); |
742 | 0 | error = state->error == EOF ? 0 : state->error; |
743 | 0 | free(state); |
744 | |
|
745 | 0 | return error; |
746 | 0 | } |
747 | | |
748 | | void |
749 | | dpif_offload_set_global_cfg(const struct ovsrec_open_vswitch *cfg) |
750 | 0 | { |
751 | 0 | static struct ovsthread_once init_once = OVSTHREAD_ONCE_INITIALIZER; |
752 | 0 | const struct smap *other_cfg = &cfg->other_config; |
753 | 0 | const char *priority; |
754 | | |
755 | | /* The 'hw-offload-priority' parameter can only be set at startup, |
756 | | * any successive change needs a restart. */ |
757 | 0 | priority = smap_get(other_cfg, "hw-offload-priority"); |
758 | |
|
759 | 0 | if (ovsthread_once_start(&init_once)) { |
760 | | /* Initialize the dpif-offload layer in case it's not yet initialized |
761 | | * at the first invocation of setting the configuration. */ |
762 | 0 | dpif_offload_module_init(); |
763 | | |
764 | | /* If priority is not set keep the default value. */ |
765 | 0 | if (priority) { |
766 | 0 | char *tokens = xstrdup(priority); |
767 | 0 | char *saveptr; |
768 | |
|
769 | 0 | free(dpif_offload_provider_priority_list); |
770 | 0 | dpif_offload_provider_priority_list = xstrdup(priority); |
771 | | |
772 | | /* Log a warning for unknown offload providers. */ |
773 | 0 | for (char *name = strtok_r(tokens, ",", &saveptr); |
774 | 0 | name; |
775 | 0 | name = strtok_r(NULL, ",", &saveptr)) { |
776 | |
|
777 | 0 | if (!shash_find(&dpif_offload_classes, name)) { |
778 | 0 | VLOG_WARN("hw-offload-priority configuration has an " |
779 | 0 | "unknown type; %s", name); |
780 | 0 | } |
781 | 0 | } |
782 | 0 | free(tokens); |
783 | 0 | } |
784 | 0 | ovsthread_once_done(&init_once); |
785 | 0 | } else { |
786 | 0 | if (priority && strcmp(priority, |
787 | 0 | dpif_offload_provider_priority_list)) { |
788 | 0 | VLOG_INFO_ONCE("hw-offload-priority configuration changed; " |
789 | 0 | "restart required"); |
790 | 0 | } |
791 | 0 | } |
792 | | |
793 | | /* Handle other global configuration settings. */ |
794 | 0 | if (smap_get_bool(other_cfg, "hw-offload", false)) { |
795 | 0 | static struct ovsthread_once once_enable = OVSTHREAD_ONCE_INITIALIZER; |
796 | |
|
797 | 0 | if (ovsthread_once_start(&once_enable)) { |
798 | 0 | atomic_store_relaxed(&offload_global_enabled, true); |
799 | 0 | VLOG_INFO("Flow HW offload is enabled"); |
800 | |
|
801 | 0 | if (smap_get_bool(other_cfg, "offload-rebalance", false)) { |
802 | 0 | atomic_store_relaxed(&offload_rebalance_policy, true); |
803 | 0 | } |
804 | |
|
805 | 0 | ovsthread_once_done(&once_enable); |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | | /* Filter out the 'hw-offload-priority' per interface setting. We need it |
810 | | * before ports are added, so we can assign the correct offload-provider. |
811 | | * Note that we can safely rebuild the map here, as we only access this |
812 | | * from the same (main) thread. */ |
813 | 0 | smap_clear(&iface_order_cfg); |
814 | 0 | for (int i = 0; i < cfg->n_bridges; i++) { |
815 | 0 | const struct ovsrec_bridge *br_cfg = cfg->bridges[i]; |
816 | |
|
817 | 0 | for (int j = 0; j < br_cfg->n_ports; j++) { |
818 | 0 | const struct ovsrec_port *port_cfg = br_cfg->ports[j]; |
819 | |
|
820 | 0 | for (int k = 0; k < port_cfg->n_interfaces; k++) { |
821 | 0 | const struct ovsrec_interface *iface_cfg; |
822 | |
|
823 | 0 | iface_cfg = port_cfg->interfaces[k]; |
824 | 0 | priority = smap_get(&iface_cfg->other_config, |
825 | 0 | "hw-offload-priority"); |
826 | 0 | if (priority) { |
827 | 0 | smap_add(&iface_order_cfg, iface_cfg->name, priority); |
828 | 0 | } |
829 | 0 | } |
830 | 0 | } |
831 | 0 | } |
832 | 0 | } |
833 | | |
834 | | void |
835 | | dpif_offload_flow_flush(struct dpif *dpif) |
836 | 0 | { |
837 | 0 | struct dpif_offload_provider_collection *collection; |
838 | 0 | struct dpif_offload *offload; |
839 | |
|
840 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
841 | 0 | if (!collection) { |
842 | 0 | return; |
843 | 0 | } |
844 | | |
845 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
846 | 0 | if (offload->class->flow_flush) { |
847 | 0 | int err = offload->class->flow_flush(offload); |
848 | |
|
849 | 0 | if (err) { |
850 | 0 | VLOG_ERR( |
851 | 0 | "Failed flow flush on dpif-offload provider %s, error %s", |
852 | 0 | dpif_offload_name(offload), ovs_strerror(err)); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | } |
856 | 0 | } |
857 | | |
858 | | enum dpif_offload_impl_type |
859 | | dpif_offload_get_impl_type(const struct dpif_offload *offload) |
860 | 0 | { |
861 | 0 | return offload->class->impl_type; |
862 | 0 | } |
863 | | |
864 | | enum dpif_offload_impl_type |
865 | | dpif_offload_get_impl_type_by_class(const char *type) |
866 | 0 | { |
867 | 0 | enum dpif_offload_impl_type impl_type = DPIF_OFFLOAD_IMPL_NONE; |
868 | 0 | struct shash_node *node; |
869 | |
|
870 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
871 | 0 | SHASH_FOR_EACH (node, &dpif_offload_classes) { |
872 | 0 | const struct dpif_offload_class *class = node->data; |
873 | |
|
874 | 0 | if (!strcmp(type, class->type)) { |
875 | 0 | impl_type = class->impl_type; |
876 | 0 | break; |
877 | 0 | } |
878 | 0 | } |
879 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
880 | |
|
881 | 0 | return impl_type; |
882 | 0 | } |
883 | | |
884 | | uint64_t |
885 | | dpif_offload_flow_count(const struct dpif *dpif) |
886 | 0 | { |
887 | 0 | struct dpif_offload_provider_collection *collection; |
888 | 0 | const struct dpif_offload *offload; |
889 | 0 | uint64_t flow_count = 0; |
890 | |
|
891 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
892 | 0 | if (!collection || !dpif_offload_enabled()) { |
893 | 0 | return 0; |
894 | 0 | } |
895 | | |
896 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
897 | 0 | if (offload->class->flow_count) { |
898 | 0 | flow_count += offload->class->flow_count(offload); |
899 | 0 | } |
900 | 0 | } |
901 | |
|
902 | 0 | return flow_count; |
903 | 0 | } |
904 | | |
905 | | uint64_t |
906 | | dpif_offload_flow_count_by_impl(const struct dpif *dpif, |
907 | | enum dpif_offload_impl_type type) |
908 | 0 | { |
909 | 0 | struct dpif_offload_provider_collection *collection; |
910 | 0 | const struct dpif_offload *offload; |
911 | 0 | uint64_t flow_count = 0; |
912 | |
|
913 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
914 | 0 | if (!collection || !dpif_offload_enabled()) { |
915 | 0 | return 0; |
916 | 0 | } |
917 | | |
918 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
919 | 0 | if (offload->class->flow_count |
920 | 0 | && type == dpif_offload_get_impl_type(offload)) { |
921 | 0 | flow_count += offload->class->flow_count(offload); |
922 | 0 | } |
923 | 0 | } |
924 | |
|
925 | 0 | return flow_count; |
926 | 0 | } |
927 | | |
928 | | void |
929 | | dpif_offload_meter_set(const struct dpif *dpif, ofproto_meter_id meter_id, |
930 | | struct ofputil_meter_config *config) |
931 | 0 | { |
932 | 0 | struct dpif_offload_provider_collection *collection; |
933 | 0 | struct dpif_offload *offload; |
934 | |
|
935 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
936 | 0 | if (!collection || !dpif_offload_enabled()) { |
937 | 0 | return; |
938 | 0 | } |
939 | | |
940 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
941 | 0 | if (offload->class->meter_set) { |
942 | 0 | int err = offload->class->meter_set(offload, meter_id, config); |
943 | |
|
944 | 0 | if (err) { |
945 | | /* Offload APIs could fail, for example, because the offload |
946 | | * is not supported. This is fine, as the offload API should |
947 | | * take care of this. */ |
948 | 0 | VLOG_DBG_RL(&rl_dbg, |
949 | 0 | "Failed setting meter %u on dpif-offload provider" |
950 | 0 | " %s, error %s", meter_id.uint32, |
951 | 0 | dpif_offload_name(offload), ovs_strerror(err)); |
952 | 0 | } |
953 | 0 | } |
954 | 0 | } |
955 | 0 | } |
956 | | |
957 | | void |
958 | | dpif_offload_meter_get(const struct dpif *dpif, ofproto_meter_id meter_id, |
959 | | struct ofputil_meter_stats *stats) |
960 | 0 | { |
961 | 0 | struct dpif_offload_provider_collection *collection; |
962 | 0 | struct dpif_offload *offload; |
963 | |
|
964 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
965 | 0 | if (!collection || !dpif_offload_enabled()) { |
966 | 0 | return; |
967 | 0 | } |
968 | | |
969 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
970 | 0 | if (offload->class->meter_get) { |
971 | 0 | int err = offload->class->meter_get(offload, meter_id, stats); |
972 | 0 | if (err) { |
973 | 0 | VLOG_DBG_RL(&rl_dbg, |
974 | 0 | "Failed getting meter %u on dpif-offload provider" |
975 | 0 | " %s, error %s", meter_id.uint32, |
976 | 0 | dpif_offload_name(offload), ovs_strerror(err)); |
977 | 0 | } |
978 | 0 | } |
979 | 0 | } |
980 | 0 | } |
981 | | |
982 | | void |
983 | | dpif_offload_meter_del(const struct dpif *dpif, ofproto_meter_id meter_id, |
984 | | struct ofputil_meter_stats *stats) |
985 | 0 | { |
986 | 0 | struct dpif_offload_provider_collection *collection; |
987 | 0 | struct dpif_offload *offload; |
988 | |
|
989 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
990 | 0 | if (!collection || !dpif_offload_enabled()) { |
991 | 0 | return; |
992 | 0 | } |
993 | | |
994 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
995 | 0 | if (offload->class->meter_del) { |
996 | 0 | int err = offload->class->meter_del(offload, meter_id, stats); |
997 | 0 | if (err) { |
998 | 0 | VLOG_DBG_RL(&rl_dbg, |
999 | 0 | "Failed deleting meter %u on dpif-offload provider" |
1000 | 0 | " %s, error %s", meter_id.uint32, |
1001 | 0 | dpif_offload_name(offload), ovs_strerror(err)); |
1002 | 0 | } |
1003 | 0 | } |
1004 | 0 | } |
1005 | 0 | } |
1006 | | |
1007 | | /* |
1008 | | * Further initializes a 'struct dpif_flow_dump' that was already initialized |
1009 | | * by dpif_flow_dump_create(), preparing it for use by |
1010 | | * dpif_offload_flow_dump_next(). |
1011 | | * |
1012 | | * For more details, see the documentation of dpif_flow_dump_create(). */ |
1013 | | void |
1014 | | dpif_offload_flow_dump_create(struct dpif_flow_dump *dump, |
1015 | | const struct dpif *dpif, bool terse) |
1016 | 0 | { |
1017 | 0 | struct dpif_offload_provider_collection *collection; |
1018 | 0 | const struct dpif_offload *offload; |
1019 | 0 | size_t n_providers = 0; |
1020 | 0 | int i = 0; |
1021 | |
|
1022 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
1023 | 0 | if (!dump || !dpif_offload_enabled() || !collection) { |
1024 | 0 | return; |
1025 | 0 | } |
1026 | | |
1027 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1028 | 0 | if (offload->class->flow_dump_create) { |
1029 | 0 | n_providers++; |
1030 | 0 | } |
1031 | 0 | } |
1032 | |
|
1033 | 0 | if (!n_providers) { |
1034 | 0 | return; |
1035 | 0 | } |
1036 | | |
1037 | 0 | dump->offload_dumps = xmalloc(n_providers * sizeof( |
1038 | 0 | struct dpif_offload_flow_dump *)); |
1039 | |
|
1040 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1041 | 0 | if (offload->class->flow_dump_create) { |
1042 | 0 | dump->offload_dumps[i++] = |
1043 | 0 | offload->class->flow_dump_create(offload, terse); |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | dump->n_offload_dumps = i; |
1047 | 0 | dump->offload_dump_index = 0; |
1048 | 0 | } |
1049 | | |
1050 | | /* Destroys the 'dump' data associated with the offload flow dump. |
1051 | | * This function is called as part of the general dump cleanup |
1052 | | * by dpif_flow_dump_destroy(). |
1053 | | * |
1054 | | * Returns 0 if all individual dpif-offload dump operations complete |
1055 | | * without error. If one or more providers return an error, the error |
1056 | | * code from the first failing provider is returned as a positive errno |
1057 | | * value. */ |
1058 | | int |
1059 | | dpif_offload_flow_dump_destroy(struct dpif_flow_dump *dump) |
1060 | 0 | { |
1061 | 0 | int error = 0; |
1062 | |
|
1063 | 0 | for (int i = 0; i < dump->n_offload_dumps; i++) { |
1064 | 0 | struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i]; |
1065 | 0 | const struct dpif_offload *offload = offload_dump->offload; |
1066 | 0 | int rc = offload->class->flow_dump_destroy(offload_dump); |
1067 | |
|
1068 | 0 | if (rc && rc != EOF) { |
1069 | 0 | VLOG_ERR("Failed flow dumping on dpif-offload provider " |
1070 | 0 | "%s, error %s", dpif_offload_name(offload), |
1071 | 0 | ovs_strerror(rc)); |
1072 | 0 | if (!error) { |
1073 | 0 | error = rc; |
1074 | 0 | } |
1075 | 0 | } |
1076 | 0 | } |
1077 | 0 | ovs_mutex_destroy(&dump->offload_dump_mutex); |
1078 | 0 | free(dump->offload_dumps); |
1079 | 0 | return error; |
1080 | 0 | } |
1081 | | |
1082 | | static void |
1083 | | dpif_offload_advance_provider_dump(struct dpif_flow_dump_thread *thread) |
1084 | 0 | { |
1085 | 0 | struct dpif_flow_dump *dump = thread->dump; |
1086 | |
|
1087 | 0 | ovs_mutex_lock(&dump->offload_dump_mutex); |
1088 | | |
1089 | | /* If we haven't finished (dumped all providers). */ |
1090 | 0 | if (dump->offload_dump_index < dump->n_offload_dumps) { |
1091 | | /* If we are the first to find that current dump is finished |
1092 | | * advance it. */ |
1093 | 0 | if (thread->offload_dump_index == dump->offload_dump_index) { |
1094 | 0 | thread->offload_dump_index = ++dump->offload_dump_index; |
1095 | | /* Did we just finish the last dump? If so we are done. */ |
1096 | 0 | if (dump->offload_dump_index == dump->n_offload_dumps) { |
1097 | 0 | thread->offload_dump_done = true; |
1098 | 0 | } |
1099 | 0 | } else { |
1100 | | /* otherwise, we are behind, catch up */ |
1101 | 0 | thread->offload_dump_index = dump->offload_dump_index; |
1102 | 0 | } |
1103 | 0 | } else { |
1104 | | /* Some other thread finished. */ |
1105 | 0 | thread->offload_dump_done = true; |
1106 | 0 | } |
1107 | |
|
1108 | 0 | ovs_mutex_unlock(&dump->offload_dump_mutex); |
1109 | 0 | } |
1110 | | |
1111 | | /* This function behaves exactly the same as dpif_flow_dump_next(), |
1112 | | * so see its documentation for details. */ |
1113 | | int |
1114 | | dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *thread, |
1115 | | struct dpif_flow *flows, int max_flows) |
1116 | 0 | { |
1117 | 0 | int n_flows = 0; |
1118 | |
|
1119 | 0 | ovs_assert(max_flows > 0); |
1120 | | |
1121 | | /* The logic here processes all registered offload providers and |
1122 | | * dumps all related flows. If done (i.e., it returns 0), continue |
1123 | | * with the next offload provider. */ |
1124 | 0 | while (!thread->offload_dump_done) { |
1125 | 0 | struct dpif_offload_flow_dump_thread *offload_thread; |
1126 | |
|
1127 | 0 | ovs_assert(thread->offload_dump_index < thread->n_offload_threads); |
1128 | 0 | offload_thread = thread->offload_threads[thread->offload_dump_index]; |
1129 | 0 | n_flows = offload_thread->dump->offload->class->flow_dump_next( |
1130 | 0 | offload_thread, flows, max_flows); |
1131 | |
|
1132 | 0 | if (n_flows > 0) { |
1133 | | /* If we got some flows, we need to return due to the constraint |
1134 | | * on returned flows, as explained in dpif_flow_dump_next(). */ |
1135 | 0 | break; |
1136 | 0 | } |
1137 | 0 | dpif_offload_advance_provider_dump(thread); |
1138 | 0 | } |
1139 | 0 | return MAX(n_flows, 0); |
1140 | 0 | } |
1141 | | |
1142 | | /* Further initializes a 'struct dpif_flow_dump_thread' that was already |
1143 | | * initialized by dpif_flow_dump_thread_create(), preparing it for use by |
1144 | | * dpif_offload_flow_dump_next(). */ |
1145 | | void |
1146 | | dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *thread, |
1147 | | struct dpif_flow_dump *dump) |
1148 | 0 | { |
1149 | 0 | if (!dpif_offload_enabled() || !dump || !dump->n_offload_dumps) { |
1150 | 0 | return; |
1151 | 0 | } |
1152 | | |
1153 | 0 | thread->n_offload_threads = dump->n_offload_dumps; |
1154 | 0 | thread->offload_dump_done = false; |
1155 | 0 | thread->offload_dump_index = 0; |
1156 | 0 | thread->offload_threads = |
1157 | 0 | xmalloc(thread->n_offload_threads * sizeof *thread->offload_threads); |
1158 | |
|
1159 | 0 | for (int i = 0; i < dump->n_offload_dumps; i++) { |
1160 | 0 | struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i]; |
1161 | 0 | const struct dpif_offload *offload = offload_dump->offload; |
1162 | |
|
1163 | 0 | thread->offload_threads[i] = |
1164 | 0 | offload->class->flow_dump_thread_create(offload_dump); |
1165 | 0 | } |
1166 | 0 | } |
1167 | | |
1168 | | /* Destroys the 'thread' data associated with the offload flow dump. |
1169 | | * This function is called as part of the general thread cleanup |
1170 | | * by dpif_flow_dump_thread_destroy(). */ |
1171 | | void |
1172 | | dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread) |
1173 | 0 | { |
1174 | 0 | for (int i = 0; i < thread->n_offload_threads; i++) { |
1175 | 0 | struct dpif_offload_flow_dump_thread *offload_thread; |
1176 | 0 | const struct dpif_offload *offload; |
1177 | |
|
1178 | 0 | offload_thread = thread->offload_threads[i]; |
1179 | 0 | offload = offload_thread->dump->offload; |
1180 | 0 | offload->class->flow_dump_thread_destroy(offload_thread); |
1181 | 0 | } |
1182 | 0 | free(thread->offload_threads); |
1183 | 0 | } |
1184 | | |
1185 | | static struct netdev * |
1186 | | dpif_offload_get_netdev_by_port_id__( |
1187 | | struct dpif_offload_provider_collection *collection, |
1188 | | struct dpif_offload **offload, odp_port_t port_no) |
1189 | 0 | { |
1190 | 0 | struct dpif_offload *tmp_offload; |
1191 | 0 | struct netdev *netdev = NULL; |
1192 | |
|
1193 | 0 | LIST_FOR_EACH (tmp_offload, dpif_list_node, &collection->list) { |
1194 | 0 | netdev = tmp_offload->class->get_netdev(tmp_offload, port_no); |
1195 | 0 | if (netdev) { |
1196 | 0 | if (offload) { |
1197 | 0 | *offload = tmp_offload; |
1198 | 0 | } |
1199 | 0 | break; |
1200 | 0 | } |
1201 | 0 | } |
1202 | 0 | return netdev; |
1203 | 0 | } |
1204 | | |
1205 | | struct netdev * |
1206 | | dpif_offload_get_netdev_by_port_id(struct dpif *dpif, |
1207 | | struct dpif_offload **offload, |
1208 | | odp_port_t port_no) |
1209 | 0 | { |
1210 | 0 | struct dpif_offload_provider_collection *collection; |
1211 | |
|
1212 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
1213 | 0 | if (!collection || !dpif_offload_enabled()) { |
1214 | 0 | return NULL; |
1215 | 0 | } |
1216 | | |
1217 | 0 | return dpif_offload_get_netdev_by_port_id__(collection, offload, port_no); |
1218 | 0 | } |
1219 | | |
1220 | | bool |
1221 | | dpif_offload_netdevs_out_of_resources(struct dpif *dpif) |
1222 | 0 | { |
1223 | 0 | struct dpif_offload_provider_collection *collection; |
1224 | 0 | struct dpif_offload_port *port; |
1225 | 0 | struct dpif_offload *offload; |
1226 | |
|
1227 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
1228 | 0 | if (!collection || !dpif_offload_enabled()) { |
1229 | 0 | return false; |
1230 | 0 | } |
1231 | | |
1232 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1233 | 0 | DPIF_OFFLOAD_PORT_FOR_EACH (port, offload) { |
1234 | 0 | if (port->netdev->hw_info.oor) { |
1235 | 0 | return true; |
1236 | 0 | } |
1237 | 0 | } |
1238 | 0 | } |
1239 | | |
1240 | 0 | return false; |
1241 | 0 | } |
1242 | | |
1243 | | /* This function tries to offload the operations to the dpif-offload |
1244 | | * providers. It will return the number of operations not handled, whose |
1245 | | * pointers are re-arranged and available in **ops. */ |
1246 | | size_t |
1247 | | dpif_offload_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops, |
1248 | | enum dpif_offload_type offload_type) |
1249 | 0 | { |
1250 | 0 | struct dpif_offload_provider_collection *collection; |
1251 | 0 | const struct dpif_offload *offload; |
1252 | 0 | size_t n_ops_left = 0; |
1253 | |
|
1254 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
1255 | 0 | if (!collection || !dpif_offload_enabled()) { |
1256 | 0 | return n_ops; |
1257 | 0 | } |
1258 | | |
1259 | 0 | for (size_t i = 0; i < n_ops; i++) { |
1260 | 0 | ops[i]->error = -1; |
1261 | 0 | } |
1262 | |
|
1263 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1264 | 0 | if (offload->class->impl_type == DPIF_OFFLOAD_IMPL_FLOWS_PROVIDER_ONLY |
1265 | 0 | && offload->class->operate) { |
1266 | |
|
1267 | 0 | offload->class->operate(dpif, offload, ops, n_ops); |
1268 | |
|
1269 | 0 | for (size_t i = 0; i < n_ops; i++) { |
1270 | 0 | struct dpif_op *op = ops[i]; |
1271 | |
|
1272 | 0 | if (op->error == EOPNOTSUPP) { |
1273 | | /* Not supported by this offload provider, try next one. */ |
1274 | 0 | op->error = -1; |
1275 | 0 | } else { |
1276 | 0 | VLOG_DBG("Tried offloading %d to dpif-offload provider " |
1277 | 0 | "%s, error %d", |
1278 | 0 | op->type, |
1279 | 0 | dpif_offload_name(offload), op->error); |
1280 | |
|
1281 | 0 | switch (op->type) { |
1282 | 0 | case DPIF_OP_FLOW_PUT: { |
1283 | 0 | int log_error = 0; |
1284 | | |
1285 | | /* Keep ENOSPC and unhandled operations on the |
1286 | | * existing debug-only path, but include full flow |
1287 | | * details for real offload failures. */ |
1288 | 0 | if (op->error > 0 && op->error != ENOSPC) { |
1289 | 0 | log_error = op->error; |
1290 | 0 | } |
1291 | 0 | log_flow_put_message(dpif, &this_module, |
1292 | 0 | &op->flow_put, log_error); |
1293 | 0 | break; |
1294 | 0 | } |
1295 | 0 | case DPIF_OP_FLOW_DEL: |
1296 | 0 | log_flow_del_message(dpif, &this_module, |
1297 | 0 | &op->flow_del, 0); |
1298 | 0 | break; |
1299 | 0 | case DPIF_OP_FLOW_GET: |
1300 | 0 | log_flow_get_message(dpif, &this_module, |
1301 | 0 | &op->flow_get, 0); |
1302 | 0 | break; |
1303 | 0 | case DPIF_OP_EXECUTE: |
1304 | 0 | break; |
1305 | 0 | } |
1306 | 0 | } |
1307 | 0 | } |
1308 | 0 | } |
1309 | 0 | } |
1310 | | |
1311 | 0 | for (size_t i = 0; i < n_ops; i++) { |
1312 | 0 | struct dpif_op *op = ops[i]; |
1313 | |
|
1314 | 0 | if (offload_type == DPIF_OFFLOAD_ALWAYS) { |
1315 | | /* For DPIF_OFFLOAD_ALWAYS, we should keep the error values, |
1316 | | * and mark the unprocessed ones as EOPNOTSUPP. This way, they |
1317 | | * will not be processed by the dpif layer. */ |
1318 | 0 | if (op->error < 0) { |
1319 | 0 | op->error = EOPNOTSUPP; |
1320 | 0 | } |
1321 | 0 | continue; |
1322 | 0 | } |
1323 | | |
1324 | | /* For the other offload types, operations that were not handled or |
1325 | | * failed to offload should be processed by the dpif layer. */ |
1326 | 0 | if (op->error != 0 && op->error != EEXIST) { |
1327 | 0 | op->error = 0; |
1328 | 0 | ops[n_ops_left++] = op; |
1329 | 0 | } |
1330 | 0 | } |
1331 | |
|
1332 | 0 | return n_ops_left; |
1333 | 0 | } |
1334 | | |
1335 | | |
1336 | | bool |
1337 | | dpif_offload_netdev_same_offload(const struct netdev *a, |
1338 | | const struct netdev *b) |
1339 | 0 | { |
1340 | 0 | const struct dpif_offload *offload_a, *offload_b; |
1341 | |
|
1342 | 0 | offload_a = ovsrcu_get(const struct dpif_offload *, &a->dpif_offload); |
1343 | 0 | offload_b = ovsrcu_get(const struct dpif_offload *, &b->dpif_offload); |
1344 | |
|
1345 | 0 | return offload_a == offload_b; |
1346 | 0 | } |
1347 | | |
1348 | | int |
1349 | | dpif_offload_datapath_flow_put(const char *dpif_name, |
1350 | | struct dpif_offload_flow_put *put, |
1351 | | void **previous_flow_reference) |
1352 | 0 | { |
1353 | 0 | struct dpif_offload_provider_collection *collection; |
1354 | 0 | struct dpif_offload *offload; |
1355 | 0 | struct netdev *netdev; |
1356 | |
|
1357 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
1358 | | /* XXX: Implement a faster solution than the current dpif_name lookup. */ |
1359 | 0 | collection = shash_find_data(&dpif_offload_providers, dpif_name); |
1360 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
1361 | |
|
1362 | 0 | if (OVS_UNLIKELY(!collection)) { |
1363 | 0 | *previous_flow_reference = NULL; |
1364 | 0 | return EOPNOTSUPP; |
1365 | 0 | } |
1366 | | |
1367 | 0 | netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload, |
1368 | 0 | put->in_port); |
1369 | |
|
1370 | 0 | if (OVS_LIKELY(netdev && offload->class->netdev_flow_put)) { |
1371 | 0 | return offload->class->netdev_flow_put(offload, netdev, put, |
1372 | 0 | previous_flow_reference); |
1373 | 0 | } |
1374 | | |
1375 | 0 | *previous_flow_reference = NULL; |
1376 | 0 | return EOPNOTSUPP; |
1377 | 0 | } |
1378 | | |
1379 | | int |
1380 | | dpif_offload_datapath_flow_del(const char *dpif_name, |
1381 | | struct dpif_offload_flow_del *del) |
1382 | 0 | { |
1383 | 0 | struct dpif_offload_provider_collection *collection; |
1384 | 0 | struct dpif_offload *offload; |
1385 | 0 | struct netdev *netdev; |
1386 | |
|
1387 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
1388 | | /* XXX: Implement a faster solution than the current dpif_name lookup. */ |
1389 | 0 | collection = shash_find_data(&dpif_offload_providers, dpif_name); |
1390 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
1391 | |
|
1392 | 0 | if (OVS_UNLIKELY(!collection)) { |
1393 | 0 | return EOPNOTSUPP; |
1394 | 0 | } |
1395 | | |
1396 | 0 | netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload, |
1397 | 0 | del->in_port); |
1398 | |
|
1399 | 0 | if (OVS_LIKELY(netdev && offload->class->netdev_flow_del)) { |
1400 | 0 | return offload->class->netdev_flow_del(offload, netdev, del); |
1401 | 0 | } |
1402 | | |
1403 | 0 | return EOPNOTSUPP; |
1404 | 0 | } |
1405 | | |
1406 | | bool |
1407 | | dpif_offload_datapath_flow_stats(const char *dpif_name, odp_port_t in_port, |
1408 | | const ovs_u128 *ufid, |
1409 | | struct dpif_flow_stats *stats, |
1410 | | struct dpif_flow_attrs *attrs) |
1411 | 0 | { |
1412 | 0 | struct dpif_offload_provider_collection *collection; |
1413 | 0 | struct dpif_offload *offload; |
1414 | 0 | struct netdev *netdev; |
1415 | |
|
1416 | 0 | if (!dpif_offload_enabled()) { |
1417 | 0 | return false; |
1418 | 0 | } |
1419 | | |
1420 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
1421 | | /* XXX: Implement a faster solution than the current dpif_name lookup. */ |
1422 | 0 | collection = shash_find_data(&dpif_offload_providers, dpif_name); |
1423 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
1424 | |
|
1425 | 0 | if (OVS_UNLIKELY(!collection)) { |
1426 | 0 | return false; |
1427 | 0 | } |
1428 | | |
1429 | 0 | netdev = dpif_offload_get_netdev_by_port_id__(collection, &offload, |
1430 | 0 | in_port); |
1431 | |
|
1432 | 0 | if (OVS_LIKELY(netdev && offload->class->netdev_flow_stats)) { |
1433 | 0 | return offload->class->netdev_flow_stats(offload, netdev, ufid, stats, |
1434 | 0 | attrs); |
1435 | 0 | } |
1436 | 0 | return false; |
1437 | 0 | } |
1438 | | |
1439 | | int |
1440 | | dpif_offload_netdev_hw_post_process(struct netdev *netdev, unsigned pmd_id, |
1441 | | struct dp_packet *packet, |
1442 | | void **flow_reference) |
1443 | 0 | { |
1444 | 0 | const struct dpif_offload *offload; |
1445 | 0 | bool post_process_api_supported; |
1446 | 0 | int rc; |
1447 | |
|
1448 | 0 | atomic_read_relaxed(&netdev->hw_info.post_process_api_supported, |
1449 | 0 | &post_process_api_supported); |
1450 | 0 | if (!post_process_api_supported) { |
1451 | 0 | return EOPNOTSUPP; |
1452 | 0 | } |
1453 | | |
1454 | 0 | offload = ovsrcu_get(const struct dpif_offload *, &netdev->dpif_offload); |
1455 | |
|
1456 | 0 | if (!offload || !offload->class->netdev_hw_post_process) { |
1457 | 0 | if (offload) { |
1458 | | /* Offload is configured and API unsupported by the port; |
1459 | | * avoid subsequent calls. */ |
1460 | 0 | atomic_store_relaxed(&netdev->hw_info.post_process_api_supported, |
1461 | 0 | false); |
1462 | 0 | } |
1463 | 0 | return EOPNOTSUPP; |
1464 | 0 | } |
1465 | | |
1466 | 0 | rc = offload->class->netdev_hw_post_process(offload, netdev, pmd_id, |
1467 | 0 | packet, flow_reference); |
1468 | 0 | if (rc == EOPNOTSUPP) { |
1469 | | /* API unsupported by the port; avoid subsequent calls. */ |
1470 | 0 | atomic_store_relaxed(&netdev->hw_info.post_process_api_supported, |
1471 | 0 | false); |
1472 | 0 | } |
1473 | 0 | return rc; |
1474 | 0 | } |
1475 | | |
1476 | | bool |
1477 | | dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *ingress_netdev, |
1478 | | struct dp_packet *packet, |
1479 | | ovs_be16 *src_port) |
1480 | 0 | { |
1481 | 0 | const struct dpif_offload *offload; |
1482 | |
|
1483 | 0 | offload = ovsrcu_get(const struct dpif_offload *, |
1484 | 0 | &ingress_netdev->dpif_offload); |
1485 | |
|
1486 | 0 | if (OVS_UNLIKELY(!offload) |
1487 | 0 | || !offload->class->netdev_udp_tnl_get_src_port) { |
1488 | 0 | return false; |
1489 | 0 | } |
1490 | | |
1491 | 0 | return offload->class->netdev_udp_tnl_get_src_port(offload, ingress_netdev, |
1492 | 0 | packet, src_port); |
1493 | 0 | } |
1494 | | |
1495 | | void |
1496 | | dpif_offload_datapath_register_flow_unreference_cb( |
1497 | | struct dpif *dpif, dpif_offload_flow_unreference_cb *cb) |
1498 | 0 | { |
1499 | 0 | struct dpif_offload_provider_collection *collection; |
1500 | 0 | const struct dpif_offload *offload; |
1501 | | |
1502 | | /* In this case, we assert to make sure this initialization is done after |
1503 | | * the offload providers have been assigned to the dpif. */ |
1504 | 0 | collection = dpif_get_offload_provider_collection(dpif); |
1505 | 0 | ovs_assert(collection); |
1506 | |
|
1507 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1508 | 0 | if (offload->class->register_flow_unreference_cb) { |
1509 | 0 | offload->class->register_flow_unreference_cb(offload, cb); |
1510 | 0 | } |
1511 | 0 | } |
1512 | 0 | } |
1513 | | |
1514 | | |
1515 | | struct dpif_offload_port_mgr * |
1516 | | dpif_offload_port_mgr_init(void) |
1517 | 0 | { |
1518 | 0 | struct dpif_offload_port_mgr *mgr = xmalloc(sizeof *mgr); |
1519 | |
|
1520 | 0 | ovs_mutex_init(&mgr->cmap_mod_lock); |
1521 | |
|
1522 | 0 | cmap_init(&mgr->odp_port_to_port); |
1523 | 0 | cmap_init(&mgr->netdev_to_port); |
1524 | 0 | cmap_init(&mgr->ifindex_to_port); |
1525 | |
|
1526 | 0 | return mgr; |
1527 | 0 | } |
1528 | | |
1529 | | void |
1530 | | dpif_offload_port_mgr_destroy(struct dpif_offload *offload) |
1531 | 0 | { |
1532 | 0 | struct dpif_offload_port_mgr *ports; |
1533 | |
|
1534 | 0 | if (!offload || !offload->ports) { |
1535 | 0 | return; |
1536 | 0 | } |
1537 | | |
1538 | 0 | ports = offload->ports; |
1539 | 0 | offload->ports = NULL; |
1540 | |
|
1541 | 0 | ovs_assert(cmap_count(&ports->odp_port_to_port) == 0); |
1542 | 0 | ovs_assert(cmap_count(&ports->netdev_to_port) == 0); |
1543 | 0 | ovs_assert(cmap_count(&ports->ifindex_to_port) == 0); |
1544 | |
|
1545 | 0 | cmap_destroy(&ports->odp_port_to_port); |
1546 | 0 | cmap_destroy(&ports->netdev_to_port); |
1547 | 0 | cmap_destroy(&ports->ifindex_to_port); |
1548 | 0 | free(ports); |
1549 | 0 | } |
1550 | | |
1551 | | struct dpif_offload_port * |
1552 | | dpif_offload_port_mgr_find_by_ifindex(const struct dpif_offload *offload, |
1553 | | int ifindex) |
1554 | 0 | { |
1555 | 0 | struct dpif_offload_port_mgr *ports = offload->ports; |
1556 | 0 | struct dpif_offload_port *port; |
1557 | |
|
1558 | 0 | ovs_assert(ports); |
1559 | |
|
1560 | 0 | if (ifindex < 0) { |
1561 | 0 | return NULL; |
1562 | 0 | } |
1563 | | |
1564 | 0 | CMAP_FOR_EACH_WITH_HASH (port, ifindex_node, hash_int(ifindex, 0), |
1565 | 0 | &ports->ifindex_to_port) { |
1566 | 0 | if (port->ifindex == ifindex) { |
1567 | 0 | return port; |
1568 | 0 | } |
1569 | 0 | } |
1570 | 0 | return NULL; |
1571 | 0 | } |
1572 | | |
1573 | | struct dpif_offload_port * |
1574 | | dpif_offload_port_mgr_find_by_netdev(const struct dpif_offload *offload, |
1575 | | struct netdev *netdev) |
1576 | 0 | { |
1577 | 0 | struct dpif_offload_port_mgr *ports = offload->ports; |
1578 | 0 | struct dpif_offload_port *port; |
1579 | |
|
1580 | 0 | ovs_assert(ports); |
1581 | |
|
1582 | 0 | if (!netdev) { |
1583 | 0 | return NULL; |
1584 | 0 | } |
1585 | | |
1586 | 0 | CMAP_FOR_EACH_WITH_HASH (port, netdev_node, hash_pointer(netdev, 0), |
1587 | 0 | &ports->netdev_to_port) { |
1588 | 0 | if (port->netdev == netdev) { |
1589 | 0 | return port; |
1590 | 0 | } |
1591 | 0 | } |
1592 | 0 | return NULL; |
1593 | 0 | } |
1594 | | |
1595 | | struct dpif_offload_port * |
1596 | | dpif_offload_port_mgr_find_by_odp_port(const struct dpif_offload *offload, |
1597 | | odp_port_t port_no) |
1598 | 0 | { |
1599 | 0 | struct dpif_offload_port_mgr *ports = offload->ports; |
1600 | 0 | struct dpif_offload_port *port; |
1601 | |
|
1602 | 0 | ovs_assert(ports); |
1603 | |
|
1604 | 0 | CMAP_FOR_EACH_WITH_HASH (port, odp_port_node, |
1605 | 0 | hash_int(odp_to_u32(port_no), 0), |
1606 | 0 | &ports->odp_port_to_port) { |
1607 | 0 | if (port->port_no == port_no) { |
1608 | 0 | return port; |
1609 | 0 | } |
1610 | 0 | } |
1611 | 0 | return NULL; |
1612 | 0 | } |
1613 | | |
1614 | | struct dpif_offload_port * |
1615 | | dpif_offload_port_mgr_remove(struct dpif_offload *offload, odp_port_t port_no) |
1616 | 0 | { |
1617 | | /* Note that it is the caller's responsibility to release the netdev |
1618 | | * after port removal. This should probably be done through the |
1619 | | * ovsrcu_postpone() API. */ |
1620 | 0 | struct dpif_offload_port_mgr *ports = offload->ports; |
1621 | 0 | struct dpif_offload_port *port; |
1622 | |
|
1623 | 0 | ovs_assert(ports); |
1624 | |
|
1625 | 0 | ovs_mutex_lock(&ports->cmap_mod_lock); |
1626 | |
|
1627 | 0 | port = dpif_offload_port_mgr_find_by_odp_port(offload, port_no); |
1628 | |
|
1629 | 0 | if (port) { |
1630 | 0 | cmap_remove(&ports->odp_port_to_port, &port->odp_port_node, |
1631 | 0 | hash_int(odp_to_u32(port_no), 0)); |
1632 | 0 | cmap_remove(&ports->netdev_to_port, &port->netdev_node, |
1633 | 0 | hash_pointer(port->netdev, 0)); |
1634 | |
|
1635 | 0 | if (port->ifindex >= 0) { |
1636 | 0 | cmap_remove(&ports->ifindex_to_port, &port->ifindex_node, |
1637 | 0 | hash_int(port->ifindex, 0)); |
1638 | 0 | } |
1639 | 0 | } |
1640 | |
|
1641 | 0 | ovs_mutex_unlock(&ports->cmap_mod_lock); |
1642 | 0 | return port; |
1643 | 0 | } |
1644 | | |
1645 | | bool |
1646 | | dpif_offload_port_mgr_add(struct dpif_offload *offload, |
1647 | | struct dpif_offload_port *port, |
1648 | | struct netdev *netdev, odp_port_t port_no, |
1649 | | bool need_ifindex) |
1650 | 0 | { |
1651 | 0 | struct dpif_offload_port_mgr *ports; |
1652 | | |
1653 | | /* Note that this function takes a reference to the passed-in netdev. |
1654 | | * However, on port removal it is the caller's responsibility to |
1655 | | * release this reference. */ |
1656 | 0 | ovs_assert(offload->ports && netdev); |
1657 | |
|
1658 | 0 | ports = offload->ports; |
1659 | 0 | memset(port, 0, sizeof *port); |
1660 | 0 | port->port_no = port_no; |
1661 | 0 | port->ifindex = need_ifindex ? netdev_get_ifindex(netdev) : -1; |
1662 | |
|
1663 | 0 | ovs_mutex_lock(&ports->cmap_mod_lock); |
1664 | |
|
1665 | 0 | if (dpif_offload_port_mgr_find_by_odp_port(offload, port_no) |
1666 | 0 | || dpif_offload_port_mgr_find_by_ifindex(offload, port->ifindex) |
1667 | 0 | || dpif_offload_port_mgr_find_by_netdev(offload, netdev)) { |
1668 | |
|
1669 | 0 | ovs_mutex_unlock(&ports->cmap_mod_lock); |
1670 | 0 | return false; |
1671 | 0 | } |
1672 | | |
1673 | 0 | port->netdev = netdev_ref(netdev); |
1674 | |
|
1675 | 0 | cmap_insert(&ports->odp_port_to_port, &port->odp_port_node, |
1676 | 0 | hash_int(odp_to_u32(port_no), 0)); |
1677 | |
|
1678 | 0 | cmap_insert(&ports->netdev_to_port, &port->netdev_node, |
1679 | 0 | hash_pointer(netdev, 0)); |
1680 | |
|
1681 | 0 | if (port->ifindex >= 0) { |
1682 | 0 | cmap_insert(&ports->ifindex_to_port, &port->ifindex_node, |
1683 | 0 | hash_int(port->ifindex, 0)); |
1684 | 0 | } |
1685 | |
|
1686 | 0 | ovs_mutex_unlock(&ports->cmap_mod_lock); |
1687 | 0 | return true; |
1688 | 0 | } |
1689 | | |
1690 | | size_t |
1691 | | dpif_offload_port_mgr_port_count(const struct dpif_offload *offload) |
1692 | 0 | { |
1693 | 0 | ovs_assert(offload && offload->ports); |
1694 | |
|
1695 | 0 | return cmap_count(&offload->ports->odp_port_to_port); |
1696 | 0 | } |
1697 | | |
1698 | | struct dpif_offload_pmd_ctx_node { |
1699 | | const struct dpif_offload *offload; |
1700 | | void *provider_ctx; |
1701 | | }; |
1702 | | |
1703 | | struct dpif_offload_pmd_ctx { |
1704 | | unsigned core_id; |
1705 | | int numa_id; |
1706 | | size_t n_nodes; |
1707 | | struct dpif_offload_pmd_ctx_node nodes[TOTAL_PROVIDERS]; |
1708 | | }; |
1709 | | |
1710 | | void |
1711 | | dpif_offload_pmd_thread_reload(const char *dpif_name, unsigned core_id, |
1712 | | int numa_id, struct dpif_offload_pmd_ctx **ctx_) |
1713 | 0 | { |
1714 | 0 | struct dpif_offload_pmd_ctx_node old_nodes[TOTAL_PROVIDERS]; |
1715 | 0 | struct dpif_offload_provider_collection *collection; |
1716 | 0 | struct dpif_offload_pmd_ctx *ctx; |
1717 | 0 | struct dpif_offload *offload; |
1718 | 0 | size_t old_n_nodes = 0; |
1719 | |
|
1720 | 0 | if (!dpif_offload_enabled()) { |
1721 | 0 | ovs_assert(!*ctx_); |
1722 | 0 | return; |
1723 | 0 | } |
1724 | | |
1725 | 0 | ovs_mutex_lock(&dpif_offload_mutex); |
1726 | 0 | collection = shash_find_data(&dpif_offload_providers, dpif_name); |
1727 | 0 | ovs_mutex_unlock(&dpif_offload_mutex); |
1728 | |
|
1729 | 0 | if (OVS_UNLIKELY(!collection)) { |
1730 | 0 | ovs_assert(!*ctx_); |
1731 | 0 | return; |
1732 | 0 | } |
1733 | | |
1734 | 0 | if (!*ctx_) { |
1735 | | /* XXX: Would be nice if we have a numa specific xzalloc(). */ |
1736 | 0 | ctx = xzalloc(sizeof *ctx); |
1737 | 0 | ctx->core_id = core_id; |
1738 | 0 | ctx->numa_id = numa_id; |
1739 | 0 | *ctx_ = ctx; |
1740 | 0 | } else { |
1741 | 0 | ctx = *ctx_; |
1742 | 0 | old_n_nodes = ctx->n_nodes; |
1743 | |
|
1744 | 0 | if (old_n_nodes) { |
1745 | 0 | memcpy(old_nodes, ctx->nodes, old_n_nodes * sizeof old_nodes[0]); |
1746 | 0 | } |
1747 | | |
1748 | | /* Reset active nodes array. */ |
1749 | 0 | memset(ctx->nodes, 0, sizeof ctx->nodes); |
1750 | 0 | ctx->n_nodes = 0; |
1751 | 0 | } |
1752 | |
|
1753 | 0 | LIST_FOR_EACH (offload, dpif_list_node, &collection->list) { |
1754 | |
|
1755 | 0 | ovs_assert(ctx->n_nodes < TOTAL_PROVIDERS); |
1756 | |
|
1757 | 0 | if (!offload->class->pmd_thread_lifecycle) { |
1758 | 0 | continue; |
1759 | 0 | } |
1760 | | |
1761 | | /* If this is a reload, try to find previous context. */ |
1762 | 0 | for (size_t i = 0; i < old_n_nodes; i++) { |
1763 | 0 | struct dpif_offload_pmd_ctx_node *node = &old_nodes[i]; |
1764 | |
|
1765 | 0 | if (offload == node->offload) { |
1766 | 0 | ctx->nodes[ctx->n_nodes].provider_ctx = node->provider_ctx; |
1767 | 0 | break; |
1768 | 0 | } |
1769 | 0 | } |
1770 | |
|
1771 | 0 | offload->class->pmd_thread_lifecycle( |
1772 | 0 | offload, false, core_id, numa_id, |
1773 | 0 | &ctx->nodes[ctx->n_nodes].provider_ctx); |
1774 | |
|
1775 | 0 | if (ctx->nodes[ctx->n_nodes].provider_ctx) { |
1776 | 0 | ctx->nodes[ctx->n_nodes].offload = offload; |
1777 | 0 | ctx->n_nodes++; |
1778 | 0 | } |
1779 | 0 | } |
1780 | 0 | } |
1781 | | |
1782 | | void |
1783 | | dpif_offload_pmd_thread_exit(struct dpif_offload_pmd_ctx *ctx) |
1784 | 0 | { |
1785 | 0 | if (!ctx) { |
1786 | 0 | return; |
1787 | 0 | } |
1788 | | |
1789 | 0 | for (size_t i = 0; i < ctx->n_nodes; i++) { |
1790 | 0 | struct dpif_offload_pmd_ctx_node *node = &ctx->nodes[i]; |
1791 | |
|
1792 | 0 | node->offload->class->pmd_thread_lifecycle(node->offload, true, |
1793 | 0 | ctx->core_id, ctx->numa_id, |
1794 | 0 | &node->provider_ctx); |
1795 | 0 | } |
1796 | |
|
1797 | 0 | free(ctx); |
1798 | 0 | } |