On 1/12/26 12:20 PM, Eelco Chaudron wrote:
> This patch introduces new APIs in dpif-offload to allow userspace
> datapaths to directly manage flows in the offloaded datapath,
> providing efficient fast-path operations.
>
> Acked-by: Eli Britstein <elibr.nvidia.com>
> Signed-off-by: Eelco Chaudron <[email protected]>
> ---
> lib/dp-packet.h | 1 +
> lib/dpif-offload-provider.h | 26 ++++++-
> lib/dpif-offload.c | 131 +++++++++++++++++++++++++++++++++---
> lib/dpif-offload.h | 54 +++++++++++++++
> 4 files changed, 201 insertions(+), 11 deletions(-)
>
> diff --git a/lib/dp-packet.h b/lib/dp-packet.h
> index 6a4a61922..d92b9d473 100644
> --- a/lib/dp-packet.h
> +++ b/lib/dp-packet.h
> @@ -48,6 +48,7 @@ enum OVS_PACKED_ENUM dp_packet_source {
> };
>
> #define DP_PACKET_CONTEXT_SIZE 64
> +#define INVALID_FLOW_MARK 0
>
> /* Bit masks for the 'offloads' member of the 'dp_packet' structure. */
> enum OVS_PACKED_ENUM dp_packet_offload_mask {
> diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
> index 631a213a5..53bd1328d 100644
> --- a/lib/dpif-offload-provider.h
> +++ b/lib/dpif-offload-provider.h
> @@ -292,8 +292,32 @@ struct dpif_offload_class {
> * fully consumed by the provider for non-error conditions. */
> int (*netdev_hw_post_process)(const struct dpif_offload *, struct netdev
> *,
> struct dp_packet *);
> -};
>
> + /* Add or modify the specified flow directly in the offload datapath.
> + * The actual implementation may choose to handle the offload
> + * asynchronously by returning EINPROGRESS and invoking the supplied
> + * 'callback' once completed. For successful synchronous handling, the
> + * callback must not be called, and 0 should be returned. If this call
> is
> + * not successful, a positive errno value should be returned. */
> + int (*netdev_flow_put)(const struct dpif_offload *, struct netdev *,
> + struct dpif_offload_flow_put *,
> + uint32_t *flow_mark);
> +
> + /* Delete the specified flow directly from the offloaded datapath. See
> the
> + * above 'netdev_flow_put' for implementation details. */
> + int (*netdev_flow_del)(const struct dpif_offload *, struct netdev *,
> + struct dpif_offload_flow_del *,
> + uint32_t *flow_mark);
> +
> + /* Get offload statistics based on the flows 'ufid'. Note that this API
> + * does NOT support asynchronous handling. Returns 'true' if the flow
> was
> + * offloaded, 'false' if not. In the latter case, 'stats' and 'attrs'
> + * are not valid. */
> + bool (*netdev_flow_stats)(const struct dpif_offload *, struct netdev *,
> + const ovs_u128 *ufid,
> + struct dpif_flow_stats *stats,
> + struct dpif_flow_attrs *attrs);
> +};
>
> extern struct dpif_offload_class dpif_offload_dummy_class;
> extern struct dpif_offload_class dpif_offload_dummy_x_class;
> diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
> index be274a954..b670ddee5 100644
> --- a/lib/dpif-offload.c
> +++ b/lib/dpif-offload.c
> @@ -1204,19 +1204,14 @@ dpif_offload_offload_get_netdev_by_port_id(struct
> dpif_offload *offload,
> return offload->class->get_netdev(offload, port_no);
> }
>
> -struct netdev *
> -dpif_offload_get_netdev_by_port_id(struct dpif *dpif,
> - struct dpif_offload **offload,
> - odp_port_t port_no)
> +static struct netdev *
> +dpif_offload_get_netdev_by_port_id_(struct dp_offload *dp_offload,
nit: Double underscore.
> + struct dpif_offload **offload,
> + odp_port_t port_no)
> {
> - struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> struct dpif_offload *tmp_offload;
> struct netdev *netdev = NULL;
>
> - if (!dp_offload || !dpif_offload_is_offload_enabled()) {
> - return NULL;
> - }
> -
> LIST_FOR_EACH (tmp_offload, dpif_list_node,
> &dp_offload->offload_providers) {
> netdev = tmp_offload->class->get_netdev(tmp_offload, port_no);
> @@ -1227,10 +1222,23 @@ dpif_offload_get_netdev_by_port_id(struct dpif *dpif,
> break;
> }
> }
> -
> return netdev;
> }
>
> +struct netdev *
> +dpif_offload_get_netdev_by_port_id(struct dpif *dpif,
> + struct dpif_offload **offload,
> + odp_port_t port_no)
> +{
> + struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> +
> + if (!dp_offload || !dpif_offload_is_offload_enabled()) {
> + return NULL;
> + }
> +
> + return dpif_offload_get_netdev_by_port_id_(dp_offload, offload, port_no);
> +}
> +
> bool
> dpif_offload_netdevs_out_of_resources(struct dpif *dpif)
> {
> @@ -1362,6 +1370,109 @@ dpif_offload_netdev_flush_flows(struct netdev *netdev)
> return EOPNOTSUPP;
> }
>
> +int
> +dpif_offload_datapath_flow_put(const char *dpif_name,
> + struct dpif_offload_flow_put *put,
> + uint32_t *flow_mark)
> +{
> + struct dpif_offload *offload;
> + struct dp_offload *dp_offload;
> + struct netdev *netdev;
> +
> + ovs_mutex_lock(&dpif_offload_mutex);
> + /* XXX: Implement a faster solution than the current dpif_name lookup. */
> + dp_offload = shash_find_data(&dpif_offload_providers, dpif_name);
> + ovs_mutex_unlock(&dpif_offload_mutex);
> +
> + if (OVS_UNLIKELY(!dp_offload)) {
> + if (flow_mark) {
> + *flow_mark = INVALID_FLOW_MARK;
> + }
> + return 0;
> + }
> +
> + netdev = dpif_offload_get_netdev_by_port_id_(dp_offload, &offload,
> + put->in_port);
> +
> + if (OVS_LIKELY(netdev && offload->class->netdev_flow_put)) {
> + return offload->class->netdev_flow_put(offload, netdev, put,
> + flow_mark);
> + }
> +
> + if (flow_mark) {
> + *flow_mark = INVALID_FLOW_MARK;
> + }
> + return 0;
> +}
> +
> +int
> +dpif_offload_datapath_flow_del(const char *dpif_name,
> + struct dpif_offload_flow_del *del,
> + uint32_t *flow_mark)
> +{
> + struct dpif_offload *offload;
> + struct dp_offload *dp_offload;
> + struct netdev *netdev;
> +
> + ovs_mutex_lock(&dpif_offload_mutex);
> + /* XXX: Implement a faster solution than the current dpif_name lookup. */
> + dp_offload = shash_find_data(&dpif_offload_providers, dpif_name);
> + ovs_mutex_unlock(&dpif_offload_mutex);
> +
> + if (OVS_UNLIKELY(!dp_offload)) {
> + if (flow_mark) {
> + *flow_mark = INVALID_FLOW_MARK;
> + }
> + return 0;
> + }
> +
> + netdev = dpif_offload_get_netdev_by_port_id_(dp_offload, &offload,
> + del->in_port);
> +
> + if (OVS_LIKELY(netdev && offload->class->netdev_flow_del)) {
> + return offload->class->netdev_flow_del(offload, netdev, del,
> + flow_mark);
> + }
> +
> + if (flow_mark) {
> + *flow_mark = INVALID_FLOW_MARK;
> + }
> + return 0;
> +}
> +
> +bool
> +dpif_offload_datapath_flow_stats(const char *dpif_name, odp_port_t in_port,
> + const ovs_u128 *ufid,
> + struct dpif_flow_stats *stats,
> + struct dpif_flow_attrs *attrs)
> +{
> + struct dpif_offload *offload;
> + struct dp_offload *dp_offload;
> + struct netdev *netdev;
> +
> + if (!dpif_offload_is_offload_enabled()) {
> + return false;
> + }
> +
> + ovs_mutex_lock(&dpif_offload_mutex);
> + /* XXX: Implement a faster solution than the current dpif_name lookup. */
> + dp_offload = shash_find_data(&dpif_offload_providers, dpif_name);
> + ovs_mutex_unlock(&dpif_offload_mutex);
> +
> + if (OVS_UNLIKELY(!dp_offload)) {
> + return false;
> + }
> +
> + netdev = dpif_offload_get_netdev_by_port_id_(dp_offload, &offload,
> + in_port);
> +
> + if (OVS_LIKELY(netdev && offload->class->netdev_flow_stats)) {
> + return offload->class->netdev_flow_stats(offload, netdev, ufid,
> stats,
> + attrs);
> + }
> + return false;
> +}
> +
> int
> dpif_offload_netdev_hw_post_process(struct netdev *netdev,
> struct dp_packet *packet)
> diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
> index 53569822e..f246d778b 100644
> --- a/lib/dpif-offload.h
> +++ b/lib/dpif-offload.h
> @@ -141,4 +141,58 @@ bool dpif_offload_netdev_same_offload(const struct
> netdev *,
> int dpif_offload_netdev_flush_flows(struct netdev *);
> int dpif_offload_netdev_hw_post_process(struct netdev *, struct dp_packet *);
>
> +
> +/* Flow modification callback definitions. */
This mechanism of callbacks can use a some documentation in the comments,
it is not obvious how it supposed to work.
> +typedef void dpif_offload_flow_op_cb(void *aux_dp, void *aux_flow,
> + struct dpif_flow_stats *stats,
> + uint32_t flow_mark, int error);
> +
> +/* Supporting structures for flow modification functions. */
> +struct dpif_offload_flow_cb_data {
> + dpif_offload_flow_op_cb *callback;
> + void *callback_aux_dp;
> + void *callback_aux_flow;
> +};
> +
> +struct dpif_offload_flow_put {
> + bool modify;
> + odp_port_t in_port;
> + odp_port_t orig_in_port; /* Originating in_port for tunneled packets. */
> + const ovs_u128 *ufid;
> + struct match *match;
> + const struct nlattr *actions;
> + size_t actions_len;
> + struct dpif_flow_stats *stats;
> + struct dpif_offload_flow_cb_data cb_data;
> +};
> +
> +struct dpif_offload_flow_del {
> + odp_port_t in_port;
> + const ovs_u128 *ufid;
> + struct dpif_flow_stats *stats;
> + struct dpif_offload_flow_cb_data cb_data;
> +};
> +
> +/* Flow modification functions, which can be used in the fast path. */
Do these need 'datapath' in their names?
> +int dpif_offload_datapath_flow_put(const char *dpif_name,
> + struct dpif_offload_flow_put *,
> + uint32_t *flow_mark);
> +int dpif_offload_datapath_flow_del(const char *dpif_name,
> + struct dpif_offload_flow_del *,
> + uint32_t *flow_mark);
> +bool dpif_offload_datapath_flow_stats(const char *dpif_name,
> + odp_port_t in_port, const ovs_u128
> *ufid,
> + struct dpif_flow_stats *,
> + struct dpif_flow_attrs *);
> +
> +static inline void dpif_offload_datapath_flow_op_continue(
> + struct dpif_offload_flow_cb_data *cb, struct dpif_flow_stats *stats,
> + uint32_t flow_mark, int error)
> +{
> + if (cb && cb->callback) {
> + cb->callback(cb->callback_aux_dp, cb->callback_aux_flow,
> + stats, flow_mark, error);
> + }
> +}
> +
> #endif /* DPIF_OFFLOAD_H */
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev