Implement driver callbacks for the following rte flow operations:
create, destroy, and flush. This change enables receive flow steering
(RFS) for n-tuple based flow rules for the gve driver.

The implementation supports matching ingress IPv4/IPv6 traffic combined
with TCP, UDP, SCTP, ESP, or AH protocols. Supported fields for
matching include IP source/destination addresses, L4 source/destination
ports (for TCP/UDP/SCTP), and SPI (for ESP/AH). The only supported
action is RTE_FLOW_ACTION_TYPE_QUEUE, which steers matching packets to
a specified rx queue.

Co-developed-by: Vee Agarwal <[email protected]>
Signed-off-by: Vee Agarwal <[email protected]>
Signed-off-by: Jasper Tran O'Leary <[email protected]>
---
 doc/guides/nics/features/gve.ini       |  12 +
 doc/guides/nics/gve.rst                |  20 +
 doc/guides/rel_notes/release_26_03.rst |   1 +
 drivers/net/gve/base/gve.h             |   3 +-
 drivers/net/gve/gve_ethdev.c           |  87 +++-
 drivers/net/gve/gve_ethdev.h           |  43 ++
 drivers/net/gve/gve_flow_rule.c        | 645 +++++++++++++++++++++++++
 drivers/net/gve/gve_flow_rule.h        |   5 +
 drivers/net/gve/meson.build            |   1 +
 9 files changed, 815 insertions(+), 2 deletions(-)
 create mode 100644 dpdk/drivers/net/gve/gve_flow_rule.c

diff --git a/doc/guides/nics/features/gve.ini b/doc/guides/nics/features/gve.ini
index ed040a0..89c97fd 100644
--- a/doc/guides/nics/features/gve.ini
+++ b/doc/guides/nics/features/gve.ini
@@ -19,3 +19,15 @@ Linux                = Y
 x86-32               = Y
 x86-64               = Y
 Usage doc            = Y
+
+[rte_flow items]
+ah                   = Y
+esp                  = Y
+ipv4                 = Y
+ipv6                 = Y
+sctp                 = Y
+tcp                  = Y
+udp                  = Y
+
+[rte_flow actions]
+queue                = Y
diff --git a/doc/guides/nics/gve.rst b/doc/guides/nics/gve.rst
index 6b4d1f7..59e0066 100644
--- a/doc/guides/nics/gve.rst
+++ b/doc/guides/nics/gve.rst
@@ -103,6 +103,26 @@ the redirection table will be available for querying upon 
initial hash configura
 When performing redirection table updates,
 it is possible to update individual table entries.
 
+Flow Steering
+^^^^^^^^^^^^^
+
+The driver supports receive flow steering (RFS) via the standard ``rte_flow``
+API. This allows applications to steer traffic to specific queues based on
+5-tuple matching. 3-tuple matching may be supported in future releases.
+
+Supported Patterns:
+  - IPv4/IPv6 source and destination addresses.
+  - TCP/UDP/SCTP source and destination ports.
+  - ESP/AH SPI.
+
+Supported Actions:
+  - ``RTE_FLOW_ACTION_TYPE_QUEUE``: Steer packets to a specific Rx queue.
+
+Limitations:
+  - Only ingress flow rules are supported.
+  - Flow priorities are not supported (must be 0).
+  - Masking is limited to full matches i.e. 0x00...0 or 0xFF...F.
+
 Application-Initiated Reset
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^

 The driver allows an application to reset the gVNIC device.
diff --git a/doc/guides/rel_notes/release_26_03.rst 
b/doc/guides/rel_notes/release_26_03.rst
index 1855d90..e45ed27 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -78,6 +78,7 @@ New Features
 * **Updated Google Virtual Ethernet (gve) driver.**
 
   * Added application-initiated device reset.
+  * Add support for receive flow steering.
 
 * **Updated Intel iavf driver.**
 
diff --git a/drivers/net/gve/base/gve.h b/drivers/net/gve/base/gve.h
index 99514cb..18363fa 100644
--- a/drivers/net/gve/base/gve.h
+++ b/drivers/net/gve/base/gve.h
@@ -50,7 +50,8 @@ enum gve_state_flags_bit {
        GVE_PRIV_FLAGS_ADMIN_QUEUE_OK           = 1,
        GVE_PRIV_FLAGS_DEVICE_RESOURCES_OK      = 2,
        GVE_PRIV_FLAGS_DEVICE_RINGS_OK          = 3,
-       GVE_PRIV_FLAGS_NAPI_ENABLED             = 4,
+       GVE_PRIV_FLAGS_FLOW_SUBSYSTEM_OK        = 4,
+       GVE_PRIV_FLAGS_NAPI_ENABLED             = 5,
 };
 
 enum gve_rss_hash_algorithm {
diff --git a/drivers/net/gve/gve_ethdev.c b/drivers/net/gve/gve_ethdev.c
index 5912fec..0d4caab 100644
--- a/drivers/net/gve/gve_ethdev.c
+++ b/drivers/net/gve/gve_ethdev.c
@@ -510,6 +510,57 @@ gve_free_ptype_lut_dqo(struct gve_priv *priv)
        }
 }
 
+static void
+gve_flow_free_bmp(struct gve_priv *priv)
+{
+       rte_free(priv->avail_flow_rule_bmp_mem);
+       priv->avail_flow_rule_bmp_mem = NULL;
+       priv->avail_flow_rule_bmp = NULL;
+}
+
+static int
+gve_setup_flow_subsystem(struct gve_priv *priv)
+{
+       int err;
+
+       priv->flow_rule_bmp_size =
+                       rte_bitmap_get_memory_footprint(priv->max_flow_rules);
+       priv->avail_flow_rule_bmp_mem = rte_zmalloc("gve_flow_rule_bmp",
+                       priv->flow_rule_bmp_size, 0);
+       if (!priv->avail_flow_rule_bmp_mem) {
+               PMD_DRV_LOG(ERR, "Failed to alloc bitmap for flow rules.");
+               err = -ENOMEM;
+               goto free_flow_rule_bmp;
+       }
+
+       err = gve_flow_init_bmp(priv);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Failed to initialize flow rule bitmap.");
+               goto free_flow_rule_bmp;
+       }
+
+       TAILQ_INIT(&priv->active_flows);
+       gve_set_flow_subsystem_ok(priv);
+
+       return 0;
+
+free_flow_rule_bmp:
+       gve_flow_free_bmp(priv);
+       return err;
+}
+
+static void
+gve_teardown_flow_subsystem(struct gve_priv *priv)
+{
+       pthread_mutex_lock(&priv->flow_rule_lock);
+
+       gve_clear_flow_subsystem_ok(priv);
+       gve_flow_free_bmp(priv);
+       gve_free_flow_rules(priv);
+
+       pthread_mutex_unlock(&priv->flow_rule_lock);
+}
+
 static void
 gve_teardown_device_resources(struct gve_priv *priv)
 {
@@ -519,7 +570,9 @@ gve_teardown_device_resources(struct gve_priv *priv)
        if (gve_get_device_resources_ok(priv)) {
                err = gve_adminq_deconfigure_device_resources(priv);
                if (err)
-                       PMD_DRV_LOG(ERR, "Could not deconfigure device 
resources: err=%d", err);
+                       PMD_DRV_LOG(ERR,
+                               "Could not deconfigure device resources: 
err=%d",
+                               err);
        }
 
        gve_free_ptype_lut_dqo(priv);
@@ -543,6 +596,11 @@ gve_dev_close(struct rte_eth_dev *dev)
                        PMD_DRV_LOG(ERR, "Failed to stop dev.");
        }
 
+       if (gve_get_flow_subsystem_ok(priv))
+               gve_teardown_flow_subsystem(priv);
+
+       pthread_mutex_destroy(&priv->flow_rule_lock);
+
        gve_free_queues(dev);
        gve_teardown_device_resources(priv);
        gve_adminq_free(priv);
@@ -566,6 +624,9 @@ gve_dev_reset(struct rte_eth_dev *dev)
        }
 
        /* Tear down all device resources before re-initializing. */
+       if (gve_get_flow_subsystem_ok(priv))
+               gve_teardown_flow_subsystem(priv);
+
        gve_free_queues(dev);
        gve_teardown_device_resources(priv);
        gve_adminq_free(priv);
@@ -1094,6 +1155,18 @@ gve_rss_reta_query(struct rte_eth_dev *dev,
        return 0;
 }
 
+static int
+gve_flow_ops_get(struct rte_eth_dev *dev, const struct rte_flow_ops **ops)
+{
+       struct gve_priv *priv = dev->data->dev_private;
+
+       if (!gve_get_flow_subsystem_ok(priv))
+               return -ENOTSUP;
+
+       *ops = &gve_flow_ops;
+       return 0;
+}
+
 static const struct eth_dev_ops gve_eth_dev_ops = {
        .dev_configure        = gve_dev_configure,
        .dev_start            = gve_dev_start,
@@ -1109,6 +1182,7 @@ static const struct eth_dev_ops gve_eth_dev_ops = {
        .tx_queue_start       = gve_tx_queue_start,
        .rx_queue_stop        = gve_rx_queue_stop,
        .tx_queue_stop        = gve_tx_queue_stop,
+       .flow_ops_get         = gve_flow_ops_get,
        .link_update          = gve_link_update,
        .stats_get            = gve_dev_stats_get,
        .stats_reset          = gve_dev_stats_reset,
@@ -1136,6 +1210,7 @@ static const struct eth_dev_ops gve_eth_dev_ops_dqo = {
        .tx_queue_start       = gve_tx_queue_start_dqo,
        .rx_queue_stop        = gve_rx_queue_stop_dqo,
        .tx_queue_stop        = gve_tx_queue_stop_dqo,
+       .flow_ops_get         = gve_flow_ops_get,
        .link_update          = gve_link_update,
        .stats_get            = gve_dev_stats_get,
        .stats_reset          = gve_dev_stats_reset,
@@ -1303,6 +1378,14 @@ gve_init_priv(struct gve_priv *priv, bool 
skip_describe_device)
                    priv->max_nb_txq, priv->max_nb_rxq);
 
 setup_device:
+       if (priv->max_flow_rules) {
+               err = gve_setup_flow_subsystem(priv);
+               if (err)
+                       PMD_DRV_LOG(WARNING,
+                                   "Failed to set up flow subsystem: err=%d, 
flow steering will be disabled.",
+                                   err);
+       }
+
        err = gve_setup_device_resources(priv);
        if (!err)
                return 0;
@@ -1377,6 +1460,8 @@ gve_dev_init(struct rte_eth_dev *eth_dev)
 
        eth_dev->data->mac_addrs = &priv->dev_addr;
 
+       pthread_mutex_init(&priv->flow_rule_lock, NULL);
+
        return 0;
 }
 
diff --git a/drivers/net/gve/gve_ethdev.h b/drivers/net/gve/gve_ethdev.h
index 4e07ca8..2d570d0 100644
--- a/drivers/net/gve/gve_ethdev.h
+++ b/drivers/net/gve/gve_ethdev.h
@@ -9,6 +9,8 @@
 #include <ethdev_pci.h>
 #include <rte_ether.h>
 #include <rte_pci.h>
+#include <pthread.h>
+#include <rte_bitmap.h>
 
 #include "base/gve.h"
 
@@ -252,6 +254,13 @@ struct gve_rx_queue {
        uint8_t is_gqi_qpl;
 };
 
+struct gve_flow {
+       uint32_t rule_id;
+       TAILQ_ENTRY(gve_flow) list_handle;
+};
+
+extern const struct rte_flow_ops gve_flow_ops;
+
 struct gve_priv {
        struct gve_irq_db *irq_dbs; /* array of num_ntfy_blks */
        const struct rte_memzone *irq_dbs_mz;
@@ -334,7 +343,13 @@ struct gve_priv {
        struct gve_rss_config rss_config;
        struct gve_ptype_lut *ptype_lut_dqo;
 
+       /* Flow rule management */
        uint32_t max_flow_rules;
+       uint32_t flow_rule_bmp_size;
+       struct rte_bitmap *avail_flow_rule_bmp; /* Tracks available rule IDs (1 
= available) */
+       void *avail_flow_rule_bmp_mem; /* Backing memory for the bitmap */
+       pthread_mutex_t flow_rule_lock; /* Lock for bitmap and tailq access */
+       TAILQ_HEAD(, gve_flow) active_flows;
 };
 
 static inline bool
@@ -407,6 +422,34 @@ gve_clear_device_rings_ok(struct gve_priv *priv)
                                &priv->state_flags);
 }
 
+static inline bool
+gve_get_flow_subsystem_ok(struct gve_priv *priv)
+{
+       bool ret;
+
+       ret = !!rte_bit_relaxed_get32(GVE_PRIV_FLAGS_FLOW_SUBSYSTEM_OK,
+                                     &priv->state_flags);
+       rte_atomic_thread_fence(rte_memory_order_acquire);
+
+       return ret;
+}
+
+static inline void
+gve_set_flow_subsystem_ok(struct gve_priv *priv)
+{
+       rte_atomic_thread_fence(rte_memory_order_release);
+       rte_bit_relaxed_set32(GVE_PRIV_FLAGS_FLOW_SUBSYSTEM_OK,
+                             &priv->state_flags);
+}
+
+static inline void
+gve_clear_flow_subsystem_ok(struct gve_priv *priv)
+{
+       rte_atomic_thread_fence(rte_memory_order_release);
+       rte_bit_relaxed_clear32(GVE_PRIV_FLAGS_FLOW_SUBSYSTEM_OK,
+                               &priv->state_flags);
+}
+
 int
 gve_rx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id, uint16_t 
nb_desc,
                   unsigned int socket_id, const struct rte_eth_rxconf *conf,
diff --git a/drivers/net/gve/gve_flow_rule.c b/drivers/net/gve/gve_flow_rule.c
new file mode 100644
index 0000000..fae5edf
--- /dev/null
+++ b/drivers/net/gve/gve_flow_rule.c
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Google LLC
+ */
+
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+#include "base/gve_adminq.h"
+#include "gve_ethdev.h"
+
+static int
+gve_validate_flow_attr(const struct rte_flow_attr *attr,
+                      struct rte_flow_error *error)
+{
+       if (!attr) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ATTR, NULL,
+                               "Invalid flow attribute");
+               return -EINVAL;
+       }
+       if (attr->egress || attr->transfer) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ATTR, attr,
+                               "Only ingress is supported");
+               return -EINVAL;
+       }
+       if (!attr->ingress) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr,
+                               "Ingress attribute must be set");
+               return -EINVAL;
+       }
+       if (attr->priority != 0) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr,
+                               "Priority levels are not supported");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void
+gve_parse_ipv4(const struct rte_flow_item *item,
+              struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_ipv4 *spec = item->spec;
+               const struct rte_flow_item_ipv4 *mask =
+                       item->mask ? item->mask : &rte_flow_item_ipv4_mask;
+
+               rule->key.src_ip[0] = spec->hdr.src_addr;
+               rule->key.dst_ip[0] = spec->hdr.dst_addr;
+               rule->mask.src_ip[0] = mask->hdr.src_addr;
+               rule->mask.dst_ip[0] = mask->hdr.dst_addr;
+       }
+}
+
+static void
+gve_parse_ipv6(const struct rte_flow_item *item,
+              struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_ipv6 *spec = item->spec;
+               const struct rte_flow_item_ipv6 *mask =
+                       item->mask ? item->mask : &rte_flow_item_ipv6_mask;
+               const __be32 *src_ip = (const __be32 *)&spec->hdr.src_addr;
+               const __be32 *src_mask = (const __be32 *)&mask->hdr.src_addr;
+               const __be32 *dst_ip = (const __be32 *)&spec->hdr.dst_addr;
+               const __be32 *dst_mask = (const __be32 *)&mask->hdr.dst_addr;
+               int i;
+
+               /*
+                * The device expects IPv6 addresses as an array of 4 32-bit 
words
+                * in reverse word order (the MSB word at index 3 and the LSB 
word
+                * at index 0). We must reverse the DPDK network byte order 
array.
+                */
+               for (i = 0; i < 4; i++) {
+                       rule->key.src_ip[3 - i] = src_ip[i];
+                       rule->key.dst_ip[3 - i] = dst_ip[i];
+                       rule->mask.src_ip[3 - i] = src_mask[i];
+                       rule->mask.dst_ip[3 - i] = dst_mask[i];
+               }
+       }
+}
+
+static void
+gve_parse_udp(const struct rte_flow_item *item,
+             struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_udp *spec = item->spec;
+               const struct rte_flow_item_udp *mask =
+                       item->mask ? item->mask : &rte_flow_item_udp_mask;
+
+               rule->key.src_port = spec->hdr.src_port;
+               rule->key.dst_port = spec->hdr.dst_port;
+               rule->mask.src_port = mask->hdr.src_port;
+               rule->mask.dst_port = mask->hdr.dst_port;
+       }
+}
+
+static void
+gve_parse_tcp(const struct rte_flow_item *item,
+             struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_tcp *spec = item->spec;
+               const struct rte_flow_item_tcp *mask =
+                       item->mask ? item->mask : &rte_flow_item_tcp_mask;
+
+               rule->key.src_port = spec->hdr.src_port;
+               rule->key.dst_port = spec->hdr.dst_port;
+               rule->mask.src_port = mask->hdr.src_port;
+               rule->mask.dst_port = mask->hdr.dst_port;
+       }
+}
+
+static void
+gve_parse_sctp(const struct rte_flow_item *item,
+              struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_sctp *spec = item->spec;
+               const struct rte_flow_item_sctp *mask =
+                       item->mask ? item->mask : &rte_flow_item_sctp_mask;
+
+               rule->key.src_port = spec->hdr.src_port;
+               rule->key.dst_port = spec->hdr.dst_port;
+               rule->mask.src_port = mask->hdr.src_port;
+               rule->mask.dst_port = mask->hdr.dst_port;
+       }
+}
+
+static void
+gve_parse_esp(const struct rte_flow_item *item,
+             struct gve_flow_rule_params *rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_esp *spec = item->spec;
+               const struct rte_flow_item_esp *mask =
+                       item->mask ? item->mask : &rte_flow_item_esp_mask;
+
+               rule->key.spi = spec->hdr.spi;
+               rule->mask.spi = mask->hdr.spi;
+       }
+}
+
+static void
+gve_parse_ah(const struct rte_flow_item *item, struct gve_flow_rule_params 
*rule)
+{
+       if (item->spec) {
+               const struct rte_flow_item_ah *spec = item->spec;
+               const struct rte_flow_item_ah *mask =
+                       item->mask ? item->mask : &rte_flow_item_ah_mask;
+
+               rule->key.spi = spec->spi;
+               rule->mask.spi = mask->spi;
+       }
+}
+
+static int
+gve_validate_and_parse_flow_pattern(const struct rte_flow_item pattern[],
+                                   struct rte_flow_error *error,
+                                   struct gve_flow_rule_params *rule)
+{
+       const struct rte_flow_item *item = pattern;
+       enum rte_flow_item_type l3_type = RTE_FLOW_ITEM_TYPE_VOID;
+       enum rte_flow_item_type l4_type = RTE_FLOW_ITEM_TYPE_VOID;
+
+       if (!pattern) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM_NUM, NULL,
+                               "Invalid flow pattern");
+               return -EINVAL;
+       }
+
+       for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+               if (item->last) {
+                       /* Last and range are not supported as match criteria. 
*/
+                       rte_flow_error_set(error, EINVAL,
+                                          RTE_FLOW_ERROR_TYPE_ITEM,
+                                          item,
+                                          "No support for range");
+                       return -EINVAL;
+               }
+               switch (item->type) {
+               case RTE_FLOW_ITEM_TYPE_VOID:
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV4:
+                       if (l3_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L3 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_ipv4(item, rule);
+                       l3_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV6:
+                       if (l3_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L3 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_ipv6(item, rule);
+                       l3_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_UDP:
+                       if (l4_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L4 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_udp(item, rule);
+                       l4_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_TCP:
+                       if (l4_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L4 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_tcp(item, rule);
+                       l4_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_SCTP:
+                       if (l4_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L4 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_sctp(item, rule);
+                       l4_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_ESP:
+                       if (l4_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L4 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_esp(item, rule);
+                       l4_type = item->type;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_AH:
+                       if (l4_type != RTE_FLOW_ITEM_TYPE_VOID) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ITEM,
+                                                  item,
+                                                  "Multiple L4 items not 
supported");
+                               return -EINVAL;
+                       }
+                       gve_parse_ah(item, rule);
+                       l4_type = item->type;
+                       break;
+               default:
+                       rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                  "Unsupported flow pattern item type");
+                       return -EINVAL;
+               }
+       }
+
+       switch (l3_type) {
+       case RTE_FLOW_ITEM_TYPE_IPV4:
+               switch (l4_type) {
+               case RTE_FLOW_ITEM_TYPE_TCP:
+                       rule->flow_type = GVE_FLOW_TYPE_TCPV4;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_UDP:
+                       rule->flow_type = GVE_FLOW_TYPE_UDPV4;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_SCTP:
+                       rule->flow_type = GVE_FLOW_TYPE_SCTPV4;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_AH:
+                       rule->flow_type = GVE_FLOW_TYPE_AHV4;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_ESP:
+                       rule->flow_type = GVE_FLOW_TYPE_ESPV4;
+                       break;
+               default:
+                       goto unsupported_flow;
+               }
+               break;
+       case RTE_FLOW_ITEM_TYPE_IPV6:
+               switch (l4_type) {
+               case RTE_FLOW_ITEM_TYPE_TCP:
+                       rule->flow_type = GVE_FLOW_TYPE_TCPV6;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_UDP:
+                       rule->flow_type = GVE_FLOW_TYPE_UDPV6;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_SCTP:
+                       rule->flow_type = GVE_FLOW_TYPE_SCTPV6;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_AH:
+                       rule->flow_type = GVE_FLOW_TYPE_AHV6;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_ESP:
+                       rule->flow_type = GVE_FLOW_TYPE_ESPV6;
+                       break;
+               default:
+                       goto unsupported_flow;
+               }
+               break;
+       default:
+               goto unsupported_flow;
+       }
+
+       return 0;
+
+unsupported_flow:
+       rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                          NULL, "Unsupported L3/L4 combination");
+       return -EINVAL;
+}
+
+static int
+gve_validate_and_parse_flow_actions(struct rte_eth_dev *dev,
+                                   const struct rte_flow_action actions[],
+                                   struct rte_flow_error *error,
+                                   struct gve_flow_rule_params *rule)
+{
+       const struct rte_flow_action_queue *action_queue;
+       const struct rte_flow_action *action = actions;
+       int num_queue_actions = 0;
+
+       if (!actions) {
+               rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL,
+                                  "Invalid flow actions");
+               return -EINVAL;
+       }
+
+       while (action->type != RTE_FLOW_ACTION_TYPE_END) {
+               switch (action->type) {
+               case RTE_FLOW_ACTION_TYPE_VOID:
+                       break;
+               case RTE_FLOW_ACTION_TYPE_QUEUE:
+                       if (!action->conf) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  
RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+                                                  action,
+                                                  "QUEUE action config cannot 
be NULL.");
+                               return -EINVAL;
+                       }
+
+                       action_queue = action->conf;
+                       if (action_queue->index >= dev->data->nb_rx_queues) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  
RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+                                                  action, "Invalid Queue ID");
+                               return -EINVAL;
+                       }
+
+                       rule->action = action_queue->index;
+                       num_queue_actions++;
+                       break;
+               default:
+                       rte_flow_error_set(error, ENOTSUP,
+                                          RTE_FLOW_ERROR_TYPE_ACTION,
+                                          action,
+                                          "Unsupported action. Only QUEUE is 
permitted.");
+                       return -ENOTSUP;
+               }
+               action++;
+       }
+
+       if (num_queue_actions == 0) {
+               rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+                                  NULL, "A QUEUE action is required.");
+               return -EINVAL;
+       }
+
+       if (num_queue_actions > 1) {
+               rte_flow_error_set(error, EINVAL,
+                                  RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+                                  NULL, "Only a single QUEUE action is 
allowed.");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+gve_validate_and_parse_flow(struct rte_eth_dev *dev,
+                           const struct rte_flow_attr *attr,
+                           const struct rte_flow_item pattern[],
+                           const struct rte_flow_action actions[],
+                           struct rte_flow_error *error,
+                           struct gve_flow_rule_params *rule)
+{
+       int err;
+
+       err = gve_validate_flow_attr(attr, error);
+       if (err)
+               return err;
+
+       err = gve_validate_and_parse_flow_pattern(pattern, error, rule);
+       if (err)
+               return err;
+
+       err = gve_validate_and_parse_flow_actions(dev, actions, error, rule);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+int
+gve_flow_init_bmp(struct gve_priv *priv)
+{
+       priv->avail_flow_rule_bmp = 
rte_bitmap_init_with_all_set(priv->max_flow_rules,
+                       priv->avail_flow_rule_bmp_mem, 
priv->flow_rule_bmp_size);
+       if (!priv->avail_flow_rule_bmp) {
+               PMD_DRV_LOG(ERR, "Flow subsystem failed: cannot init bitmap.");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/*
+ * The caller must acquire the flow rule lock before calling this function.
+ */
+int
+gve_free_flow_rules(struct gve_priv *priv)
+{
+       struct gve_flow *flow;
+       int err = 0;
+
+       if (!TAILQ_EMPTY(&priv->active_flows)) {
+               err = gve_adminq_reset_flow_rules(priv);
+               if (err) {
+                       PMD_DRV_LOG(ERR,
+                               "Failed to reset flow rules, internal device 
err=%d",
+                               err);
+               }
+
+               /* Free flows even if AQ fails to avoid leaking memory. */
+               while (!TAILQ_EMPTY(&priv->active_flows)) {
+                       flow = TAILQ_FIRST(&priv->active_flows);
+                       TAILQ_REMOVE(&priv->active_flows, flow, list_handle);
+                       rte_free(flow);
+               }
+       }
+
+       return err;
+}
+
+static struct rte_flow *
+gve_create_flow_rule(struct rte_eth_dev *dev,
+                    const struct rte_flow_attr *attr,
+                    const struct rte_flow_item pattern[],
+                    const struct rte_flow_action actions[],
+                    struct rte_flow_error *error)
+{
+       struct gve_priv *priv = dev->data->dev_private;
+       struct gve_flow_rule_params rule = {0};
+       uint64_t bmp_slab __rte_unused;
+       struct gve_flow *flow;
+       int err;
+
+       err = gve_validate_and_parse_flow(dev, attr, pattern, actions, error,
+                                         &rule);
+       if (err)
+               return NULL;
+
+       flow = rte_zmalloc("gve_flow", sizeof(struct gve_flow), 0);
+       if (!flow) {
+               rte_flow_error_set(error, ENOMEM,
+                               RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                               "Failed to allocate memory for flow rule.");
+               return NULL;
+       }
+
+       pthread_mutex_lock(&priv->flow_rule_lock);
+
+       if (!gve_get_flow_subsystem_ok(priv)) {
+               rte_flow_error_set(error, ENOTSUP,
+                               RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                               "Failed to create flow, flow subsystem not 
initialized.");
+               goto free_flow_and_unlock;
+       }
+
+       /* Try to allocate a new rule ID from the bitmap. */
+       if (rte_bitmap_scan(priv->avail_flow_rule_bmp, &flow->rule_id,
+                       &bmp_slab) == 1) {
+               rte_bitmap_clear(priv->avail_flow_rule_bmp, flow->rule_id);
+       } else {
+               rte_flow_error_set(error, ENOMEM,
+                               RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                               "Failed to create flow, could not allocate a 
new rule ID.");
+               goto free_flow_and_unlock;
+       }
+
+       err = gve_adminq_add_flow_rule(priv, &rule, flow->rule_id);
+       if (err) {
+               rte_bitmap_set(priv->avail_flow_rule_bmp, flow->rule_id);
+               rte_flow_error_set(error, -err,
+                                  RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                                  "Failed to create flow rule, internal device 
error.");
+               goto free_flow_and_unlock;
+       }
+
+       TAILQ_INSERT_TAIL(&priv->active_flows, flow, list_handle);
+
+       pthread_mutex_unlock(&priv->flow_rule_lock);
+
+       return (struct rte_flow *)flow;
+
+free_flow_and_unlock:
+       rte_free(flow);
+       pthread_mutex_unlock(&priv->flow_rule_lock);
+       return NULL;
+}
+
+static int
+gve_destroy_flow_rule(struct rte_eth_dev *dev, struct rte_flow *flow_handle,
+                     struct rte_flow_error *error)
+{
+       struct gve_priv *priv = dev->data->dev_private;
+       struct gve_flow *flow;
+       bool flow_rule_active;
+       int err;
+
+       pthread_mutex_lock(&priv->flow_rule_lock);
+
+       if (!gve_get_flow_subsystem_ok(priv)) {
+               rte_flow_error_set(error, ENOTSUP,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to destroy flow, flow subsystem not 
initialized.");
+               err = -ENOTSUP;
+               goto unlock_and_return;
+       }
+
+       flow = (struct gve_flow *)flow_handle;
+
+       if (!flow) {
+               rte_flow_error_set(error, EINVAL,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to destroy flow, invalid flow provided.");
+               err = -EINVAL;
+               goto unlock_and_return;
+       }
+
+       if (flow->rule_id >= priv->max_flow_rules) {
+               PMD_DRV_LOG(ERR,
+                       "Cannot destroy flow rule with invalid ID %d.",
+                       flow->rule_id);
+               rte_flow_error_set(error, EINVAL,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to destroy flow, rule ID is invalid.");
+               err = -EINVAL;
+               goto unlock_and_return;
+       }
+
+       flow_rule_active = !rte_bitmap_get(priv->avail_flow_rule_bmp,
+                                          flow->rule_id);
+
+       if (!flow_rule_active) {
+               rte_flow_error_set(error, EINVAL,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to destroy flow, handle not found in active 
list.");
+               err = -EINVAL;
+               goto unlock_and_return;
+       }
+
+       err = gve_adminq_del_flow_rule(priv, flow->rule_id);
+       if (err) {
+               rte_flow_error_set(error, -err,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to destroy flow, internal device error.");
+               goto unlock_and_return;
+       }
+
+       rte_bitmap_set(priv->avail_flow_rule_bmp, flow->rule_id);
+       TAILQ_REMOVE(&priv->active_flows, flow, list_handle);
+       rte_free(flow);
+
+       err = 0;
+
+unlock_and_return:
+       pthread_mutex_unlock(&priv->flow_rule_lock);
+       return err;
+}
+
+static int
+gve_flush_flow_rules(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+       struct gve_priv *priv = dev->data->dev_private;
+       int err;
+
+       pthread_mutex_lock(&priv->flow_rule_lock);
+
+       if (!gve_get_flow_subsystem_ok(priv)) {
+               rte_flow_error_set(error, ENOTSUP,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to flush rules, flow subsystem not 
initialized.");
+               err = -ENOTSUP;
+               goto unlock_and_return;
+       }
+
+       err = gve_free_flow_rules(priv);
+       if (err) {
+               rte_flow_error_set(error, -err,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to flush rules due to internal device error, 
disabling flow subsystem.");
+               gve_clear_flow_subsystem_ok(priv);
+               goto unlock_and_return;
+       }
+
+       err = gve_flow_init_bmp(priv);
+       if (err) {
+               rte_flow_error_set(error, -err,
+                       RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+                       "Failed to re-initialize rule ID bitmap, disabling flow 
subsystem.");
+               gve_clear_flow_subsystem_ok(priv);
+               goto unlock_and_return;
+       }
+
+       err = 0;
+
+unlock_and_return:
+       pthread_mutex_unlock(&priv->flow_rule_lock);
+       return err;
+}
+
+const struct rte_flow_ops gve_flow_ops = {
+       .create = gve_create_flow_rule,
+       .destroy = gve_destroy_flow_rule,
+       .flush = gve_flush_flow_rules,
+};
diff --git a/drivers/net/gve/gve_flow_rule.h b/drivers/net/gve/gve_flow_rule.h
index d1a2622..d483914 100644
--- a/drivers/net/gve/gve_flow_rule.h
+++ b/drivers/net/gve/gve_flow_rule.h
@@ -56,4 +56,9 @@ struct gve_flow_rule_params {
        struct gve_flow_spec mask;
 };
 
+struct gve_priv;
+
+int gve_flow_init_bmp(struct gve_priv *priv);
+int gve_free_flow_rules(struct gve_priv *priv);
+
 #endif /* _GVE_FLOW_RULE_H_ */
diff --git a/drivers/net/gve/meson.build b/drivers/net/gve/meson.build
index c6a9f36..7074988 100644
--- a/drivers/net/gve/meson.build
+++ b/drivers/net/gve/meson.build
@@ -16,5 +16,6 @@ sources = files(
         'gve_ethdev.c',
         'gve_version.c',
         'gve_rss.c',
+        'gve_flow_rule.c',
 )
 includes += include_directories('base')
-- 
2.53.0.473.g4a7958ca14-goog

Reply via email to