diff mbox series

[ovs-dev] netdev-offload: Set 'miss_api_supported' to be under netdev

Message ID 20220630085342.3483802-1-elibr@nvidia.com
State Changes Requested
Headers show
Series [ovs-dev] netdev-offload: Set 'miss_api_supported' to be under netdev | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Eli Britstein June 30, 2022, 8:53 a.m. UTC
Commit [1] introduced a flag in dpif-netdev level, to optimize
performance and avoid hw_miss_packet_recover() for devices with no such
support.
However, there is a race condition between traffic processing and
assigning a 'flow_api' object to the netdev. In such case, EOPNOTSUPP is
returned by netdev_hw_miss_packet_recover() in netdev-offload.c layer
because 'flow_api' is not yet initialized. As a result, the flag is
falsely disabled, and subsequent packets won't be recovered, though they
should.

In order to fix it, move the flag to be in netdev-offload layer, to
avoid that race.

[1]: 6e50c1651869 ("dpif-netdev: Avoid hw_miss_packet_recover() for devices with no support.")

Fixes: 6e50c1651869 ("dpif-netdev: Avoid hw_miss_packet_recover() for devices with no support.")
Signed-off-by: Eli Britstein <elibr@nvidia.com>
---
 lib/dpif-netdev.c    | 15 ++++-----------
 lib/netdev-offload.c | 25 ++++++++++++++++++++-----
 lib/netdev-offload.h |  1 +
 3 files changed, 25 insertions(+), 16 deletions(-)

Github actions:
- v1: https://github.com/elibritstein/OVS/actions/runs/2587743230

Comments

Ilya Maximets Aug. 30, 2022, 12:14 p.m. UTC | #1
On 6/30/22 10:53, Eli Britstein wrote:
> Commit [1] introduced a flag in dpif-netdev level, to optimize
> performance and avoid hw_miss_packet_recover() for devices with no such
> support.
> However, there is a race condition between traffic processing and
> assigning a 'flow_api' object to the netdev. In such case, EOPNOTSUPP is
> returned by netdev_hw_miss_packet_recover() in netdev-offload.c layer
> because 'flow_api' is not yet initialized. As a result, the flag is
> falsely disabled, and subsequent packets won't be recovered, though they
> should.
> 
> In order to fix it, move the flag to be in netdev-offload layer, to
> avoid that race.
> 
> [1]: 6e50c1651869 ("dpif-netdev: Avoid hw_miss_packet_recover() for devices with no support.")
> 
> Fixes: 6e50c1651869 ("dpif-netdev: Avoid hw_miss_packet_recover() for devices with no support.")
> Signed-off-by: Eli Britstein <elibr@nvidia.com>
> ---

Hi, Eli.  Sorry for the late reply.
Thanks for the fix!

The change looks fine to me in general, see some comments inline.

Best regards, Ilya Maximets.

>  lib/dpif-netdev.c    | 15 ++++-----------
>  lib/netdev-offload.c | 25 ++++++++++++++++++++-----
>  lib/netdev-offload.h |  1 +
>  3 files changed, 25 insertions(+), 16 deletions(-)
> 
> Github actions:
> - v1: https://github.com/elibritstein/OVS/actions/runs/2587743230
> 
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index f46b9fe183..a286050b57 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -431,7 +431,6 @@ struct dp_netdev_rxq {
>      unsigned intrvl_idx;               /* Write index for 'cycles_intrvl'. */
>      struct dp_netdev_pmd_thread *pmd;  /* pmd thread that polls this queue. */
>      bool is_vhost;                     /* Is rxq of a vhost port. */
> -    bool hw_miss_api_supported;        /* hw_miss_packet_recover() supported.*/
>  
>      /* Counters of cycles spent successfully polling and processing pkts. */
>      atomic_ullong cycles[RXQ_N_CYCLES];
> @@ -5421,7 +5420,6 @@ port_reconfigure(struct dp_netdev_port *port)
>  
>          port->rxqs[i].port = port;
>          port->rxqs[i].is_vhost = !strncmp(port->type, "dpdkvhost", 9);
> -        port->rxqs[i].hw_miss_api_supported = true;
>  
>          err = netdev_rxq_open(netdev, &port->rxqs[i].rx, i);
>          if (err) {
> @@ -8039,16 +8037,11 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
>      /* Restore the packet if HW processing was terminated before completion. */
>      struct dp_netdev_rxq *rxq = pmd->ctx.last_rxq;
>  
> -    if (rxq->hw_miss_api_supported) {
> +    if (rxq->port->netdev->hw_info.miss_api_supported) {
>          int err = netdev_hw_miss_packet_recover(rxq->port->netdev, packet);
> -        if (err) {
> -            if (err != EOPNOTSUPP) {
> -                COVERAGE_INC(datapath_drop_hw_miss_recover);
> -                return -1;
> -            } else {
> -                /* API unsupported by the port; avoid subsequent calls. */
> -                rxq->hw_miss_api_supported = false;
> -            }
> +        if (err && err != EOPNOTSUPP) {
> +            COVERAGE_INC(datapath_drop_hw_miss_recover);
> +            return -1;
>          }
>      }
>  #endif
> diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
> index fb108c0d50..50c5076a63 100644
> --- a/lib/netdev-offload.c
> +++ b/lib/netdev-offload.c
> @@ -182,6 +182,7 @@ netdev_assign_flow_api(struct netdev *netdev)
>      CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
>          if (!rfa->flow_api->init_flow_api(netdev)) {
>              ovs_refcount_ref(&rfa->refcnt);
> +            netdev->hw_info.miss_api_supported = true;
>              ovsrcu_set(&netdev->flow_api, rfa->flow_api);
>              VLOG_INFO("%s: Assigned flow API '%s'.",
>                        netdev_get_name(netdev), rfa->flow_api->type);
> @@ -190,6 +191,7 @@ netdev_assign_flow_api(struct netdev *netdev)
>          VLOG_DBG("%s: flow API '%s' is not suitable.",
>                   netdev_get_name(netdev), rfa->flow_api->type);
>      }
> +    netdev->hw_info.miss_api_supported = false;

I'm not sure these initializations belong to this function.
We should move them to one of the initialization functions
instead.

Since 'miss_api_supported' flag can in theory be used before
the flow api is initialized, it should be correctly initialized
in the netdev_open() function.  Not sure about the default
value.  'false', I guess, should be fine.
And then netdev_init_flow_api() can flip it to 'true' once
the flow API is initialized.
The opposite is also fine.  The field is supposed to represent
hardware capabilities, so the default value doesn't mean much.


>      VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
>  
>      return -1;
> @@ -263,12 +265,25 @@ int
>  netdev_hw_miss_packet_recover(struct netdev *netdev,
>                                struct dp_packet *packet)
>  {
> -    const struct netdev_flow_api *flow_api =
> -        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
> +    const struct netdev_flow_api *flow_api;
> +    int rv;
> +
> +    if (!netdev->hw_info.miss_api_supported) {
> +        return EOPNOTSUPP;
> +    }
> +
> +    flow_api = ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
> +    if (!flow_api || !flow_api->hw_miss_packet_recover) {
> +        return EOPNOTSUPP;
> +    }
> +
> +    rv = flow_api->hw_miss_packet_recover(netdev, packet);
> +    if (rv == EOPNOTSUPP) {
> +        /* API unsupported by the port; avoid subsequent calls. */
> +        netdev->hw_info.miss_api_supported = false;
> +    }
>  
> -    return (flow_api && flow_api->hw_miss_packet_recover)
> -            ? flow_api->hw_miss_packet_recover(netdev, packet)
> -            : EOPNOTSUPP;
> +    return rv;
>  }
>  
>  int
> diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
> index 8237a85ddb..c56f9d49ad 100644
> --- a/lib/netdev-offload.h
> +++ b/lib/netdev-offload.h
> @@ -45,6 +45,7 @@ struct ovs_action_push_tnl;
>  /* Offload-capable (HW) netdev information */
>  struct netdev_hw_info {
>      bool oor;		/* Out of Offload Resources ? */
> +    bool miss_api_supported;  /* hw_miss_packet_recover() supported.*/

I think, this should be an atomic_bool.  It was OK to have a
non-atomic read/write access to rxq, since rxq is only used
by a single thread at a time.  But this value is shared among
multiple threads working on the same netdev, so it should be
atomic.  This should not cause any performance issues, AFAIU,
since relaxed atomic read/write on bool should be equal to a
normal read/write on most modern architectures, but having it
atomic is semantically more correct.

>      int offload_count;  /* Pending (non-offloaded) flow count */
>      int pending_count;  /* Offloaded flow count */
>      OVSRCU_TYPE(void *) offload_data; /* Offload metadata. */
diff mbox series

Patch

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index f46b9fe183..a286050b57 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -431,7 +431,6 @@  struct dp_netdev_rxq {
     unsigned intrvl_idx;               /* Write index for 'cycles_intrvl'. */
     struct dp_netdev_pmd_thread *pmd;  /* pmd thread that polls this queue. */
     bool is_vhost;                     /* Is rxq of a vhost port. */
-    bool hw_miss_api_supported;        /* hw_miss_packet_recover() supported.*/
 
     /* Counters of cycles spent successfully polling and processing pkts. */
     atomic_ullong cycles[RXQ_N_CYCLES];
@@ -5421,7 +5420,6 @@  port_reconfigure(struct dp_netdev_port *port)
 
         port->rxqs[i].port = port;
         port->rxqs[i].is_vhost = !strncmp(port->type, "dpdkvhost", 9);
-        port->rxqs[i].hw_miss_api_supported = true;
 
         err = netdev_rxq_open(netdev, &port->rxqs[i].rx, i);
         if (err) {
@@ -8039,16 +8037,11 @@  dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
     /* Restore the packet if HW processing was terminated before completion. */
     struct dp_netdev_rxq *rxq = pmd->ctx.last_rxq;
 
-    if (rxq->hw_miss_api_supported) {
+    if (rxq->port->netdev->hw_info.miss_api_supported) {
         int err = netdev_hw_miss_packet_recover(rxq->port->netdev, packet);
-        if (err) {
-            if (err != EOPNOTSUPP) {
-                COVERAGE_INC(datapath_drop_hw_miss_recover);
-                return -1;
-            } else {
-                /* API unsupported by the port; avoid subsequent calls. */
-                rxq->hw_miss_api_supported = false;
-            }
+        if (err && err != EOPNOTSUPP) {
+            COVERAGE_INC(datapath_drop_hw_miss_recover);
+            return -1;
         }
     }
 #endif
diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
index fb108c0d50..50c5076a63 100644
--- a/lib/netdev-offload.c
+++ b/lib/netdev-offload.c
@@ -182,6 +182,7 @@  netdev_assign_flow_api(struct netdev *netdev)
     CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
         if (!rfa->flow_api->init_flow_api(netdev)) {
             ovs_refcount_ref(&rfa->refcnt);
+            netdev->hw_info.miss_api_supported = true;
             ovsrcu_set(&netdev->flow_api, rfa->flow_api);
             VLOG_INFO("%s: Assigned flow API '%s'.",
                       netdev_get_name(netdev), rfa->flow_api->type);
@@ -190,6 +191,7 @@  netdev_assign_flow_api(struct netdev *netdev)
         VLOG_DBG("%s: flow API '%s' is not suitable.",
                  netdev_get_name(netdev), rfa->flow_api->type);
     }
+    netdev->hw_info.miss_api_supported = false;
     VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
 
     return -1;
@@ -263,12 +265,25 @@  int
 netdev_hw_miss_packet_recover(struct netdev *netdev,
                               struct dp_packet *packet)
 {
-    const struct netdev_flow_api *flow_api =
-        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
+    const struct netdev_flow_api *flow_api;
+    int rv;
+
+    if (!netdev->hw_info.miss_api_supported) {
+        return EOPNOTSUPP;
+    }
+
+    flow_api = ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
+    if (!flow_api || !flow_api->hw_miss_packet_recover) {
+        return EOPNOTSUPP;
+    }
+
+    rv = flow_api->hw_miss_packet_recover(netdev, packet);
+    if (rv == EOPNOTSUPP) {
+        /* API unsupported by the port; avoid subsequent calls. */
+        netdev->hw_info.miss_api_supported = false;
+    }
 
-    return (flow_api && flow_api->hw_miss_packet_recover)
-            ? flow_api->hw_miss_packet_recover(netdev, packet)
-            : EOPNOTSUPP;
+    return rv;
 }
 
 int
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 8237a85ddb..c56f9d49ad 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -45,6 +45,7 @@  struct ovs_action_push_tnl;
 /* Offload-capable (HW) netdev information */
 struct netdev_hw_info {
     bool oor;		/* Out of Offload Resources ? */
+    bool miss_api_supported;  /* hw_miss_packet_recover() supported.*/
     int offload_count;  /* Pending (non-offloaded) flow count */
     int pending_count;  /* Offloaded flow count */
     OVSRCU_TYPE(void *) offload_data; /* Offload metadata. */