@@ -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
@@ -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;
@@ -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);
@@ -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,
@@ -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,
@@ -153,6 +153,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);
@@ -821,6 +832,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)
@@ -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;
@@ -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;
@@ -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,
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 <echaudro@redhat.com> --- 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(-)