Introduce an API in dpif-offload to dump all offloaded flows. This API centralizes flow retrieval and is intended to eventually replace the existing netdev-offload flow dump mechanism.
Signed-off-by: Eelco Chaudron <[email protected]> --- lib/dpctl.c | 8 +- lib/dpif-netdev.c | 7 +- lib/dpif-netlink.c | 9 +- lib/dpif-offload-provider.h | 72 ++++++++++++++ lib/dpif-offload-tc.c | 48 +++++++++ lib/dpif-offload.c | 188 ++++++++++++++++++++++++++++++++++++ lib/dpif-provider.h | 35 ++++++- lib/dpif.c | 26 ++++- lib/dpif.h | 2 +- 9 files changed, 373 insertions(+), 22 deletions(-) diff --git a/lib/dpctl.c b/lib/dpctl.c index 2a700f24a..1411c5a31 100644 --- a/lib/dpctl.c +++ b/lib/dpctl.c @@ -964,10 +964,10 @@ determine_dpif_flow_dump_types(struct dump_types *dump_types, struct dpif_flow_dump_types *dpif_dump_types) { dpif_dump_types->ovs_flows = dump_types->ovs || dump_types->non_offloaded; - dpif_dump_types->netdev_flows = dump_types->tc || dump_types->offloaded - || dump_types->non_offloaded - || dump_types->dpdk - || dump_types->partially_offloaded; + dpif_dump_types->offloaded_flows = dump_types->tc || dump_types->offloaded + || dump_types->non_offloaded + || dump_types->dpdk + || dump_types->partially_offloaded; } static bool diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 5d32a47b3..fe9c8b7be 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -4482,13 +4482,12 @@ dpif_netdev_flow_dump_cast(struct dpif_flow_dump *dump) static struct dpif_flow_dump * dpif_netdev_flow_dump_create(const struct dpif *dpif_, bool terse, - struct dpif_flow_dump_types *types OVS_UNUSED) + struct dpif_flow_dump_types *types) { struct dpif_netdev_flow_dump *dump; dump = xzalloc(sizeof *dump); - dpif_flow_dump_init(&dump->up, dpif_); - dump->up.terse = terse; + dpif_flow_dump_init(&dump->up, dpif_, terse, types); ovs_mutex_init(&dump->mutex); return &dump->up; @@ -4546,7 +4545,7 @@ dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread *thread_, = dpif_netdev_flow_dump_thread_cast(thread_); struct dpif_netdev_flow_dump *dump = thread->dump; struct dp_netdev_flow *netdev_flows[FLOW_DUMP_MAX_BATCH]; - struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dpif); + struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dump->dpif); struct dp_netdev *dp = get_dp_netdev(&dpif->dpif); int n_flows = 0; int i; diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index aff90976c..90e92d3a0 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -1621,7 +1621,7 @@ start_netdev_dump(const struct dpif *dpif_, { ovs_mutex_init(&dump->netdev_lock); - if (!(dump->types.netdev_flows)) { + if (!(dump->types.offloaded_flows)) { dump->netdev_dumps_num = 0; dump->netdev_dumps = NULL; return; @@ -1642,7 +1642,7 @@ dpif_netlink_populate_flow_dump_types(struct dpif_netlink_flow_dump *dump, { if (!types) { dump->types.ovs_flows = true; - dump->types.netdev_flows = true; + dump->types.offloaded_flows = true; } else { memcpy(&dump->types, types, sizeof *types); } @@ -1658,7 +1658,7 @@ dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse, struct ofpbuf *buf; dump = xmalloc(sizeof *dump); - dpif_flow_dump_init(&dump->up, dpif_); + dpif_flow_dump_init(&dump->up, dpif_, terse, types); dpif_netlink_populate_flow_dump_types(dump, types); @@ -1675,7 +1675,6 @@ dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse, ofpbuf_delete(buf); } atomic_init(&dump->status, 0); - dump->up.terse = terse; start_netdev_dump(dpif_, dump); @@ -1885,7 +1884,7 @@ dpif_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_, struct dpif_netlink_flow_dump_thread *thread = dpif_netlink_flow_dump_thread_cast(thread_); struct dpif_netlink_flow_dump *dump = thread->dump; - struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dpif); + struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dump->dpif); int n_flows; ofpbuf_delete(thread->nl_actions); diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h index 78cf5d7fa..070328893 100644 --- a/lib/dpif-offload-provider.h +++ b/lib/dpif-offload-provider.h @@ -59,6 +59,32 @@ struct dpif_offload { }; +struct dpif_offload_flow_dump { + struct dpif_offload *offload; + bool terse; +}; + +static inline void +dpif_offload_flow_dump_init(struct dpif_offload_flow_dump *dump, + const struct dpif_offload *offload, bool terse) +{ + dump->offload = CONST_CAST(struct dpif_offload *, offload); + dump->terse = terse; +} + +struct dpif_offload_flow_dump_thread { + struct dpif_offload_flow_dump *dump; +}; + +static inline void +dpif_offload_flow_dump_thread_init( + struct dpif_offload_flow_dump_thread *thread, + struct dpif_offload_flow_dump *dump) +{ + thread->dump = dump; +} + + struct dpif_offload_class { /* Type of DPIF offload provider in this class, e.g., "tc", "dpdk", * "dummy", etc. */ @@ -131,6 +157,44 @@ struct dpif_offload_class { * successful, otherwise returns a positive errno value. */ int (*flow_flush)(const struct dpif_offload *); + /* Flow Dumping Interface for dpif-offload. + * + * This interface mirrors the flow dumping interface found in the dpif + * layer. For a thorough understanding of the design and expectations, + * please refer to the documentation in: + * - include/openvswitch/dpif.h + * - include/openvswitch/dpif-provider.h + * + * The dpif-offload flow dumping interface is intended for use only when + * there is a clear separation between traditional dpif flows and offloaded + * flows handled by an offload mechanism. + * + * For example: + * - The 'tc' offload provider installs flow rules via kernel tc without + * creating corresponding kernel datapath (dpif) flows. In such cases, + * dumping dpif flows would not reflect the actual set of active + * offloaded flows. This interface provides a way to explicitly + * enumerate such offloaded flows. + * + * 'flow_dump_create' and 'flow_dump_thread_create' must always return + * initialized and usable data structures. Specifically, they must + * initialize the returned structures using dpif_offload_flow_dump_init() + * and dpif_offload_flow_dump_thread_init(), respectively, and defer any + * error reporting until flow_dump_destroy() is called. */ + struct dpif_offload_flow_dump *(*flow_dump_create)( + const struct dpif_offload *, bool terse); + + int (*flow_dump_next)(struct dpif_offload_flow_dump_thread *, + struct dpif_flow *, int max_flows); + + int (*flow_dump_destroy)(struct dpif_offload_flow_dump *); + + struct dpif_offload_flow_dump_thread *(*flow_dump_thread_create)( + struct dpif_offload_flow_dump *); + + void (*flow_dump_thread_destroy)( + struct dpif_offload_flow_dump_thread *); + /* Returns the number of flows offloaded by the offload provider. */ uint64_t (*flow_get_n_offloaded)(const struct dpif_offload *); @@ -237,6 +301,14 @@ void dpif_offload_port_del(struct dpif *, odp_port_t); void dpif_offload_port_set_config(struct dpif *, odp_port_t, const struct smap *cfg); void dpif_offload_set_netdev_offload(struct netdev *, struct dpif_offload *); +void dpif_offload_flow_dump_create(struct dpif_flow_dump *, + const struct dpif *, bool terse); +int dpif_offload_flow_dump_destroy(struct dpif_flow_dump *); +int dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *, + struct dpif_flow *, int max_flows); +void dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *, + struct dpif_flow_dump *); +void dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *); static inline void dpif_offload_assert_class( const struct dpif_offload *dpif_offload, diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c index 4ac6f359a..a0e6194a9 100644 --- a/lib/dpif-offload-tc.c +++ b/lib/dpif-offload-tc.c @@ -279,6 +279,49 @@ dpif_offload_tc_flow_flush(const struct dpif_offload *offload) return err; } +static struct dpif_offload_flow_dump * +dpif_offload_tc_flow_dump_create(const struct dpif_offload *offload, + bool terse) +{ + struct dpif_offload_flow_dump *dump; + + dump = xmalloc(sizeof *dump); + dpif_offload_flow_dump_init(dump, offload, terse); + return dump; +} + +static int +dpif_offload_tc_flow_dump_next(struct dpif_offload_flow_dump_thread *thread, + struct dpif_flow *flows, int max_flows) +{ + ovs_assert(thread && flows && max_flows); + return 0; +} + +static int +dpif_offload_tc_flow_dump_destroy(struct dpif_offload_flow_dump *dump) +{ + free(dump); + return 0; +} + +static struct dpif_offload_flow_dump_thread * +dpif_offload_tc_flow_dump_thread_create(struct dpif_offload_flow_dump *dump) +{ + struct dpif_offload_flow_dump_thread *thread; + + thread = xmalloc(sizeof *thread); + dpif_offload_flow_dump_thread_init(thread, dump); + return thread; +} + +static void +dpif_offload_tc_flow_dump_thread_destroy( + struct dpif_offload_flow_dump_thread *thread) +{ + free(thread); +} + struct dpif_offload_class dpif_offload_tc_class = { .type = "tc", .supported_dpif_types = (const char *const[]) { @@ -292,6 +335,11 @@ struct dpif_offload_class dpif_offload_tc_class = { .port_add = dpif_offload_tc_port_add, .port_del = dpif_offload_tc_port_del, .flow_flush = dpif_offload_tc_flow_flush, + .flow_dump_create = dpif_offload_tc_flow_dump_create, + .flow_dump_next = dpif_offload_tc_flow_dump_next, + .flow_dump_destroy = dpif_offload_tc_flow_dump_destroy, + .flow_dump_thread_create = dpif_offload_tc_flow_dump_thread_create, + .flow_dump_thread_destroy = dpif_offload_tc_flow_dump_thread_destroy, .flow_get_n_offloaded = dpif_offload_tc_flow_get_n_offloaded, .meter_set = dpif_offload_tc_meter_set, .meter_get = dpif_offload_tc_meter_get, diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c index 8ac0f8f96..2d170e1f1 100644 --- a/lib/dpif-offload.c +++ b/lib/dpif-offload.c @@ -160,6 +160,17 @@ dp_offload_initialize(void) && base_dpif_offload_classes[i]->port_add && base_dpif_offload_classes[i]->port_del); + ovs_assert((base_dpif_offload_classes[i]->flow_dump_create && + base_dpif_offload_classes[i]->flow_dump_next && + base_dpif_offload_classes[i]->flow_dump_destroy && + base_dpif_offload_classes[i]->flow_dump_thread_create && + base_dpif_offload_classes[i]->flow_dump_thread_destroy) || + (!base_dpif_offload_classes[i]->flow_dump_create && + !base_dpif_offload_classes[i]->flow_dump_next && + !base_dpif_offload_classes[i]->flow_dump_destroy && + !base_dpif_offload_classes[i]->flow_dump_thread_create && + !base_dpif_offload_classes[i]->flow_dump_thread_destroy)); + dpif_offload_register_provider(base_dpif_offload_classes[i]); } ovsthread_once_done(&once); @@ -832,6 +843,183 @@ dpif_offload_meter_del(const struct dpif *dpif, ofproto_meter_id meter_id, } } +/* + * Further initializes a 'struct dpif_flow_dump' that was already initialized + * by dpif_flow_dump_create(), preparing it for use by + * dpif_offload_flow_dump_next(). + * + * For more details, see the documentation of dpif_flow_dump_create(). */ +void +dpif_offload_flow_dump_create(struct dpif_flow_dump *dump, + const struct dpif *dpif, bool terse) +{ + struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif); + const struct dpif_offload *offload; + size_t n_providers = 0; + int i = 0; + + if (!dump || !dpif_offload_is_offload_enabled() || !dp_offload) { + return; + } + + LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) { + if (offload->class->flow_dump_create) { + n_providers++; + } + } + + if (!n_providers) { + return; + } + + dump->offload_dumps = xmalloc(n_providers * sizeof( + struct dpif_offload_flow_dump *)); + + LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) { + if (offload->class->flow_dump_create) { + dump->offload_dumps[i++] = offload->class->flow_dump_create( + offload, terse); + } + } + dump->n_offload_dumps = i; + dump->offload_dump_index = 0; +} + +/* Destroys the 'dump' data associated with the offload flow dump. + * This function is called as part of the general dump cleanup + * by dpif_flow_dump_destroy(). + * + * Returns 0 if all individual dpif-offload dump operations complete + * without error. If one or more providers return an error, the error + * code from the first failing provider is returned as a positive errno + * value. */ +int +dpif_offload_flow_dump_destroy(struct dpif_flow_dump *dump) +{ + int error = 0; + + for (int i = 0; i < dump->n_offload_dumps; i++) { + struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i]; + const struct dpif_offload *offload = offload_dump->offload; + int rc = offload->class->flow_dump_destroy(offload_dump); + + if (rc && rc != EOF) { + VLOG_ERR("Failed flow dumping on dpif-offload provider " + "%s, error %s", dpif_offload_name(offload), + ovs_strerror(rc)); + if (!error) { + error = rc; + } + } + } + ovs_mutex_destroy(&dump->offload_dump_mutex); + free(dump->offload_dumps); + return error; +} + +static void +dpif_offload_advance_provider_dump(struct dpif_flow_dump_thread *thread) +{ + struct dpif_flow_dump *dump = thread->dump; + + ovs_mutex_lock(&dump->offload_dump_mutex); + + /* If we haven't finished (dumped all providers). */ + if (dump->offload_dump_index < dump->n_offload_dumps) { + /* If we are the first to find that current dump is finished + * advance it. */ + if (thread->offload_dump_index == dump->offload_dump_index) { + thread->offload_dump_index = ++dump->offload_dump_index; + /* Did we just finish the last dump? If so we are done. */ + if (dump->offload_dump_index == dump->n_offload_dumps) { + thread->offload_dump_done = true; + } + } else { + /* otherwise, we are behind, catch up */ + thread->offload_dump_index = dump->offload_dump_index; + } + } else { + /* Some other thread finished. */ + thread->offload_dump_done = true; + } + + ovs_mutex_unlock(&dump->offload_dump_mutex); +} + +/* This function behaves exactly the same as dpif_flow_dump_next(), + * so see its documentation for details. */ +int dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *thread, + struct dpif_flow *flows, int max_flows) +{ + int n_flows = 0; + + ovs_assert(max_flows > 0); + + /* The logic here processes all registered offload providers and + * dumps all related flows. If done (i.e., it returns 0), continue + * with the next offload provider. */ + while (!thread->offload_dump_done) { + struct dpif_offload_flow_dump_thread *offload_thread; + + ovs_assert(thread->offload_dump_index < thread->n_offload_threads); + offload_thread = thread->offload_threads[thread->offload_dump_index]; + n_flows = offload_thread->dump->offload->class->flow_dump_next( + offload_thread, flows, max_flows); + + if (n_flows > 0) { + /* If we got some flows, we need to return due to the constraint + * on returned flows, as explained in dpif_flow_dump_next(). */ + break; + } + dpif_offload_advance_provider_dump(thread); + } + return MAX(n_flows, 0); +} + +/* Further initializes a 'struct dpif_flow_dump_thread' that was already + * initialized by dpif_flow_dump_thread_create(), preparing it for use by + * dpif_offload_flow_dump_next(). */ +void +dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *thread, + struct dpif_flow_dump *dump) +{ + if (!dpif_offload_is_offload_enabled() || !dump + || !dump->n_offload_dumps) { + return; + } + + thread->n_offload_threads = dump->n_offload_dumps; + thread->offload_dump_done = false; + thread->offload_dump_index = 0; + thread->offload_threads = xmalloc(thread->n_offload_threads * sizeof( + struct dpif_offload_flow_dump_thread *)); + + for (int i = 0; i < dump->n_offload_dumps; i++) { + struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i]; + const struct dpif_offload *offload = offload_dump->offload; + + thread->offload_threads[i] = offload->class->flow_dump_thread_create( + offload_dump); + } +} + +/* Destroys the 'thread' data associated with the offload flow dump. + * This function is called as part of the general thread cleanup + * by dpif_flow_dump_thread_destroy(). */ +void +dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread) +{ + for (int i = 0; i < thread->n_offload_threads; i++) { + struct dpif_offload_flow_dump_thread *offload_thread; + const struct dpif_offload *offload; + + offload_thread = thread->offload_threads[i]; + offload = offload_thread->dump->offload; + offload->class->flow_dump_thread_destroy(offload_thread); + } + free(thread->offload_threads); +} + int dpif_offload_netdev_flush_flows(struct netdev *netdev) diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index e99807782..91e7ddcfa 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -65,23 +65,52 @@ static inline void dpif_assert_class(const struct dpif *dpif, struct dpif_flow_dump { struct dpif *dpif; bool terse; /* If true, key/mask/actions may be omitted. */ + + struct ovs_mutex offload_dump_mutex; + struct dpif_offload_flow_dump **offload_dumps; + size_t n_offload_dumps; + size_t offload_dump_index; }; +void dpif_offload_flow_dump_create(struct dpif_flow_dump *, + const struct dpif *, bool terse); +void dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *, + struct dpif_flow_dump *); + static inline void -dpif_flow_dump_init(struct dpif_flow_dump *dump, const struct dpif *dpif) +dpif_flow_dump_init(struct dpif_flow_dump *dump, const struct dpif *dpif, + bool terse, struct dpif_flow_dump_types *types) { dump->dpif = CONST_CAST(struct dpif *, dpif); + dump->terse = terse; + dump->offload_dumps = NULL; + dump->n_offload_dumps = 0; + dump->offload_dump_index = 0; + ovs_mutex_init(&dump->offload_dump_mutex); + if (!types || types->offloaded_flows) { + dpif_offload_flow_dump_create(dump, dpif, terse); + } } struct dpif_flow_dump_thread { - struct dpif *dpif; + struct dpif_flow_dump *dump; + + struct dpif_offload_flow_dump_thread **offload_threads; + size_t n_offload_threads; + size_t offload_dump_index; + bool offload_dump_done; }; static inline void dpif_flow_dump_thread_init(struct dpif_flow_dump_thread *thread, struct dpif_flow_dump *dump) { - thread->dpif = dump->dpif; + thread->dump = dump; + thread->offload_threads = NULL; + thread->n_offload_threads = 0; + thread->offload_dump_index = 0; + thread->offload_dump_done = true; + dpif_offload_flow_dump_thread_create(thread, dump); } struct ct_dpif_dump_state; diff --git a/lib/dpif.c b/lib/dpif.c index 6e8857144..5319d6b85 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1115,7 +1115,15 @@ int dpif_flow_dump_destroy(struct dpif_flow_dump *dump) { const struct dpif *dpif = dump->dpif; - int error = dpif->dpif_class->flow_dump_destroy(dump); + int error; + int offload_error; + + offload_error = dpif_offload_flow_dump_destroy(dump); + error = dpif->dpif_class->flow_dump_destroy(dump); + + if (!error || error == EOF) { + error = offload_error; + } log_operation(dpif, "flow_dump_destroy", error); return error == EOF ? 0 : error; } @@ -1131,7 +1139,8 @@ dpif_flow_dump_thread_create(struct dpif_flow_dump *dump) void dpif_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread) { - thread->dpif->dpif_class->flow_dump_thread_destroy(thread); + dpif_offload_flow_dump_thread_destroy(thread); + thread->dump->dpif->dpif_class->flow_dump_thread_destroy(thread); } /* Attempts to retrieve up to 'max_flows' more flows from 'thread'. Returns 0 @@ -1156,11 +1165,18 @@ int dpif_flow_dump_next(struct dpif_flow_dump_thread *thread, struct dpif_flow *flows, int max_flows) { - struct dpif *dpif = thread->dpif; - int n; + struct dpif *dpif = thread->dump->dpif; + int n = 0; ovs_assert(max_flows > 0); - n = dpif->dpif_class->flow_dump_next(thread, flows, max_flows); + + if (!thread->offload_dump_done) { + n = dpif_offload_flow_dump_next(thread, flows, max_flows); + } + if (n == 0) { + n = dpif->dpif_class->flow_dump_next(thread, flows, max_flows); + } + if (n > 0) { struct dpif_flow *f; diff --git a/lib/dpif.h b/lib/dpif.h index 7fd3c939c..473eacb2f 100644 --- a/lib/dpif.h +++ b/lib/dpif.h @@ -522,7 +522,7 @@ struct dpif_flow_attrs { struct dpif_flow_dump_types { bool ovs_flows; - bool netdev_flows; + bool offloaded_flows; }; void dpif_flow_stats_extract(const struct flow *, const struct dp_packet *packet, -- 2.50.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
