diff mbox series

[ovs-dev,v2] dpif-netlink: Fix issues of the offloaded flows counter

Message ID 20201215045008.29598-1-jianbol@nvidia.com
State Changes Requested
Headers show
Series [ovs-dev,v2] dpif-netlink: Fix issues of the offloaded flows counter | expand

Commit Message

Jianbo Liu Dec. 15, 2020, 4:50 a.m. UTC
The n_offloaded_flows counter is saved in dpif, and this is the first
one when ofproto is created. When flow operation is done by ovs-appctl
commands, such as, dpctl/add-flow, a new dpif is opened, and the
n_offloaded_flows in it can't be used. So, instead of using counter,
the number of offloaded flows is queried from each netdev, then sum
them up. To achieve this, a new API is added in netdev_flow_api to get
how many flows assigned to a netdev.

In order to get better performance, this number is calculated directly
from tc_to_ufid hmap for netdev-offload-tc, because flow dumping by tc
takes much time if there are many flows offloaded.

Fixes: af0618470507 ("dpif-netlink: Count the number of offloaded rules")
Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
---
 lib/dpif-netlink.c               |  9 ---------
 lib/dpif.c                       | 22 ++++++++++++++++++++++
 lib/dpif.h                       |  3 ++-
 lib/netdev-offload-provider.h    |  4 ++++
 lib/netdev-offload-tc.c          | 20 ++++++++++++++++++++
 lib/netdev-offload.c             | 27 +++++++++++++++++++++++++++
 lib/netdev-offload.h             |  3 +++
 ofproto/ofproto-dpif-upcall.c    | 10 ++++------
 tests/system-offloads-traffic.at |  4 ++++
 9 files changed, 86 insertions(+), 16 deletions(-)

Comments

Jianbo Liu Dec. 15, 2020, 6:30 a.m. UTC | #1
The 12/15/2020 04:50, Jianbo Liu wrote:
> The n_offloaded_flows counter is saved in dpif, and this is the first
> one when ofproto is created. When flow operation is done by ovs-appctl
> commands, such as, dpctl/add-flow, a new dpif is opened, and the
> n_offloaded_flows in it can't be used. So, instead of using counter,
> the number of offloaded flows is queried from each netdev, then sum
> them up. To achieve this, a new API is added in netdev_flow_api to get
> how many flows assigned to a netdev.
> 
> In order to get better performance, this number is calculated directly
> from tc_to_ufid hmap for netdev-offload-tc, because flow dumping by tc
> takes much time if there are many flows offloaded.
> 
> Fixes: af0618470507 ("dpif-netlink: Count the number of offloaded rules")
> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
> ---
>  lib/dpif-netlink.c               |  9 ---------
>  lib/dpif.c                       | 22 ++++++++++++++++++++++
>  lib/dpif.h                       |  3 ++-
>  lib/netdev-offload-provider.h    |  4 ++++
>  lib/netdev-offload-tc.c          | 20 ++++++++++++++++++++
>  lib/netdev-offload.c             | 27 +++++++++++++++++++++++++++
>  lib/netdev-offload.h             |  3 +++
>  ofproto/ofproto-dpif-upcall.c    | 10 ++++------
>  tests/system-offloads-traffic.at |  4 ++++
>  9 files changed, 86 insertions(+), 16 deletions(-)
> 
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index 6858ba612..2f881e4fa 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -208,7 +208,6 @@ struct dpif_netlink {
>      /* Change notification. */
>      struct nl_sock *port_notifier; /* vport multicast group subscriber. */
>      bool refresh_channels;
> -    struct atomic_count n_offloaded_flows;
>  };
>  
>  static void report_loss(struct dpif_netlink *, struct dpif_channel *,
> @@ -654,7 +653,6 @@ dpif_netlink_run(struct dpif *dpif_)
>  static int
>  dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
>  {
> -    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
>      struct dpif_netlink_dp dp;
>      struct ofpbuf *buf;
>      int error;
> @@ -680,7 +678,6 @@ dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
>          }
>          ofpbuf_delete(buf);
>      }
> -    stats->n_offloaded_flows = atomic_count_get(&dpif->n_offloaded_flows);
>      return error;
>  }
>  
> @@ -2192,9 +2189,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
>          }
>  
>          err = parse_flow_put(dpif, put);
> -        if (!err && (put->flags & DPIF_FP_CREATE)) {
> -            atomic_count_inc(&dpif->n_offloaded_flows);
> -        }
>          log_flow_put_message(&dpif->dpif, &this_module, put, 0);
>          break;
>      }
> @@ -2209,9 +2203,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
>                                  dpif_normalize_type(dpif_type(&dpif->dpif)),
>                                  del->ufid,
>                                  del->stats);
> -        if (!err) {
> -            atomic_count_dec(&dpif->n_offloaded_flows);
> -        }
>          log_flow_del_message(&dpif->dpif, &this_module, del, 0);
>          break;
>      }
> diff --git a/lib/dpif.c b/lib/dpif.c
> index ac2860764..7f01aa48b 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -2018,3 +2018,25 @@ dpif_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
>             ? dpif->dpif_class->bond_stats_get(dpif, bond_id, n_bytes)
>             : EOPNOTSUPP;
>  }
> +
> +int
> +dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows)
> +{
> +    const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
> +    struct dpif_port_dump port_dump;
> +    struct dpif_port dpif_port;
> +    int ret, n_devs = 0;
> +    uint64_t nflows;
> +
> +    *n_flows = 0;
> +    DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
> +        ret = netdev_ports_get_n_flows(dpif_type_str, &dpif_port, &nflows);
> +        if (!ret) {
> +            *n_flows += nflows;
> +        } else if (ret == EOPNOTSUPP) {
> +            continue;
> +        }
> +        n_devs++;
> +    }
> +    return n_devs ? 0 : EOPNOTSUPP;
> +}
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 7ef148c6d..9d0d662fc 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -429,7 +429,6 @@ struct dpif_dp_stats {
>      uint64_t n_missed;          /* Number of flow table misses. */
>      uint64_t n_lost;            /* Number of misses not sent to userspace. */
>      uint64_t n_flows;           /* Number of flows present. */
> -    uint64_t n_offloaded_flows; /* Number of offloaded flows present. */
>      uint64_t n_mask_hit;        /* Number of mega flow masks visited for
>                                     flow table matches. */
>      uint32_t n_masks;           /* Number of mega flow masks. */
> @@ -438,6 +437,8 @@ int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
>  
>  int dpif_set_features(struct dpif *, uint32_t new_features);
>  
> +int dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows);
> +
>  
>  /* Port operations. */
>  
> diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
> index 0bed7bf61..cf859d1b4 100644
> --- a/lib/netdev-offload-provider.h
> +++ b/lib/netdev-offload-provider.h
> @@ -83,6 +83,10 @@ struct netdev_flow_api {
>      int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
>                      struct dpif_flow_stats *);
>  
> +    /* Get the number of flows offloaded to netdev.
> +     * Return 0 if successful, otherwise returns a positive errno value. */
> +    int (*flow_get_n_flows)(struct netdev *, uint64_t *n_flows);
> +
>      /* Initializies the netdev flow api.
>       * Return 0 if successful, otherwise returns a positive errno value. */
>      int (*init_flow_api)(struct netdev *);
> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> index 2a772a971..f17980a47 100644
> --- a/lib/netdev-offload-tc.c
> +++ b/lib/netdev-offload-tc.c
> @@ -1904,6 +1904,25 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
>      return error;
>  }
>  
> +static int
> +netdev_tc_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
> +{
> +    struct ufid_tc_data *data, *next;
> +    uint64_t total = 0;
> +
> +    ovs_mutex_lock(&ufid_lock);
> +    HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {
> +        if (data->netdev != netdev) {
> +            continue;
> +        }
> +        total++;
> +   }
> +   ovs_mutex_unlock(&ufid_lock);
> +
> +   *n_flows = total;
> +   return 0;
> +}
> +
>  static void
>  probe_multi_mask_per_prio(int ifindex)
>  {
> @@ -2044,5 +2063,6 @@ const struct netdev_flow_api netdev_offload_tc = {
>     .flow_put = netdev_tc_flow_put,
>     .flow_get = netdev_tc_flow_get,
>     .flow_del = netdev_tc_flow_del,
> +   .flow_get_n_flows = netdev_tc_get_n_flows,
>     .init_flow_api = netdev_tc_init_flow_api,
>  };
> diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
> index 2da3bc701..696877058 100644
> --- a/lib/netdev-offload.c
> +++ b/lib/netdev-offload.c
> @@ -280,6 +280,17 @@ netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>             : EOPNOTSUPP;
>  }
>  
> +int
> +netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
> +{
> +    const struct netdev_flow_api *flow_api =
> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
> +
> +    return (flow_api && flow_api->flow_get_n_flows)
> +           ? flow_api->flow_get_n_flows(netdev, n_flows)
> +           : EOPNOTSUPP;
> +}
> +
>  int
>  netdev_init_flow_api(struct netdev *netdev)
>  {
> @@ -602,6 +613,22 @@ netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
>      return ret;
>  }
>  
> +int
> +netdev_ports_get_n_flows(const char *dpif_type, struct dpif_port *dpif_port,
> +                         uint64_t *n_flows)
> +{
> +    struct port_to_netdev_data *data;
> +    int ret = EOPNOTSUPP;
> +
> +    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
> +    data = netdev_ports_lookup(dpif_port->port_no, dpif_type);
> +    if (data) {
> +        ret = netdev_flow_get_n_flows(data->netdev, n_flows);
> +    }
> +    ovs_rwlock_unlock(&netdev_hmap_rwlock);
> +    return ret;
> +}
> +
>  odp_port_t
>  netdev_ifindex_to_odp_port(int ifindex)
>  {
> diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
> index 4c0ed2ae8..ffd321bfc 100644
> --- a/lib/netdev-offload.h
> +++ b/lib/netdev-offload.h
> @@ -103,6 +103,7 @@ bool netdev_any_oor(void);
>  bool netdev_is_flow_api_enabled(void);
>  void netdev_set_flow_api_enabled(const struct smap *ovs_other_config);
>  bool netdev_is_offload_rebalance_policy_enabled(void);
> +int netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows);
>  
>  struct dpif_port;
>  int netdev_ports_insert(struct netdev *, const char *dpif_type,
> @@ -124,6 +125,8 @@ int netdev_ports_flow_get(const char *dpif_type, struct match *match,
>                            struct dpif_flow_stats *stats,
>                            struct dpif_flow_attrs *attrs,
>                            struct ofpbuf *buf);
> +int netdev_ports_get_n_flows(const char *dpif_type,
> +                             struct dpif_port *dpif_port, uint64_t *n_flows);
>  
>  #ifdef  __cplusplus
>  }
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 19b92dfe0..75496ac6d 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -175,7 +175,6 @@ struct udpif {
>  
>      /* n_flows_mutex prevents multiple threads updating these concurrently. */
>      atomic_uint n_flows;               /* Number of flows in the datapath. */
> -    atomic_uint n_offloaded_flows;     /* Number of the offloaded flows. */
>      atomic_llong n_flows_timestamp;    /* Last time n_flows was updated. */
>      struct ovs_mutex n_flows_mutex;
>  
> @@ -731,8 +730,6 @@ udpif_get_n_flows(struct udpif *udpif)
>          dpif_get_dp_stats(udpif->dpif, &stats);
>          flow_count = stats.n_flows;
>          atomic_store_relaxed(&udpif->n_flows, flow_count);
> -        atomic_store_relaxed(&udpif->n_offloaded_flows,
> -                             stats.n_offloaded_flows);
>          ovs_mutex_unlock(&udpif->n_flows_mutex);
>      } else {
>          atomic_read_relaxed(&udpif->n_flows, &flow_count);
> @@ -2904,10 +2901,10 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
>  {
>      struct ds ds = DS_EMPTY_INITIALIZER;
> +    uint64_t n_offloaded_flows;
>      struct udpif *udpif;
>  
>      LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
> -        unsigned int n_offloaded_flows;
>          unsigned int flow_limit;
>          bool ufid_enabled;
>          size_t i;
> @@ -2919,8 +2916,9 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>          ds_put_format(&ds, "  flows         : (current %lu)"
>              " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
>              udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
> -        atomic_read_relaxed(&udpif->n_offloaded_flows, &n_offloaded_flows);
> -        ds_put_format(&ds, "  offloaded flows : %u\n", n_offloaded_flows);
> +        if (!dpif_get_dp_offloaded_flows(udpif->dpif, &n_offloaded_flows)) {
> +            ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);
> +        }
>          ds_put_format(&ds, "  dump duration : %lldms\n", udpif->dump_duration);
>          ds_put_format(&ds, "  ufid enabled : ");
>          if (ufid_enabled) {
> diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
> index 379a8a5e9..c03ffd598 100644
> --- a/tests/system-offloads-traffic.at
> +++ b/tests/system-offloads-traffic.at
> @@ -32,6 +32,8 @@ in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:
>  
>  AT_CHECK([ovs-appctl dpctl/dump-flows type=offloaded], [0], [])
>  
> +AT_CHECK([ovs-appctl upcall/show | grep "offloaded flows : 0"], [1], [ignore])

With this change, "offloaded flows" will not be displayed if hw offload
is disabled.

> +
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
>  
> @@ -64,5 +66,7 @@ in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:
>  in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output
>  ])
>  
> +AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore])

It's 8 offloaded rules, but I got another value sometimes, which was
caused by testing environment. So I don't check the exact value here.

> +
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
> -- 
> 2.26.2
>
Ilya Maximets Dec. 16, 2020, 12:17 p.m. UTC | #2
On 12/15/20 7:30 AM, Jianbo Liu wrote:
> The 12/15/2020 04:50, Jianbo Liu wrote:
>> The n_offloaded_flows counter is saved in dpif, and this is the first
>> one when ofproto is created. When flow operation is done by ovs-appctl
>> commands, such as, dpctl/add-flow, a new dpif is opened, and the
>> n_offloaded_flows in it can't be used. So, instead of using counter,
>> the number of offloaded flows is queried from each netdev, then sum
>> them up. To achieve this, a new API is added in netdev_flow_api to get
>> how many flows assigned to a netdev.
>>
>> In order to get better performance, this number is calculated directly
>> from tc_to_ufid hmap for netdev-offload-tc, because flow dumping by tc
>> takes much time if there are many flows offloaded.

Thanks, this looks like a cleaner solution.
I didn't read the patch very carefully, but it looks OK at a quick glance.
Some comments inline.

Bets regards, Ilya Maximets.

>>
>> Fixes: af0618470507 ("dpif-netlink: Count the number of offloaded rules")
>> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
>> ---
>>  lib/dpif-netlink.c               |  9 ---------
>>  lib/dpif.c                       | 22 ++++++++++++++++++++++
>>  lib/dpif.h                       |  3 ++-
>>  lib/netdev-offload-provider.h    |  4 ++++
>>  lib/netdev-offload-tc.c          | 20 ++++++++++++++++++++
>>  lib/netdev-offload.c             | 27 +++++++++++++++++++++++++++
>>  lib/netdev-offload.h             |  3 +++
>>  ofproto/ofproto-dpif-upcall.c    | 10 ++++------
>>  tests/system-offloads-traffic.at |  4 ++++
>>  9 files changed, 86 insertions(+), 16 deletions(-)
>>
>> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
>> index 6858ba612..2f881e4fa 100644
>> --- a/lib/dpif-netlink.c
>> +++ b/lib/dpif-netlink.c
>> @@ -208,7 +208,6 @@ struct dpif_netlink {
>>      /* Change notification. */
>>      struct nl_sock *port_notifier; /* vport multicast group subscriber. */
>>      bool refresh_channels;
>> -    struct atomic_count n_offloaded_flows;
>>  };
>>  
>>  static void report_loss(struct dpif_netlink *, struct dpif_channel *,
>> @@ -654,7 +653,6 @@ dpif_netlink_run(struct dpif *dpif_)
>>  static int
>>  dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
>>  {
>> -    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
>>      struct dpif_netlink_dp dp;
>>      struct ofpbuf *buf;
>>      int error;
>> @@ -680,7 +678,6 @@ dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
>>          }
>>          ofpbuf_delete(buf);
>>      }
>> -    stats->n_offloaded_flows = atomic_count_get(&dpif->n_offloaded_flows);
>>      return error;
>>  }
>>  
>> @@ -2192,9 +2189,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
>>          }
>>  
>>          err = parse_flow_put(dpif, put);
>> -        if (!err && (put->flags & DPIF_FP_CREATE)) {
>> -            atomic_count_inc(&dpif->n_offloaded_flows);
>> -        }
>>          log_flow_put_message(&dpif->dpif, &this_module, put, 0);
>>          break;
>>      }
>> @@ -2209,9 +2203,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
>>                                  dpif_normalize_type(dpif_type(&dpif->dpif)),
>>                                  del->ufid,
>>                                  del->stats);
>> -        if (!err) {
>> -            atomic_count_dec(&dpif->n_offloaded_flows);
>> -        }
>>          log_flow_del_message(&dpif->dpif, &this_module, del, 0);
>>          break;
>>      }
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index ac2860764..7f01aa48b 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -2018,3 +2018,25 @@ dpif_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
>>             ? dpif->dpif_class->bond_stats_get(dpif, bond_id, n_bytes)
>>             : EOPNOTSUPP;
>>  }
>> +
>> +int
>> +dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows)

Shouldn't this be dpif_get_n_offloaded_flows?

>> +{
>> +    const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
>> +    struct dpif_port_dump port_dump;
>> +    struct dpif_port dpif_port;
>> +    int ret, n_devs = 0;
>> +    uint64_t nflows;
>> +
>> +    *n_flows = 0;
>> +    DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
>> +        ret = netdev_ports_get_n_flows(dpif_type_str, &dpif_port, &nflows);
>> +        if (!ret) {
>> +            *n_flows += nflows;
>> +        } else if (ret == EOPNOTSUPP) {
>> +            continue;
>> +        }
>> +        n_devs++;
>> +    }
>> +    return n_devs ? 0 : EOPNOTSUPP;
>> +}
>> diff --git a/lib/dpif.h b/lib/dpif.h
>> index 7ef148c6d..9d0d662fc 100644
>> --- a/lib/dpif.h
>> +++ b/lib/dpif.h
>> @@ -429,7 +429,6 @@ struct dpif_dp_stats {
>>      uint64_t n_missed;          /* Number of flow table misses. */
>>      uint64_t n_lost;            /* Number of misses not sent to userspace. */
>>      uint64_t n_flows;           /* Number of flows present. */
>> -    uint64_t n_offloaded_flows; /* Number of offloaded flows present. */
>>      uint64_t n_mask_hit;        /* Number of mega flow masks visited for
>>                                     flow table matches. */
>>      uint32_t n_masks;           /* Number of mega flow masks. */
>> @@ -438,6 +437,8 @@ int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
>>  
>>  int dpif_set_features(struct dpif *, uint32_t new_features);
>>  
>> +int dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows);
>> +
>>  
>>  /* Port operations. */
>>  
>> diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
>> index 0bed7bf61..cf859d1b4 100644
>> --- a/lib/netdev-offload-provider.h
>> +++ b/lib/netdev-offload-provider.h
>> @@ -83,6 +83,10 @@ struct netdev_flow_api {
>>      int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
>>                      struct dpif_flow_stats *);
>>  
>> +    /* Get the number of flows offloaded to netdev.
>> +     * Return 0 if successful, otherwise returns a positive errno value. */
>> +    int (*flow_get_n_flows)(struct netdev *, uint64_t *n_flows);
>> +
>>      /* Initializies the netdev flow api.
>>       * Return 0 if successful, otherwise returns a positive errno value. */
>>      int (*init_flow_api)(struct netdev *);
>> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
>> index 2a772a971..f17980a47 100644
>> --- a/lib/netdev-offload-tc.c
>> +++ b/lib/netdev-offload-tc.c
>> @@ -1904,6 +1904,25 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
>>      return error;
>>  }
>>  
>> +static int
>> +netdev_tc_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
>> +{
>> +    struct ufid_tc_data *data, *next;
>> +    uint64_t total = 0;
>> +
>> +    ovs_mutex_lock(&ufid_lock);
>> +    HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {

We're not modifying the hash map here, so it should be simple HMAP_FOR_EACH
iterator.

>> +        if (data->netdev != netdev) {
>> +            continue;
>> +        }
>> +        total++;

Maybe, it's better to turn 'if' condition to positive one and save some lines?
e.g.,

    if (data->netdev == netdev) {
        total++;
    }

>> +   }
>> +   ovs_mutex_unlock(&ufid_lock);
>> +
>> +   *n_flows = total;
>> +   return 0;
>> +}
>> +
>>  static void
>>  probe_multi_mask_per_prio(int ifindex)
>>  {
>> @@ -2044,5 +2063,6 @@ const struct netdev_flow_api netdev_offload_tc = {
>>     .flow_put = netdev_tc_flow_put,
>>     .flow_get = netdev_tc_flow_get,
>>     .flow_del = netdev_tc_flow_del,
>> +   .flow_get_n_flows = netdev_tc_get_n_flows,
>>     .init_flow_api = netdev_tc_init_flow_api,
>>  };
>> diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
>> index 2da3bc701..696877058 100644
>> --- a/lib/netdev-offload.c
>> +++ b/lib/netdev-offload.c
>> @@ -280,6 +280,17 @@ netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>>             : EOPNOTSUPP;
>>  }
>>  
>> +int
>> +netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
>> +{
>> +    const struct netdev_flow_api *flow_api =
>> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>> +
>> +    return (flow_api && flow_api->flow_get_n_flows)
>> +           ? flow_api->flow_get_n_flows(netdev, n_flows)
>> +           : EOPNOTSUPP;
>> +}
>> +
>>  int
>>  netdev_init_flow_api(struct netdev *netdev)
>>  {
>> @@ -602,6 +613,22 @@ netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
>>      return ret;
>>  }
>>  
>> +int
>> +netdev_ports_get_n_flows(const char *dpif_type, struct dpif_port *dpif_port,

'dpif_port' is here only for the 'port_no'.  It's , probably, better, to just pass
'port_no' to this function directly.

>> +                         uint64_t *n_flows)
>> +{
>> +    struct port_to_netdev_data *data;
>> +    int ret = EOPNOTSUPP;
>> +
>> +    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
>> +    data = netdev_ports_lookup(dpif_port->port_no, dpif_type);
>> +    if (data) {
>> +        ret = netdev_flow_get_n_flows(data->netdev, n_flows);
>> +    }
>> +    ovs_rwlock_unlock(&netdev_hmap_rwlock);
>> +    return ret;
>> +}
>> +
>>  odp_port_t
>>  netdev_ifindex_to_odp_port(int ifindex)
>>  {
>> diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
>> index 4c0ed2ae8..ffd321bfc 100644
>> --- a/lib/netdev-offload.h
>> +++ b/lib/netdev-offload.h
>> @@ -103,6 +103,7 @@ bool netdev_any_oor(void);
>>  bool netdev_is_flow_api_enabled(void);
>>  void netdev_set_flow_api_enabled(const struct smap *ovs_other_config);
>>  bool netdev_is_offload_rebalance_policy_enabled(void);
>> +int netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows);
>>  
>>  struct dpif_port;
>>  int netdev_ports_insert(struct netdev *, const char *dpif_type,
>> @@ -124,6 +125,8 @@ int netdev_ports_flow_get(const char *dpif_type, struct match *match,
>>                            struct dpif_flow_stats *stats,
>>                            struct dpif_flow_attrs *attrs,
>>                            struct ofpbuf *buf);
>> +int netdev_ports_get_n_flows(const char *dpif_type,
>> +                             struct dpif_port *dpif_port, uint64_t *n_flows);
>>  
>>  #ifdef  __cplusplus
>>  }
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index 19b92dfe0..75496ac6d 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -175,7 +175,6 @@ struct udpif {
>>  
>>      /* n_flows_mutex prevents multiple threads updating these concurrently. */
>>      atomic_uint n_flows;               /* Number of flows in the datapath. */
>> -    atomic_uint n_offloaded_flows;     /* Number of the offloaded flows. */
>>      atomic_llong n_flows_timestamp;    /* Last time n_flows was updated. */
>>      struct ovs_mutex n_flows_mutex;
>>  
>> @@ -731,8 +730,6 @@ udpif_get_n_flows(struct udpif *udpif)
>>          dpif_get_dp_stats(udpif->dpif, &stats);
>>          flow_count = stats.n_flows;
>>          atomic_store_relaxed(&udpif->n_flows, flow_count);
>> -        atomic_store_relaxed(&udpif->n_offloaded_flows,
>> -                             stats.n_offloaded_flows);
>>          ovs_mutex_unlock(&udpif->n_flows_mutex);
>>      } else {
>>          atomic_read_relaxed(&udpif->n_flows, &flow_count);
>> @@ -2904,10 +2901,10 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
>>  {
>>      struct ds ds = DS_EMPTY_INITIALIZER;
>> +    uint64_t n_offloaded_flows;
>>      struct udpif *udpif;
>>  
>>      LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
>> -        unsigned int n_offloaded_flows;
>>          unsigned int flow_limit;
>>          bool ufid_enabled;
>>          size_t i;
>> @@ -2919,8 +2916,9 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>          ds_put_format(&ds, "  flows         : (current %lu)"
>>              " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
>>              udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
>> -        atomic_read_relaxed(&udpif->n_offloaded_flows, &n_offloaded_flows);
>> -        ds_put_format(&ds, "  offloaded flows : %u\n", n_offloaded_flows);
>> +        if (!dpif_get_dp_offloaded_flows(udpif->dpif, &n_offloaded_flows)) {
>> +            ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);

This causes build failure on 32bit:
ofproto/ofproto-dpif-upcall.c:2920:55: error: format ‘%ld’ expects argument of type
‘long int’, but argument 3 has type ‘uint64_t {aka long long unsigned int}’ [-Werror=format=]
             ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);
                                                     ~~^
                                                     %lld

You should use PRIu64 instead.

>> +        }
>>          ds_put_format(&ds, "  dump duration : %lldms\n", udpif->dump_duration);
>>          ds_put_format(&ds, "  ufid enabled : ");
>>          if (ufid_enabled) {
>> diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
>> index 379a8a5e9..c03ffd598 100644
>> --- a/tests/system-offloads-traffic.at
>> +++ b/tests/system-offloads-traffic.at
>> @@ -32,6 +32,8 @@ in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:
>>  
>>  AT_CHECK([ovs-appctl dpctl/dump-flows type=offloaded], [0], [])
>>  
>> +AT_CHECK([ovs-appctl upcall/show | grep "offloaded flows : 0"], [1], [ignore])
> 
> With this change, "offloaded flows" will not be displayed if hw offload
> is disabled.

Maybe this could be:

AT_CHECK([test $(ovs-appctl upcall/show | grep -c "offloaded flows") -eq 0 ])

?

> 
>> +
>>  OVS_TRAFFIC_VSWITCHD_STOP
>>  AT_CLEANUP
>>  
>> @@ -64,5 +66,7 @@ in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:
>>  in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output
>>  ])
>>  
>> +AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore])
> 
> It's 8 offloaded rules, but I got another value sometimes, which was
> caused by testing environment. So I don't check the exact value here.

Yeah, there could be some random traffic, since it's a system test, e.g. some ARP
and ipv6 packets.  If you want a precise check, you could still add an implementation
for netdev_offload_dummy and add some checks to 'dpif-netdev - partial hw offload' tests
in tests/dpif-netdev.at.  It supports only partial offloading, but I don't think that
really matters here.
Jianbo Liu Dec. 17, 2020, 1:26 a.m. UTC | #3
The 12/16/2020 13:17, Ilya Maximets wrote:
> On 12/15/20 7:30 AM, Jianbo Liu wrote:
> > The 12/15/2020 04:50, Jianbo Liu wrote:
> >> The n_offloaded_flows counter is saved in dpif, and this is the first
> >> one when ofproto is created. When flow operation is done by ovs-appctl
> >> commands, such as, dpctl/add-flow, a new dpif is opened, and the
> >> n_offloaded_flows in it can't be used. So, instead of using counter,
> >> the number of offloaded flows is queried from each netdev, then sum
> >> them up. To achieve this, a new API is added in netdev_flow_api to get
> >> how many flows assigned to a netdev.
> >>
> >> In order to get better performance, this number is calculated directly
> >> from tc_to_ufid hmap for netdev-offload-tc, because flow dumping by tc
> >> takes much time if there are many flows offloaded.
> 
> Thanks, this looks like a cleaner solution.
> I didn't read the patch very carefully, but it looks OK at a quick glance.
> Some comments inline.

Thanks. I will address them all in v3, please continue review it.

> 
> Bets regards, Ilya Maximets.
> 
> >>
> >> Fixes: af0618470507 ("dpif-netlink: Count the number of offloaded rules")
> >> Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
> >> ---
> >>  lib/dpif-netlink.c               |  9 ---------
> >>  lib/dpif.c                       | 22 ++++++++++++++++++++++
> >>  lib/dpif.h                       |  3 ++-
> >>  lib/netdev-offload-provider.h    |  4 ++++
> >>  lib/netdev-offload-tc.c          | 20 ++++++++++++++++++++
> >>  lib/netdev-offload.c             | 27 +++++++++++++++++++++++++++
> >>  lib/netdev-offload.h             |  3 +++
> >>  ofproto/ofproto-dpif-upcall.c    | 10 ++++------
> >>  tests/system-offloads-traffic.at |  4 ++++
> >>  9 files changed, 86 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> >> index 6858ba612..2f881e4fa 100644
> >> --- a/lib/dpif-netlink.c
> >> +++ b/lib/dpif-netlink.c
> >> @@ -208,7 +208,6 @@ struct dpif_netlink {
> >>      /* Change notification. */
> >>      struct nl_sock *port_notifier; /* vport multicast group subscriber. */
> >>      bool refresh_channels;
> >> -    struct atomic_count n_offloaded_flows;
> >>  };
> >>  
> >>  static void report_loss(struct dpif_netlink *, struct dpif_channel *,
> >> @@ -654,7 +653,6 @@ dpif_netlink_run(struct dpif *dpif_)
> >>  static int
> >>  dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
> >>  {
> >> -    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
> >>      struct dpif_netlink_dp dp;
> >>      struct ofpbuf *buf;
> >>      int error;
> >> @@ -680,7 +678,6 @@ dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
> >>          }
> >>          ofpbuf_delete(buf);
> >>      }
> >> -    stats->n_offloaded_flows = atomic_count_get(&dpif->n_offloaded_flows);
> >>      return error;
> >>  }
> >>  
> >> @@ -2192,9 +2189,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
> >>          }
> >>  
> >>          err = parse_flow_put(dpif, put);
> >> -        if (!err && (put->flags & DPIF_FP_CREATE)) {
> >> -            atomic_count_inc(&dpif->n_offloaded_flows);
> >> -        }
> >>          log_flow_put_message(&dpif->dpif, &this_module, put, 0);
> >>          break;
> >>      }
> >> @@ -2209,9 +2203,6 @@ try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
> >>                                  dpif_normalize_type(dpif_type(&dpif->dpif)),
> >>                                  del->ufid,
> >>                                  del->stats);
> >> -        if (!err) {
> >> -            atomic_count_dec(&dpif->n_offloaded_flows);
> >> -        }
> >>          log_flow_del_message(&dpif->dpif, &this_module, del, 0);
> >>          break;
> >>      }
> >> diff --git a/lib/dpif.c b/lib/dpif.c
> >> index ac2860764..7f01aa48b 100644
> >> --- a/lib/dpif.c
> >> +++ b/lib/dpif.c
> >> @@ -2018,3 +2018,25 @@ dpif_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
> >>             ? dpif->dpif_class->bond_stats_get(dpif, bond_id, n_bytes)
> >>             : EOPNOTSUPP;
> >>  }
> >> +
> >> +int
> >> +dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows)
> 
> Shouldn't this be dpif_get_n_offloaded_flows?
> 
> >> +{
> >> +    const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
> >> +    struct dpif_port_dump port_dump;
> >> +    struct dpif_port dpif_port;
> >> +    int ret, n_devs = 0;
> >> +    uint64_t nflows;
> >> +
> >> +    *n_flows = 0;
> >> +    DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
> >> +        ret = netdev_ports_get_n_flows(dpif_type_str, &dpif_port, &nflows);
> >> +        if (!ret) {
> >> +            *n_flows += nflows;
> >> +        } else if (ret == EOPNOTSUPP) {
> >> +            continue;
> >> +        }
> >> +        n_devs++;
> >> +    }
> >> +    return n_devs ? 0 : EOPNOTSUPP;
> >> +}
> >> diff --git a/lib/dpif.h b/lib/dpif.h
> >> index 7ef148c6d..9d0d662fc 100644
> >> --- a/lib/dpif.h
> >> +++ b/lib/dpif.h
> >> @@ -429,7 +429,6 @@ struct dpif_dp_stats {
> >>      uint64_t n_missed;          /* Number of flow table misses. */
> >>      uint64_t n_lost;            /* Number of misses not sent to userspace. */
> >>      uint64_t n_flows;           /* Number of flows present. */
> >> -    uint64_t n_offloaded_flows; /* Number of offloaded flows present. */
> >>      uint64_t n_mask_hit;        /* Number of mega flow masks visited for
> >>                                     flow table matches. */
> >>      uint32_t n_masks;           /* Number of mega flow masks. */
> >> @@ -438,6 +437,8 @@ int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
> >>  
> >>  int dpif_set_features(struct dpif *, uint32_t new_features);
> >>  
> >> +int dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows);
> >> +
> >>  
> >>  /* Port operations. */
> >>  
> >> diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
> >> index 0bed7bf61..cf859d1b4 100644
> >> --- a/lib/netdev-offload-provider.h
> >> +++ b/lib/netdev-offload-provider.h
> >> @@ -83,6 +83,10 @@ struct netdev_flow_api {
> >>      int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
> >>                      struct dpif_flow_stats *);
> >>  
> >> +    /* Get the number of flows offloaded to netdev.
> >> +     * Return 0 if successful, otherwise returns a positive errno value. */
> >> +    int (*flow_get_n_flows)(struct netdev *, uint64_t *n_flows);
> >> +
> >>      /* Initializies the netdev flow api.
> >>       * Return 0 if successful, otherwise returns a positive errno value. */
> >>      int (*init_flow_api)(struct netdev *);
> >> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> >> index 2a772a971..f17980a47 100644
> >> --- a/lib/netdev-offload-tc.c
> >> +++ b/lib/netdev-offload-tc.c
> >> @@ -1904,6 +1904,25 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
> >>      return error;
> >>  }
> >>  
> >> +static int
> >> +netdev_tc_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
> >> +{
> >> +    struct ufid_tc_data *data, *next;
> >> +    uint64_t total = 0;
> >> +
> >> +    ovs_mutex_lock(&ufid_lock);
> >> +    HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {
> 
> We're not modifying the hash map here, so it should be simple HMAP_FOR_EACH
> iterator.
> 
> >> +        if (data->netdev != netdev) {
> >> +            continue;
> >> +        }
> >> +        total++;
> 
> Maybe, it's better to turn 'if' condition to positive one and save some lines?
> e.g.,
> 
>     if (data->netdev == netdev) {
>         total++;
>     }
> 
> >> +   }
> >> +   ovs_mutex_unlock(&ufid_lock);
> >> +
> >> +   *n_flows = total;
> >> +   return 0;
> >> +}
> >> +
> >>  static void
> >>  probe_multi_mask_per_prio(int ifindex)
> >>  {
> >> @@ -2044,5 +2063,6 @@ const struct netdev_flow_api netdev_offload_tc = {
> >>     .flow_put = netdev_tc_flow_put,
> >>     .flow_get = netdev_tc_flow_get,
> >>     .flow_del = netdev_tc_flow_del,
> >> +   .flow_get_n_flows = netdev_tc_get_n_flows,
> >>     .init_flow_api = netdev_tc_init_flow_api,
> >>  };
> >> diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
> >> index 2da3bc701..696877058 100644
> >> --- a/lib/netdev-offload.c
> >> +++ b/lib/netdev-offload.c
> >> @@ -280,6 +280,17 @@ netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
> >>             : EOPNOTSUPP;
> >>  }
> >>  
> >> +int
> >> +netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
> >> +{
> >> +    const struct netdev_flow_api *flow_api =
> >> +        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
> >> +
> >> +    return (flow_api && flow_api->flow_get_n_flows)
> >> +           ? flow_api->flow_get_n_flows(netdev, n_flows)
> >> +           : EOPNOTSUPP;
> >> +}
> >> +
> >>  int
> >>  netdev_init_flow_api(struct netdev *netdev)
> >>  {
> >> @@ -602,6 +613,22 @@ netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
> >>      return ret;
> >>  }
> >>  
> >> +int
> >> +netdev_ports_get_n_flows(const char *dpif_type, struct dpif_port *dpif_port,
> 
> 'dpif_port' is here only for the 'port_no'.  It's , probably, better, to just pass
> 'port_no' to this function directly.
> 
> >> +                         uint64_t *n_flows)
> >> +{
> >> +    struct port_to_netdev_data *data;
> >> +    int ret = EOPNOTSUPP;
> >> +
> >> +    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
> >> +    data = netdev_ports_lookup(dpif_port->port_no, dpif_type);
> >> +    if (data) {
> >> +        ret = netdev_flow_get_n_flows(data->netdev, n_flows);
> >> +    }
> >> +    ovs_rwlock_unlock(&netdev_hmap_rwlock);
> >> +    return ret;
> >> +}
> >> +
> >>  odp_port_t
> >>  netdev_ifindex_to_odp_port(int ifindex)
> >>  {
> >> diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
> >> index 4c0ed2ae8..ffd321bfc 100644
> >> --- a/lib/netdev-offload.h
> >> +++ b/lib/netdev-offload.h
> >> @@ -103,6 +103,7 @@ bool netdev_any_oor(void);
> >>  bool netdev_is_flow_api_enabled(void);
> >>  void netdev_set_flow_api_enabled(const struct smap *ovs_other_config);
> >>  bool netdev_is_offload_rebalance_policy_enabled(void);
> >> +int netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows);
> >>  
> >>  struct dpif_port;
> >>  int netdev_ports_insert(struct netdev *, const char *dpif_type,
> >> @@ -124,6 +125,8 @@ int netdev_ports_flow_get(const char *dpif_type, struct match *match,
> >>                            struct dpif_flow_stats *stats,
> >>                            struct dpif_flow_attrs *attrs,
> >>                            struct ofpbuf *buf);
> >> +int netdev_ports_get_n_flows(const char *dpif_type,
> >> +                             struct dpif_port *dpif_port, uint64_t *n_flows);
> >>  
> >>  #ifdef  __cplusplus
> >>  }
> >> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> >> index 19b92dfe0..75496ac6d 100644
> >> --- a/ofproto/ofproto-dpif-upcall.c
> >> +++ b/ofproto/ofproto-dpif-upcall.c
> >> @@ -175,7 +175,6 @@ struct udpif {
> >>  
> >>      /* n_flows_mutex prevents multiple threads updating these concurrently. */
> >>      atomic_uint n_flows;               /* Number of flows in the datapath. */
> >> -    atomic_uint n_offloaded_flows;     /* Number of the offloaded flows. */
> >>      atomic_llong n_flows_timestamp;    /* Last time n_flows was updated. */
> >>      struct ovs_mutex n_flows_mutex;
> >>  
> >> @@ -731,8 +730,6 @@ udpif_get_n_flows(struct udpif *udpif)
> >>          dpif_get_dp_stats(udpif->dpif, &stats);
> >>          flow_count = stats.n_flows;
> >>          atomic_store_relaxed(&udpif->n_flows, flow_count);
> >> -        atomic_store_relaxed(&udpif->n_offloaded_flows,
> >> -                             stats.n_offloaded_flows);
> >>          ovs_mutex_unlock(&udpif->n_flows_mutex);
> >>      } else {
> >>          atomic_read_relaxed(&udpif->n_flows, &flow_count);
> >> @@ -2904,10 +2901,10 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
> >>                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
> >>  {
> >>      struct ds ds = DS_EMPTY_INITIALIZER;
> >> +    uint64_t n_offloaded_flows;
> >>      struct udpif *udpif;
> >>  
> >>      LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
> >> -        unsigned int n_offloaded_flows;
> >>          unsigned int flow_limit;
> >>          bool ufid_enabled;
> >>          size_t i;
> >> @@ -2919,8 +2916,9 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
> >>          ds_put_format(&ds, "  flows         : (current %lu)"
> >>              " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
> >>              udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
> >> -        atomic_read_relaxed(&udpif->n_offloaded_flows, &n_offloaded_flows);
> >> -        ds_put_format(&ds, "  offloaded flows : %u\n", n_offloaded_flows);
> >> +        if (!dpif_get_dp_offloaded_flows(udpif->dpif, &n_offloaded_flows)) {
> >> +            ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);
> 
> This causes build failure on 32bit:
> ofproto/ofproto-dpif-upcall.c:2920:55: error: format ‘%ld’ expects argument of type
> ‘long int’, but argument 3 has type ‘uint64_t {aka long long unsigned int}’ [-Werror=format=]
>              ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);
>                                                      ~~^
>                                                      %lld
> 
> You should use PRIu64 instead.
> 
> >> +        }
> >>          ds_put_format(&ds, "  dump duration : %lldms\n", udpif->dump_duration);
> >>          ds_put_format(&ds, "  ufid enabled : ");
> >>          if (ufid_enabled) {
> >> diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
> >> index 379a8a5e9..c03ffd598 100644
> >> --- a/tests/system-offloads-traffic.at
> >> +++ b/tests/system-offloads-traffic.at
> >> @@ -32,6 +32,8 @@ in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:
> >>  
> >>  AT_CHECK([ovs-appctl dpctl/dump-flows type=offloaded], [0], [])
> >>  
> >> +AT_CHECK([ovs-appctl upcall/show | grep "offloaded flows : 0"], [1], [ignore])
> > 
> > With this change, "offloaded flows" will not be displayed if hw offload
> > is disabled.
> 
> Maybe this could be:
> 
> AT_CHECK([test $(ovs-appctl upcall/show | grep -c "offloaded flows") -eq 0 ])
> 
> ?
> 
> > 
> >> +
> >>  OVS_TRAFFIC_VSWITCHD_STOP
> >>  AT_CLEANUP
> >>  
> >> @@ -64,5 +66,7 @@ in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:
> >>  in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output
> >>  ])
> >>  
> >> +AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore])
> > 
> > It's 8 offloaded rules, but I got another value sometimes, which was
> > caused by testing environment. So I don't check the exact value here.
> 
> Yeah, there could be some random traffic, since it's a system test, e.g. some ARP
> and ipv6 packets.  If you want a precise check, you could still add an implementation
> for netdev_offload_dummy and add some checks to 'dpif-netdev - partial hw offload' tests
> in tests/dpif-netdev.at.  It supports only partial offloading, but I don't think that
> really matters here.
diff mbox series

Patch

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 6858ba612..2f881e4fa 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -208,7 +208,6 @@  struct dpif_netlink {
     /* Change notification. */
     struct nl_sock *port_notifier; /* vport multicast group subscriber. */
     bool refresh_channels;
-    struct atomic_count n_offloaded_flows;
 };
 
 static void report_loss(struct dpif_netlink *, struct dpif_channel *,
@@ -654,7 +653,6 @@  dpif_netlink_run(struct dpif *dpif_)
 static int
 dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
 {
-    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
     struct dpif_netlink_dp dp;
     struct ofpbuf *buf;
     int error;
@@ -680,7 +678,6 @@  dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
         }
         ofpbuf_delete(buf);
     }
-    stats->n_offloaded_flows = atomic_count_get(&dpif->n_offloaded_flows);
     return error;
 }
 
@@ -2192,9 +2189,6 @@  try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
         }
 
         err = parse_flow_put(dpif, put);
-        if (!err && (put->flags & DPIF_FP_CREATE)) {
-            atomic_count_inc(&dpif->n_offloaded_flows);
-        }
         log_flow_put_message(&dpif->dpif, &this_module, put, 0);
         break;
     }
@@ -2209,9 +2203,6 @@  try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
                                 dpif_normalize_type(dpif_type(&dpif->dpif)),
                                 del->ufid,
                                 del->stats);
-        if (!err) {
-            atomic_count_dec(&dpif->n_offloaded_flows);
-        }
         log_flow_del_message(&dpif->dpif, &this_module, del, 0);
         break;
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index ac2860764..7f01aa48b 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -2018,3 +2018,25 @@  dpif_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
            ? dpif->dpif_class->bond_stats_get(dpif, bond_id, n_bytes)
            : EOPNOTSUPP;
 }
+
+int
+dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows)
+{
+    const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
+    struct dpif_port_dump port_dump;
+    struct dpif_port dpif_port;
+    int ret, n_devs = 0;
+    uint64_t nflows;
+
+    *n_flows = 0;
+    DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
+        ret = netdev_ports_get_n_flows(dpif_type_str, &dpif_port, &nflows);
+        if (!ret) {
+            *n_flows += nflows;
+        } else if (ret == EOPNOTSUPP) {
+            continue;
+        }
+        n_devs++;
+    }
+    return n_devs ? 0 : EOPNOTSUPP;
+}
diff --git a/lib/dpif.h b/lib/dpif.h
index 7ef148c6d..9d0d662fc 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -429,7 +429,6 @@  struct dpif_dp_stats {
     uint64_t n_missed;          /* Number of flow table misses. */
     uint64_t n_lost;            /* Number of misses not sent to userspace. */
     uint64_t n_flows;           /* Number of flows present. */
-    uint64_t n_offloaded_flows; /* Number of offloaded flows present. */
     uint64_t n_mask_hit;        /* Number of mega flow masks visited for
                                    flow table matches. */
     uint32_t n_masks;           /* Number of mega flow masks. */
@@ -438,6 +437,8 @@  int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *);
 
 int dpif_set_features(struct dpif *, uint32_t new_features);
 
+int dpif_get_dp_offloaded_flows(struct dpif *dpif, uint64_t *n_flows);
+
 
 /* Port operations. */
 
diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
index 0bed7bf61..cf859d1b4 100644
--- a/lib/netdev-offload-provider.h
+++ b/lib/netdev-offload-provider.h
@@ -83,6 +83,10 @@  struct netdev_flow_api {
     int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
                     struct dpif_flow_stats *);
 
+    /* Get the number of flows offloaded to netdev.
+     * Return 0 if successful, otherwise returns a positive errno value. */
+    int (*flow_get_n_flows)(struct netdev *, uint64_t *n_flows);
+
     /* Initializies the netdev flow api.
      * Return 0 if successful, otherwise returns a positive errno value. */
     int (*init_flow_api)(struct netdev *);
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 2a772a971..f17980a47 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -1904,6 +1904,25 @@  netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
     return error;
 }
 
+static int
+netdev_tc_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
+{
+    struct ufid_tc_data *data, *next;
+    uint64_t total = 0;
+
+    ovs_mutex_lock(&ufid_lock);
+    HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {
+        if (data->netdev != netdev) {
+            continue;
+        }
+        total++;
+   }
+   ovs_mutex_unlock(&ufid_lock);
+
+   *n_flows = total;
+   return 0;
+}
+
 static void
 probe_multi_mask_per_prio(int ifindex)
 {
@@ -2044,5 +2063,6 @@  const struct netdev_flow_api netdev_offload_tc = {
    .flow_put = netdev_tc_flow_put,
    .flow_get = netdev_tc_flow_get,
    .flow_del = netdev_tc_flow_del,
+   .flow_get_n_flows = netdev_tc_get_n_flows,
    .init_flow_api = netdev_tc_init_flow_api,
 };
diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c
index 2da3bc701..696877058 100644
--- a/lib/netdev-offload.c
+++ b/lib/netdev-offload.c
@@ -280,6 +280,17 @@  netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
            : EOPNOTSUPP;
 }
 
+int
+netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
+{
+    const struct netdev_flow_api *flow_api =
+        ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
+
+    return (flow_api && flow_api->flow_get_n_flows)
+           ? flow_api->flow_get_n_flows(netdev, n_flows)
+           : EOPNOTSUPP;
+}
+
 int
 netdev_init_flow_api(struct netdev *netdev)
 {
@@ -602,6 +613,22 @@  netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
     return ret;
 }
 
+int
+netdev_ports_get_n_flows(const char *dpif_type, struct dpif_port *dpif_port,
+                         uint64_t *n_flows)
+{
+    struct port_to_netdev_data *data;
+    int ret = EOPNOTSUPP;
+
+    ovs_rwlock_rdlock(&netdev_hmap_rwlock);
+    data = netdev_ports_lookup(dpif_port->port_no, dpif_type);
+    if (data) {
+        ret = netdev_flow_get_n_flows(data->netdev, n_flows);
+    }
+    ovs_rwlock_unlock(&netdev_hmap_rwlock);
+    return ret;
+}
+
 odp_port_t
 netdev_ifindex_to_odp_port(int ifindex)
 {
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 4c0ed2ae8..ffd321bfc 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -103,6 +103,7 @@  bool netdev_any_oor(void);
 bool netdev_is_flow_api_enabled(void);
 void netdev_set_flow_api_enabled(const struct smap *ovs_other_config);
 bool netdev_is_offload_rebalance_policy_enabled(void);
+int netdev_flow_get_n_flows(struct netdev *netdev, uint64_t *n_flows);
 
 struct dpif_port;
 int netdev_ports_insert(struct netdev *, const char *dpif_type,
@@ -124,6 +125,8 @@  int netdev_ports_flow_get(const char *dpif_type, struct match *match,
                           struct dpif_flow_stats *stats,
                           struct dpif_flow_attrs *attrs,
                           struct ofpbuf *buf);
+int netdev_ports_get_n_flows(const char *dpif_type,
+                             struct dpif_port *dpif_port, uint64_t *n_flows);
 
 #ifdef  __cplusplus
 }
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 19b92dfe0..75496ac6d 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -175,7 +175,6 @@  struct udpif {
 
     /* n_flows_mutex prevents multiple threads updating these concurrently. */
     atomic_uint n_flows;               /* Number of flows in the datapath. */
-    atomic_uint n_offloaded_flows;     /* Number of the offloaded flows. */
     atomic_llong n_flows_timestamp;    /* Last time n_flows was updated. */
     struct ovs_mutex n_flows_mutex;
 
@@ -731,8 +730,6 @@  udpif_get_n_flows(struct udpif *udpif)
         dpif_get_dp_stats(udpif->dpif, &stats);
         flow_count = stats.n_flows;
         atomic_store_relaxed(&udpif->n_flows, flow_count);
-        atomic_store_relaxed(&udpif->n_offloaded_flows,
-                             stats.n_offloaded_flows);
         ovs_mutex_unlock(&udpif->n_flows_mutex);
     } else {
         atomic_read_relaxed(&udpif->n_flows, &flow_count);
@@ -2904,10 +2901,10 @@  upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
+    uint64_t n_offloaded_flows;
     struct udpif *udpif;
 
     LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
-        unsigned int n_offloaded_flows;
         unsigned int flow_limit;
         bool ufid_enabled;
         size_t i;
@@ -2919,8 +2916,9 @@  upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         ds_put_format(&ds, "  flows         : (current %lu)"
             " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
             udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
-        atomic_read_relaxed(&udpif->n_offloaded_flows, &n_offloaded_flows);
-        ds_put_format(&ds, "  offloaded flows : %u\n", n_offloaded_flows);
+        if (!dpif_get_dp_offloaded_flows(udpif->dpif, &n_offloaded_flows)) {
+            ds_put_format(&ds, "  offloaded flows : %ld\n", n_offloaded_flows);
+        }
         ds_put_format(&ds, "  dump duration : %lldms\n", udpif->dump_duration);
         ds_put_format(&ds, "  ufid enabled : ");
         if (ufid_enabled) {
diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
index 379a8a5e9..c03ffd598 100644
--- a/tests/system-offloads-traffic.at
+++ b/tests/system-offloads-traffic.at
@@ -32,6 +32,8 @@  in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:882, used:
 
 AT_CHECK([ovs-appctl dpctl/dump-flows type=offloaded], [0], [])
 
+AT_CHECK([ovs-appctl upcall/show | grep "offloaded flows : 0"], [1], [ignore])
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -64,5 +66,7 @@  in_port(2),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:
 in_port(3),eth(macs),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.001s, actions:output
 ])
 
+AT_CHECK([ovs-appctl upcall/show | grep -E "offloaded flows : [[1-9]]"], [0], [ignore])
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP