diff mbox series

[ovs-dev,RFC] netdev-dpdk: Expose per rxq/txq basic statistics.

Message ID 20211015150406.12949-1-david.marchand@redhat.com
State Changes Requested
Headers show
Series [ovs-dev,RFC] netdev-dpdk: Expose per rxq/txq basic statistics. | expand

Commit Message

David Marchand Oct. 15, 2021, 3:04 p.m. UTC
When troubleshooting multiqueue setups, having per queue statistics helps
checking packets repartition in rx and tx queues.

Per queue statistics are exported by most DPDK drivers (with capability
RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
it exposes, there is nothing to request in DPDK API.

Extend existing filter with a regular expression.
string_ends_with() helper is not used anymore, and removed as a
consequence.

Querying statistics with
$ ovs-vsctl get interface dpdk0 statistics | \
  sed -e 's#[{}]##g' -e 's#, #\n#g'

and comparing gives:
@@ -13,7 +13,12 @@
 rx_phy_crc_errors=0
 rx_phy_in_range_len_errors=0
 rx_phy_symbol_errors=0
+rx_q0_bytes=0
 rx_q0_errors=0
+rx_q0_packets=0
+rx_q1_bytes=0
+rx_q1_errors=0
+rx_q1_packets=0
 rx_wqe_errors=0
 tx_broadcast_packets=0
 tx_bytes=0
@@ -27,3 +32,13 @@
 tx_pp_rearm_queue_errors=0
 tx_pp_timestamp_future_errors=0
 tx_pp_timestamp_past_errors=0
+tx_q0_bytes=0
+tx_q0_packets=0
+tx_q1_bytes=0
+tx_q1_packets=0
+tx_q2_bytes=0
+tx_q2_packets=0
+tx_q3_bytes=0
+tx_q3_packets=0
+tx_q4_bytes=0
+tx_q4_packets=0

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Sending this as a RFC for feedback.
vhost user ports are left untouched for now, but could be added in the
future.

---
 lib/netdev-dpdk.c | 32 ++++++++++++++++++++++++++------
 lib/util.c        | 13 -------------
 lib/util.h        |  2 --
 3 files changed, 26 insertions(+), 21 deletions(-)

Comments

Maxime Coquelin Nov. 18, 2021, 9:20 a.m. UTC | #1
Hi,

On 10/15/21 17:04, David Marchand wrote:
> When troubleshooting multiqueue setups, having per queue statistics helps
> checking packets repartition in rx and tx queues.
> 
> Per queue statistics are exported by most DPDK drivers (with capability
> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
> it exposes, there is nothing to request in DPDK API.
> 
> Extend existing filter with a regular expression.
> string_ends_with() helper is not used anymore, and removed as a
> consequence.
> 
> Querying statistics with
> $ ovs-vsctl get interface dpdk0 statistics | \
>    sed -e 's#[{}]##g' -e 's#, #\n#g'
> 
> and comparing gives:
> @@ -13,7 +13,12 @@
>   rx_phy_crc_errors=0
>   rx_phy_in_range_len_errors=0
>   rx_phy_symbol_errors=0
> +rx_q0_bytes=0
>   rx_q0_errors=0
> +rx_q0_packets=0
> +rx_q1_bytes=0
> +rx_q1_errors=0
> +rx_q1_packets=0
>   rx_wqe_errors=0
>   tx_broadcast_packets=0
>   tx_bytes=0
> @@ -27,3 +32,13 @@
>   tx_pp_rearm_queue_errors=0
>   tx_pp_timestamp_future_errors=0
>   tx_pp_timestamp_past_errors=0
> +tx_q0_bytes=0
> +tx_q0_packets=0
> +tx_q1_bytes=0
> +tx_q1_packets=0
> +tx_q2_bytes=0
> +tx_q2_packets=0
> +tx_q3_bytes=0
> +tx_q3_packets=0
> +tx_q4_bytes=0
> +tx_q4_packets=0
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
> Sending this as a RFC for feedback.

I think this is a very good idea.

> vhost user ports are left untouched for now, but could be added in the
> future.

I actually need it for Vhost-user ports for a feature I'm working on.
Do you want me to implement it for Vhost-user ports on top of your
patch, or you prefer to support both in a v1?

> ---
>   lib/netdev-dpdk.c | 32 ++++++++++++++++++++++++++------
>   lib/util.c        | 13 -------------
>   lib/util.h        |  2 --
>   3 files changed, 26 insertions(+), 21 deletions(-)
> 

For this part:
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>

Thanks,
Maxime
Kevin Traynor Nov. 18, 2021, 3:31 p.m. UTC | #2
On 15/10/2021 16:04, David Marchand wrote:
> When troubleshooting multiqueue setups, having per queue statistics helps
> checking packets repartition in rx and tx queues.
> 
> Per queue statistics are exported by most DPDK drivers (with capability
> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
> it exposes, there is nothing to request in DPDK API.
> 
> Extend existing filter with a regular expression.
> string_ends_with() helper is not used anymore, and removed as a
> consequence.
> 
> Querying statistics with
> $ ovs-vsctl get interface dpdk0 statistics | \
>    sed -e 's#[{}]##g' -e 's#, #\n#g'
> 
> and comparing gives:
> @@ -13,7 +13,12 @@
>   rx_phy_crc_errors=0
>   rx_phy_in_range_len_errors=0
>   rx_phy_symbol_errors=0
> +rx_q0_bytes=0
>   rx_q0_errors=0
> +rx_q0_packets=0
> +rx_q1_bytes=0
> +rx_q1_errors=0
> +rx_q1_packets=0
>   rx_wqe_errors=0
>   tx_broadcast_packets=0
>   tx_bytes=0
> @@ -27,3 +32,13 @@
>   tx_pp_rearm_queue_errors=0
>   tx_pp_timestamp_future_errors=0
>   tx_pp_timestamp_past_errors=0
> +tx_q0_bytes=0
> +tx_q0_packets=0
> +tx_q1_bytes=0
> +tx_q1_packets=0
> +tx_q2_bytes=0
> +tx_q2_packets=0
> +tx_q3_bytes=0
> +tx_q3_packets=0
> +tx_q4_bytes=0
> +tx_q4_packets=0
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>

The patch looks good but I tested with with ixgbe, and seeing something 
I am unsure of. I changed the number of rxqs to 11 but I am only seeing 
stats for q0. i'm not sure if this because there was no traffic yet 
(even though there is also no traffic on q0). i will set up a traffic 
gen and see what happens when the q1-q10 receives traffic.

One other comment below.

# /root/ovs/utilities/ovs-appctl dpif-netdev/pmd-rxq-show
pmd thread numa_id 0 core_id 8:
   isolated : false
   port: myport            queue-id:  0 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  3 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  4 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  7 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  8 (enabled)   pmd usage:  0 %
   port: urport            queue-id:  0 (enabled)   pmd usage:  0 %
   overhead:  0 %
pmd thread numa_id 0 core_id 10:
   isolated : false
   port: myport            queue-id:  1 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  2 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  5 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  6 (enabled)   pmd usage:  0 %
   port: myport            queue-id:  9 (enabled)   pmd usage:  0 %
   port: myport            queue-id: 10 (enabled)   pmd usage:  0 %
   overhead:  0 %

# /root/ovs/utilities/ovs-vsctl get Interface myport statistics
{flow_director_filter_add_errors=0, 
flow_director_filter_remove_errors=0, mac_local_errors=0, 
mac_remote_errors=0, ovs_rx_qos_drops=0, ovs_tx_failure_drops=0, 
ovs_tx_invalid_hwol_drops=0, ovs_tx_mtu_exceeded_drops=0, 
ovs_tx_qos_drops=0, rx_128_to_255_packets=0, rx_1_to_64_packets=0, 
rx_256_to_511_packets=0, rx_512_to_1023_packets=0, 
rx_65_to_127_packets=0, rx_broadcast_packets=0, rx_bytes=0, 
rx_crc_errors=0, rx_dropped=0, rx_errors=0, rx_fcoe_crc_errors=0, 
rx_fcoe_dropped=0, rx_fcoe_mbuf_allocation_errors=0, 
rx_fragment_errors=0, rx_illegal_byte_errors=0, rx_jabber_errors=0, 
rx_length_errors=0, rx_mac_short_packet_dropped=0, 
rx_management_dropped=0, rx_management_packets=0, 
rx_mbuf_allocation_errors=0, rx_missed_errors=0, rx_oversize_errors=0, 
rx_packets=0, rx_priority0_dropped=0, 
rx_priority0_mbuf_allocation_errors=0, rx_priority1_dropped=0, 
rx_priority1_mbuf_allocation_errors=0, rx_priority2_dropped=0, 
rx_priority2_mbuf_allocation_errors=0, rx_priority3_dropped=0, 
rx_priority3_mbuf_allocation_errors=0, rx_priority4_dropped=0, 
rx_priority4_mbuf_allocation_errors=0, rx_priority5_dropped=0, 
rx_priority5_mbuf_allocation_errors=0, rx_priority6_dropped=0, 
rx_priority6_mbuf_allocation_errors=0, rx_priority7_dropped=0, 
rx_priority7_mbuf_allocation_errors=0, rx_q0_bytes=0, rx_q0_errors=0, 
rx_q0_packets=0, rx_undersize_errors=0, tx_128_to_255_packets=0, 
tx_1_to_64_packets=0, tx_256_to_511_packets=0, tx_512_to_1023_packets=0, 
tx_65_to_127_packets=0, tx_broadcast_packets=0, tx_bytes=0, 
tx_dropped=0, tx_errors=0, tx_management_packets=0, 
tx_multicast_packets=0, tx_packets=0, tx_q0_bytes=0, tx_q0_packets=0, 
tx_q1_bytes=0, tx_q1_packets=0, tx_q2_bytes=0, tx_q2_packets=0}


> ---
> Sending this as a RFC for feedback.
> vhost user ports are left untouched for now, but could be added in the
> future.
> 
> ---
>   lib/netdev-dpdk.c | 32 ++++++++++++++++++++++++++------
>   lib/util.c        | 13 -------------
>   lib/util.h        |  2 --
>   3 files changed, 26 insertions(+), 21 deletions(-)
> 
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index ca92c947a2..12447cc4b2 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -18,6 +18,7 @@
>   #include "netdev-dpdk.h"
>   
>   #include <errno.h>
> +#include <regex.h>
>   #include <signal.h>
>   #include <stdlib.h>
>   #include <string.h>
> @@ -1583,6 +1584,13 @@ netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
>       return dev->rte_xstats_names[id].name;
>   }
>   
> +/* We filter out everything except per rxq/txq basic stats, and dropped,
> + * error and management counters.
> + * Note: rx_qX_errors is handled by the _errors$ pattern.
> + */
> +#define DPDK_STATS_REGEX_FILTER \
> +    "(^(rx_q|tx_q)[0-9]*_(packets|bytes)$|_errors$|_dropped$|_management_)"
> +

iirc, there was some pmds that were not conforming correctly with this 
rx_q syntax, but I *think* they were fixed a few releases ago in DPDK. 
Just mentioning because if there was still some, they could be fixed or 
the regex could be expanded to capture them.

>   static bool
>   netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
>       OVS_REQUIRES(dev->mutex)
> @@ -1617,6 +1625,20 @@ netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
>                                               sizeof *dev->rte_xstats_names);
>   
>               if (dev->rte_xstats_names) {
> +                int err;
> +                regex_t r;
> +
> +                err = regcomp(&r, DPDK_STATS_REGEX_FILTER, REG_EXTENDED);
> +                if (err != 0) {
> +                    size_t errbuf_size = regerror(err, &r, NULL, 0);
> +                    char *errbuf = xcalloc(1, errbuf_size);
> +
> +                    regerror(err, &r, errbuf, errbuf_size);
> +                    VLOG_DBG("Could not setup stats regexp: %s", errbuf);
> +                    free(errbuf);
> +                    goto out;
> +                }
> +
>                   /* Retreive xstats names */
>                   rte_xstats_len =
>                           rte_eth_xstats_get_names(dev->port_id,
> @@ -1626,10 +1648,12 @@ netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
>                   if (rte_xstats_len < 0) {
>                       VLOG_WARN("Cannot get XSTATS names for port: "
>                                 DPDK_PORT_ID_FMT, dev->port_id);
> +                    regfree(&r);
>                       goto out;
>                   } else if (rte_xstats_len != dev->rte_xstats_names_size) {
>                       VLOG_WARN("XSTATS size doesn't match for port: "
>                                 DPDK_PORT_ID_FMT, dev->port_id);
> +                    regfree(&r);
>                       goto out;
>                   }
>   
> @@ -1648,12 +1672,7 @@ netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
>                       for (uint32_t i = 0; i < rte_xstats_len; i++) {
>                           id = rte_xstats[i].id;
>                           name = netdev_dpdk_get_xstat_name(dev, id);
> -                        /* We need to filter out everything except
> -                         * dropped, error and management counters */
> -                        if (string_ends_with(name, "_errors") ||
> -                            strstr(name, "_management_") ||
> -                            string_ends_with(name, "_dropped")) {
> -
> +                        if (regexec(&r, name, 0, NULL, 0) == 0) {
>                               dev->rte_xstats_ids[xstats_no] = id;
>                               xstats_no++;
>                           }
> @@ -1666,6 +1685,7 @@ netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
>                   }
>   
>                   free(rte_xstats);
> +                regfree(&r);
>               }
>           }
>       } else {
> diff --git a/lib/util.c b/lib/util.c
> index 1195c79821..34755a4dad 100644
> --- a/lib/util.c
> +++ b/lib/util.c
> @@ -405,19 +405,6 @@ ovs_strzcpy(char *dst, const char *src, size_t size)
>       }
>   }
>   
> -/*
> - * Returns true if 'str' ends with given 'suffix'.
> - */
> -int
> -string_ends_with(const char *str, const char *suffix)
> -{
> -    int str_len = strlen(str);
> -    int suffix_len = strlen(suffix);
> -
> -    return (str_len >= suffix_len) &&
> -           (0 == strcmp(str + (str_len - suffix_len), suffix));
> -}
> -
>   /* Prints 'format' on stderr, formatting it like printf() does.  If 'err_no' is
>    * nonzero, then it is formatted with ovs_retval_to_string() and appended to
>    * the message inside parentheses.  Then, terminates with abort().
> diff --git a/lib/util.h b/lib/util.h
> index aea19d45fa..4ad6e28274 100644
> --- a/lib/util.h
> +++ b/lib/util.h
> @@ -183,8 +183,6 @@ void free_cacheline(void *);
>   void ovs_strlcpy(char *dst, const char *src, size_t size);
>   void ovs_strzcpy(char *dst, const char *src, size_t size);
>   
> -int string_ends_with(const char *str, const char *suffix);
> -
>   void *xmalloc_pagealign(size_t) MALLOC_LIKE;
>   void free_pagealign(void *);
>   void *xmalloc_size_align(size_t, size_t) MALLOC_LIKE;
>
Maxime Coquelin Nov. 19, 2021, 9:40 a.m. UTC | #3
Hi Kevin, David,

On 11/18/21 16:31, Kevin Traynor wrote:
> On 15/10/2021 16:04, David Marchand wrote:
>> When troubleshooting multiqueue setups, having per queue statistics helps
>> checking packets repartition in rx and tx queues.
>>
>> Per queue statistics are exported by most DPDK drivers (with capability
>> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
>> it exposes, there is nothing to request in DPDK API.
>>
>> Extend existing filter with a regular expression.
>> string_ends_with() helper is not used anymore, and removed as a
>> consequence.
>>
>> Querying statistics with
>> $ ovs-vsctl get interface dpdk0 statistics | \
>>    sed -e 's#[{}]##g' -e 's#, #\n#g'
>>
>> and comparing gives:
>> @@ -13,7 +13,12 @@
>>   rx_phy_crc_errors=0
>>   rx_phy_in_range_len_errors=0
>>   rx_phy_symbol_errors=0
>> +rx_q0_bytes=0
>>   rx_q0_errors=0
>> +rx_q0_packets=0
>> +rx_q1_bytes=0
>> +rx_q1_errors=0
>> +rx_q1_packets=0
>>   rx_wqe_errors=0
>>   tx_broadcast_packets=0
>>   tx_bytes=0
>> @@ -27,3 +32,13 @@
>>   tx_pp_rearm_queue_errors=0
>>   tx_pp_timestamp_future_errors=0
>>   tx_pp_timestamp_past_errors=0
>> +tx_q0_bytes=0
>> +tx_q0_packets=0
>> +tx_q1_bytes=0
>> +tx_q1_packets=0
>> +tx_q2_bytes=0
>> +tx_q2_packets=0
>> +tx_q3_bytes=0
>> +tx_q3_packets=0
>> +tx_q4_bytes=0
>> +tx_q4_packets=0
>>
>> Signed-off-by: David Marchand <david.marchand@redhat.com>
> 
> The patch looks good but I tested with with ixgbe, and seeing something 
> I am unsure of. I changed the number of rxqs to 11 but I am only seeing 
> stats for q0. i'm not sure if this because there was no traffic yet 
> (even though there is also no traffic on q0). i will set up a traffic 
> gen and see what happens when the q1-q10 receives traffic.

I think the problem is that when port reconfiguration is called because
the number of queues changed, netdev_dpdk_clear_xstats() is not called.

So the next time netdev_dpdk_configure_xstats() is called, it returns
true early as it is already configured. So old configuration is keeped.

When increasing the number of queues, I think we get your behavior, i.e.
only previous number of queues counters are disaplayed. But when
decreasing the number of queues, I think rte_eth_xstats_get_by_id() will
fail.

I think calling netdev_dpdk_clear_xstats() in netdev_dpdk_reconfigure()
should fix your issue.

Maxime
David Marchand Nov. 19, 2021, 10:31 a.m. UTC | #4
On Fri, Nov 19, 2021 at 10:40 AM Maxime Coquelin
<maxime.coquelin@redhat.com> wrote:
>
> Hi Kevin, David,
>
> On 11/18/21 16:31, Kevin Traynor wrote:
> > On 15/10/2021 16:04, David Marchand wrote:
> >> When troubleshooting multiqueue setups, having per queue statistics helps
> >> checking packets repartition in rx and tx queues.
> >>
> >> Per queue statistics are exported by most DPDK drivers (with capability
> >> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
> >> it exposes, there is nothing to request in DPDK API.
> >>
> >> Extend existing filter with a regular expression.
> >> string_ends_with() helper is not used anymore, and removed as a
> >> consequence.
> >>
> >> Querying statistics with
> >> $ ovs-vsctl get interface dpdk0 statistics | \
> >>    sed -e 's#[{}]##g' -e 's#, #\n#g'
> >>
> >> and comparing gives:
> >> @@ -13,7 +13,12 @@
> >>   rx_phy_crc_errors=0
> >>   rx_phy_in_range_len_errors=0
> >>   rx_phy_symbol_errors=0
> >> +rx_q0_bytes=0
> >>   rx_q0_errors=0
> >> +rx_q0_packets=0
> >> +rx_q1_bytes=0
> >> +rx_q1_errors=0
> >> +rx_q1_packets=0
> >>   rx_wqe_errors=0
> >>   tx_broadcast_packets=0
> >>   tx_bytes=0
> >> @@ -27,3 +32,13 @@
> >>   tx_pp_rearm_queue_errors=0
> >>   tx_pp_timestamp_future_errors=0
> >>   tx_pp_timestamp_past_errors=0
> >> +tx_q0_bytes=0
> >> +tx_q0_packets=0
> >> +tx_q1_bytes=0
> >> +tx_q1_packets=0
> >> +tx_q2_bytes=0
> >> +tx_q2_packets=0
> >> +tx_q3_bytes=0
> >> +tx_q3_packets=0
> >> +tx_q4_bytes=0
> >> +tx_q4_packets=0
> >>
> >> Signed-off-by: David Marchand <david.marchand@redhat.com>
> >
> > The patch looks good but I tested with with ixgbe, and seeing something
> > I am unsure of. I changed the number of rxqs to 11 but I am only seeing
> > stats for q0. i'm not sure if this because there was no traffic yet
> > (even though there is also no traffic on q0). i will set up a traffic
> > gen and see what happens when the q1-q10 receives traffic.
>
> I think the problem is that when port reconfiguration is called because
> the number of queues changed, netdev_dpdk_clear_xstats() is not called.
>
> So the next time netdev_dpdk_configure_xstats() is called, it returns
> true early as it is already configured. So old configuration is keeped.
>
> When increasing the number of queues, I think we get your behavior, i.e.
> only previous number of queues counters are disaplayed. But when
> decreasing the number of queues, I think rte_eth_xstats_get_by_id() will
> fail.
>
> I think calling netdev_dpdk_clear_xstats() in netdev_dpdk_reconfigure()
> should fix your issue.

Thanks for the analysis.

I'll need a bit more time to dig in (I don't understand the logic
behind the configure_xstats/clear_xstats stuff).
In any case, it seems this is a bug predating my patch.
Kevin Traynor Nov. 19, 2021, 12:08 p.m. UTC | #5
On 19/11/2021 10:31, David Marchand wrote:
> On Fri, Nov 19, 2021 at 10:40 AM Maxime Coquelin
> <maxime.coquelin@redhat.com> wrote:
>>
>> Hi Kevin, David,
>>
>> On 11/18/21 16:31, Kevin Traynor wrote:
>>> On 15/10/2021 16:04, David Marchand wrote:
>>>> When troubleshooting multiqueue setups, having per queue statistics helps
>>>> checking packets repartition in rx and tx queues.
>>>>
>>>> Per queue statistics are exported by most DPDK drivers (with capability
>>>> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
>>>> it exposes, there is nothing to request in DPDK API.
>>>>
>>>> Extend existing filter with a regular expression.
>>>> string_ends_with() helper is not used anymore, and removed as a
>>>> consequence.
>>>>
>>>> Querying statistics with
>>>> $ ovs-vsctl get interface dpdk0 statistics | \
>>>>     sed -e 's#[{}]##g' -e 's#, #\n#g'
>>>>
>>>> and comparing gives:
>>>> @@ -13,7 +13,12 @@
>>>>    rx_phy_crc_errors=0
>>>>    rx_phy_in_range_len_errors=0
>>>>    rx_phy_symbol_errors=0
>>>> +rx_q0_bytes=0
>>>>    rx_q0_errors=0
>>>> +rx_q0_packets=0
>>>> +rx_q1_bytes=0
>>>> +rx_q1_errors=0
>>>> +rx_q1_packets=0
>>>>    rx_wqe_errors=0
>>>>    tx_broadcast_packets=0
>>>>    tx_bytes=0
>>>> @@ -27,3 +32,13 @@
>>>>    tx_pp_rearm_queue_errors=0
>>>>    tx_pp_timestamp_future_errors=0
>>>>    tx_pp_timestamp_past_errors=0
>>>> +tx_q0_bytes=0
>>>> +tx_q0_packets=0
>>>> +tx_q1_bytes=0
>>>> +tx_q1_packets=0
>>>> +tx_q2_bytes=0
>>>> +tx_q2_packets=0
>>>> +tx_q3_bytes=0
>>>> +tx_q3_packets=0
>>>> +tx_q4_bytes=0
>>>> +tx_q4_packets=0
>>>>
>>>> Signed-off-by: David Marchand <david.marchand@redhat.com>
>>>
>>> The patch looks good but I tested with with ixgbe, and seeing something
>>> I am unsure of. I changed the number of rxqs to 11 but I am only seeing
>>> stats for q0. i'm not sure if this because there was no traffic yet
>>> (even though there is also no traffic on q0). i will set up a traffic
>>> gen and see what happens when the q1-q10 receives traffic.
>>
>> I think the problem is that when port reconfiguration is called because
>> the number of queues changed, netdev_dpdk_clear_xstats() is not called.
>>
>> So the next time netdev_dpdk_configure_xstats() is called, it returns
>> true early as it is already configured. So old configuration is keeped.
>>
>> When increasing the number of queues, I think we get your behavior, i.e.
>> only previous number of queues counters are disaplayed. But when
>> decreasing the number of queues, I think rte_eth_xstats_get_by_id() will
>> fail.
>>
>> I think calling netdev_dpdk_clear_xstats() in netdev_dpdk_reconfigure()
>> should fix your issue.
> 
> Thanks for the analysis.
> 
> I'll need a bit more time to dig in (I don't understand the logic
> behind the configure_xstats/clear_xstats stuff).
> In any case, it seems this is a bug predating my patch.
> 
> 

Thanks Maxime. Yes, that seems to be explanation for it.

The issue was also present with the txqs when the number of PMDs was 
increased/decreased, hence changing requested txqs. I added below and it 
is showing the right amount of rx/tx queues now.

I'm not sure it's an existing bug that we'd backport a fix for as the 
num queues changing wouldn't have previously impacted because of the 
filter. It seems more like a short cut that we now can't keep :-)

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index ca92c947a..720041c34 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -1876,4 +1876,5 @@ dpdk_set_rxq_config(struct netdev_dpdk *dev, const 
struct smap *args)
          dev->requested_n_rxq = new_n_rxq;
          netdev_request_reconfigure(&dev->up);
+        netdev_dpdk_clear_xstats(dev);
      }
  }
@@ -2109,4 +2110,5 @@ netdev_dpdk_set_tx_multiq(struct netdev *netdev, 
unsigned int n_txq)
      dev->requested_n_txq = n_txq;
      netdev_request_reconfigure(netdev);
+    netdev_dpdk_clear_xstats(dev);
Maxime Coquelin Nov. 19, 2021, 12:45 p.m. UTC | #6
On 11/19/21 13:08, Kevin Traynor wrote:
> On 19/11/2021 10:31, David Marchand wrote:
>> On Fri, Nov 19, 2021 at 10:40 AM Maxime Coquelin
>> <maxime.coquelin@redhat.com> wrote:
>>>
>>> Hi Kevin, David,
>>>
>>> On 11/18/21 16:31, Kevin Traynor wrote:
>>>> On 15/10/2021 16:04, David Marchand wrote:
>>>>> When troubleshooting multiqueue setups, having per queue statistics 
>>>>> helps
>>>>> checking packets repartition in rx and tx queues.
>>>>>
>>>>> Per queue statistics are exported by most DPDK drivers (with 
>>>>> capability
>>>>> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters 
>>>>> statistics
>>>>> it exposes, there is nothing to request in DPDK API.
>>>>>
>>>>> Extend existing filter with a regular expression.
>>>>> string_ends_with() helper is not used anymore, and removed as a
>>>>> consequence.
>>>>>
>>>>> Querying statistics with
>>>>> $ ovs-vsctl get interface dpdk0 statistics | \
>>>>>     sed -e 's#[{}]##g' -e 's#, #\n#g'
>>>>>
>>>>> and comparing gives:
>>>>> @@ -13,7 +13,12 @@
>>>>>    rx_phy_crc_errors=0
>>>>>    rx_phy_in_range_len_errors=0
>>>>>    rx_phy_symbol_errors=0
>>>>> +rx_q0_bytes=0
>>>>>    rx_q0_errors=0
>>>>> +rx_q0_packets=0
>>>>> +rx_q1_bytes=0
>>>>> +rx_q1_errors=0
>>>>> +rx_q1_packets=0
>>>>>    rx_wqe_errors=0
>>>>>    tx_broadcast_packets=0
>>>>>    tx_bytes=0
>>>>> @@ -27,3 +32,13 @@
>>>>>    tx_pp_rearm_queue_errors=0
>>>>>    tx_pp_timestamp_future_errors=0
>>>>>    tx_pp_timestamp_past_errors=0
>>>>> +tx_q0_bytes=0
>>>>> +tx_q0_packets=0
>>>>> +tx_q1_bytes=0
>>>>> +tx_q1_packets=0
>>>>> +tx_q2_bytes=0
>>>>> +tx_q2_packets=0
>>>>> +tx_q3_bytes=0
>>>>> +tx_q3_packets=0
>>>>> +tx_q4_bytes=0
>>>>> +tx_q4_packets=0
>>>>>
>>>>> Signed-off-by: David Marchand <david.marchand@redhat.com>
>>>>
>>>> The patch looks good but I tested with with ixgbe, and seeing something
>>>> I am unsure of. I changed the number of rxqs to 11 but I am only seeing
>>>> stats for q0. i'm not sure if this because there was no traffic yet
>>>> (even though there is also no traffic on q0). i will set up a traffic
>>>> gen and see what happens when the q1-q10 receives traffic.
>>>
>>> I think the problem is that when port reconfiguration is called because
>>> the number of queues changed, netdev_dpdk_clear_xstats() is not called.
>>>
>>> So the next time netdev_dpdk_configure_xstats() is called, it returns
>>> true early as it is already configured. So old configuration is keeped.
>>>
>>> When increasing the number of queues, I think we get your behavior, i.e.
>>> only previous number of queues counters are disaplayed. But when
>>> decreasing the number of queues, I think rte_eth_xstats_get_by_id() will
>>> fail.
>>>
>>> I think calling netdev_dpdk_clear_xstats() in netdev_dpdk_reconfigure()
>>> should fix your issue.
>>
>> Thanks for the analysis.
>>
>> I'll need a bit more time to dig in (I don't understand the logic
>> behind the configure_xstats/clear_xstats stuff).
>> In any case, it seems this is a bug predating my patch.
>>
>>
> 
> Thanks Maxime. Yes, that seems to be explanation for it.
> 
> The issue was also present with the txqs when the number of PMDs was 
> increased/decreased, hence changing requested txqs. I added below and it 
> is showing the right amount of rx/tx queues now.
> 
> I'm not sure it's an existing bug that we'd backport a fix for as the 
> num queues changing wouldn't have previously impacted because of the 
> filter. It seems more like a short cut that we now can't keep :-)

As per David's commit log above, don't we need to backport it because we 
already dumped rxqN_error counter before?

> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index ca92c947a..720041c34 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -1876,4 +1876,5 @@ dpdk_set_rxq_config(struct netdev_dpdk *dev, const 
> struct smap *args)
>           dev->requested_n_rxq = new_n_rxq;
>           netdev_request_reconfigure(&dev->up);
> +        netdev_dpdk_clear_xstats(dev);
>       }
>   }
> @@ -2109,4 +2110,5 @@ netdev_dpdk_set_tx_multiq(struct netdev *netdev, 
> unsigned int n_txq)
>       dev->requested_n_txq = n_txq;
>       netdev_request_reconfigure(netdev);
> +    netdev_dpdk_clear_xstats(dev);
>
Kevin Traynor Nov. 19, 2021, 12:52 p.m. UTC | #7
On 19/11/2021 12:45, Maxime Coquelin wrote:
> 
> 
> On 11/19/21 13:08, Kevin Traynor wrote:
>> On 19/11/2021 10:31, David Marchand wrote:
>>> On Fri, Nov 19, 2021 at 10:40 AM Maxime Coquelin
>>> <maxime.coquelin@redhat.com> wrote:
>>>>
>>>> Hi Kevin, David,
>>>>
>>>> On 11/18/21 16:31, Kevin Traynor wrote:
>>>>> On 15/10/2021 16:04, David Marchand wrote:
>>>>>> When troubleshooting multiqueue setups, having per queue statistics
>>>>>> helps
>>>>>> checking packets repartition in rx and tx queues.
>>>>>>
>>>>>> Per queue statistics are exported by most DPDK drivers (with
>>>>>> capability
>>>>>> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters
>>>>>> statistics
>>>>>> it exposes, there is nothing to request in DPDK API.
>>>>>>
>>>>>> Extend existing filter with a regular expression.
>>>>>> string_ends_with() helper is not used anymore, and removed as a
>>>>>> consequence.
>>>>>>
>>>>>> Querying statistics with
>>>>>> $ ovs-vsctl get interface dpdk0 statistics | \
>>>>>>      sed -e 's#[{}]##g' -e 's#, #\n#g'
>>>>>>
>>>>>> and comparing gives:
>>>>>> @@ -13,7 +13,12 @@
>>>>>>     rx_phy_crc_errors=0
>>>>>>     rx_phy_in_range_len_errors=0
>>>>>>     rx_phy_symbol_errors=0
>>>>>> +rx_q0_bytes=0
>>>>>>     rx_q0_errors=0
>>>>>> +rx_q0_packets=0
>>>>>> +rx_q1_bytes=0
>>>>>> +rx_q1_errors=0
>>>>>> +rx_q1_packets=0
>>>>>>     rx_wqe_errors=0
>>>>>>     tx_broadcast_packets=0
>>>>>>     tx_bytes=0
>>>>>> @@ -27,3 +32,13 @@
>>>>>>     tx_pp_rearm_queue_errors=0
>>>>>>     tx_pp_timestamp_future_errors=0
>>>>>>     tx_pp_timestamp_past_errors=0
>>>>>> +tx_q0_bytes=0
>>>>>> +tx_q0_packets=0
>>>>>> +tx_q1_bytes=0
>>>>>> +tx_q1_packets=0
>>>>>> +tx_q2_bytes=0
>>>>>> +tx_q2_packets=0
>>>>>> +tx_q3_bytes=0
>>>>>> +tx_q3_packets=0
>>>>>> +tx_q4_bytes=0
>>>>>> +tx_q4_packets=0
>>>>>>
>>>>>> Signed-off-by: David Marchand <david.marchand@redhat.com>
>>>>>
>>>>> The patch looks good but I tested with with ixgbe, and seeing something
>>>>> I am unsure of. I changed the number of rxqs to 11 but I am only seeing
>>>>> stats for q0. i'm not sure if this because there was no traffic yet
>>>>> (even though there is also no traffic on q0). i will set up a traffic
>>>>> gen and see what happens when the q1-q10 receives traffic.
>>>>
>>>> I think the problem is that when port reconfiguration is called because
>>>> the number of queues changed, netdev_dpdk_clear_xstats() is not called.
>>>>
>>>> So the next time netdev_dpdk_configure_xstats() is called, it returns
>>>> true early as it is already configured. So old configuration is keeped.
>>>>
>>>> When increasing the number of queues, I think we get your behavior, i.e.
>>>> only previous number of queues counters are disaplayed. But when
>>>> decreasing the number of queues, I think rte_eth_xstats_get_by_id() will
>>>> fail.
>>>>
>>>> I think calling netdev_dpdk_clear_xstats() in netdev_dpdk_reconfigure()
>>>> should fix your issue.
>>>
>>> Thanks for the analysis.
>>>
>>> I'll need a bit more time to dig in (I don't understand the logic
>>> behind the configure_xstats/clear_xstats stuff).
>>> In any case, it seems this is a bug predating my patch.
>>>
>>>
>>
>> Thanks Maxime. Yes, that seems to be explanation for it.
>>
>> The issue was also present with the txqs when the number of PMDs was
>> increased/decreased, hence changing requested txqs. I added below and it
>> is showing the right amount of rx/tx queues now.
>>
>> I'm not sure it's an existing bug that we'd backport a fix for as the
>> num queues changing wouldn't have previously impacted because of the
>> filter. It seems more like a short cut that we now can't keep :-)
> 
> As per David's commit log above, don't we need to backport it because we
> already dumped rxqN_error counter before?
> 

Yes, I forgot that the filter would catch the rx_qN_error already, so 
you're both right, it's an existing bug.

>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>> index ca92c947a..720041c34 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -1876,4 +1876,5 @@ dpdk_set_rxq_config(struct netdev_dpdk *dev, const
>> struct smap *args)
>>            dev->requested_n_rxq = new_n_rxq;
>>            netdev_request_reconfigure(&dev->up);
>> +        netdev_dpdk_clear_xstats(dev);
>>        }
>>    }
>> @@ -2109,4 +2110,5 @@ netdev_dpdk_set_tx_multiq(struct netdev *netdev,
>> unsigned int n_txq)
>>        dev->requested_n_txq = n_txq;
>>        netdev_request_reconfigure(netdev);
>> +    netdev_dpdk_clear_xstats(dev);
>>
>
David Marchand Nov. 23, 2021, 3:13 p.m. UTC | #8
On Thu, Nov 18, 2021 at 4:31 PM Kevin Traynor <ktraynor@redhat.com> wrote:
> > @@ -1583,6 +1584,13 @@ netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
> >       return dev->rte_xstats_names[id].name;
> >   }
> >
> > +/* We filter out everything except per rxq/txq basic stats, and dropped,
> > + * error and management counters.
> > + * Note: rx_qX_errors is handled by the _errors$ pattern.
> > + */
> > +#define DPDK_STATS_REGEX_FILTER \
> > +    "(^(rx_q|tx_q)[0-9]*_(packets|bytes)$|_errors$|_dropped$|_management_)"
> > +
>
> iirc, there was some pmds that were not conforming correctly with this
> rx_q syntax, but I *think* they were fixed a few releases ago in DPDK.
> Just mentioning because if there was still some, they could be fixed or
> the regex could be expanded to capture them.

In theory, those per q stats are part of the "basic" stats handled at
the ethdev level.
There should be no issue for them.


I remember an issue with incorrect stats, but it was a conflict
between OVS and i40e stats.
Is this what you had in mind?
Kevin Traynor Nov. 23, 2021, 3:40 p.m. UTC | #9
On 23/11/2021 15:13, David Marchand wrote:
> On Thu, Nov 18, 2021 at 4:31 PM Kevin Traynor <ktraynor@redhat.com> wrote:
>>> @@ -1583,6 +1584,13 @@ netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
>>>        return dev->rte_xstats_names[id].name;
>>>    }
>>>
>>> +/* We filter out everything except per rxq/txq basic stats, and dropped,
>>> + * error and management counters.
>>> + * Note: rx_qX_errors is handled by the _errors$ pattern.
>>> + */
>>> +#define DPDK_STATS_REGEX_FILTER \
>>> +    "(^(rx_q|tx_q)[0-9]*_(packets|bytes)$|_errors$|_dropped$|_management_)"
>>> +
>>
>> iirc, there was some pmds that were not conforming correctly with this
>> rx_q syntax, but I *think* they were fixed a few releases ago in DPDK.
>> Just mentioning because if there was still some, they could be fixed or
>> the regex could be expanded to capture them.
> 
> In theory, those per q stats are part of the "basic" stats handled at
> the ethdev level.
> There should be no issue for them.
> 
> 
> I remember an issue with incorrect stats, but it was a conflict
> between OVS and i40e stats.
> Is this what you had in mind?
> 
> 

No, I had a thought that some driver for maybe Broadcom or Mellanox was 
reporting say 'rxq' instead of 'rx_q' and it was noticed and fixed while 
some other work on xstats. But it's hazy and I can't find it now, so 
best just to ignore unless someone hits it.
Ilya Maximets Nov. 29, 2021, 5:48 p.m. UTC | #10
On 10/15/21 17:04, David Marchand wrote:
> When troubleshooting multiqueue setups, having per queue statistics helps
> checking packets repartition in rx and tx queues.
> 
> Per queue statistics are exported by most DPDK drivers (with capability
> RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS). But since OVS only filters statistics
> it exposes, there is nothing to request in DPDK API.
> 
> Extend existing filter with a regular expression.
> string_ends_with() helper is not used anymore, and removed as a
> consequence.
> 
> Querying statistics with
> $ ovs-vsctl get interface dpdk0 statistics | \
>   sed -e 's#[{}]##g' -e 's#, #\n#g'
> 
> and comparing gives:
> @@ -13,7 +13,12 @@
>  rx_phy_crc_errors=0
>  rx_phy_in_range_len_errors=0
>  rx_phy_symbol_errors=0
> +rx_q0_bytes=0
>  rx_q0_errors=0
> +rx_q0_packets=0
> +rx_q1_bytes=0
> +rx_q1_errors=0
> +rx_q1_packets=0
>  rx_wqe_errors=0
>  tx_broadcast_packets=0
>  tx_bytes=0
> @@ -27,3 +32,13 @@
>  tx_pp_rearm_queue_errors=0
>  tx_pp_timestamp_future_errors=0
>  tx_pp_timestamp_past_errors=0
> +tx_q0_bytes=0
> +tx_q0_packets=0
> +tx_q1_bytes=0
> +tx_q1_packets=0
> +tx_q2_bytes=0
> +tx_q2_packets=0
> +tx_q3_bytes=0
> +tx_q3_packets=0
> +tx_q4_bytes=0
> +tx_q4_packets=0
> 
> Signed-off-by: David Marchand <david.marchand@redhat.com>
> ---
> Sending this as a RFC for feedback.
> vhost user ports are left untouched for now, but could be added in the
> future.
> 
> ---
>  lib/netdev-dpdk.c | 32 ++++++++++++++++++++++++++------
>  lib/util.c        | 13 -------------
>  lib/util.h        |  2 --
>  3 files changed, 26 insertions(+), 21 deletions(-)
> 
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index ca92c947a2..12447cc4b2 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -18,6 +18,7 @@
>  #include "netdev-dpdk.h"
>  
>  #include <errno.h>
> +#include <regex.h>
>  #include <signal.h>
>  #include <stdlib.h>
>  #include <string.h>
> @@ -1583,6 +1584,13 @@ netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
>      return dev->rte_xstats_names[id].name;
>  }
>  
> +/* We filter out everything except per rxq/txq basic stats, and dropped,
> + * error and management counters.
> + * Note: rx_qX_errors is handled by the _errors$ pattern.
> + */
> +#define DPDK_STATS_REGEX_FILTER \
> +    "(^(rx_q|tx_q)[0-9]*_(packets|bytes)$|_errors$|_dropped$|_management_)"
> +

Hi, David.  The idea seems OK to me, but I'm a bit concerned about
using the regex.  It seems that not every libc implements this API
and it might become a problem with attempts to enable OVS with DPDK
on Windows, AFAICT.  Can we replace this regex with a usual string
checking function?  For example, we could keep the current checks
and add something like this:

static bool
is_queue_stat(const char *s)
{
  return (s[0] == 'r' || s[1] == 't')
         && (ovs_scan(s + 1, "x_q%"SCNu16"_packets", &tmp)
             || ovs_scan(s + 1, "x_q%"SCNu16"_bytes", &tmp));
}

Should be equal to your regex, if I didn't mess up something.
What do you think?

Best regards, Ilya Maximets.
diff mbox series

Patch

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index ca92c947a2..12447cc4b2 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -18,6 +18,7 @@ 
 #include "netdev-dpdk.h"
 
 #include <errno.h>
+#include <regex.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -1583,6 +1584,13 @@  netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
     return dev->rte_xstats_names[id].name;
 }
 
+/* We filter out everything except per rxq/txq basic stats, and dropped,
+ * error and management counters.
+ * Note: rx_qX_errors is handled by the _errors$ pattern.
+ */
+#define DPDK_STATS_REGEX_FILTER \
+    "(^(rx_q|tx_q)[0-9]*_(packets|bytes)$|_errors$|_dropped$|_management_)"
+
 static bool
 netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
     OVS_REQUIRES(dev->mutex)
@@ -1617,6 +1625,20 @@  netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
                                             sizeof *dev->rte_xstats_names);
 
             if (dev->rte_xstats_names) {
+                int err;
+                regex_t r;
+
+                err = regcomp(&r, DPDK_STATS_REGEX_FILTER, REG_EXTENDED);
+                if (err != 0) {
+                    size_t errbuf_size = regerror(err, &r, NULL, 0);
+                    char *errbuf = xcalloc(1, errbuf_size);
+
+                    regerror(err, &r, errbuf, errbuf_size);
+                    VLOG_DBG("Could not setup stats regexp: %s", errbuf);
+                    free(errbuf);
+                    goto out;
+                }
+
                 /* Retreive xstats names */
                 rte_xstats_len =
                         rte_eth_xstats_get_names(dev->port_id,
@@ -1626,10 +1648,12 @@  netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
                 if (rte_xstats_len < 0) {
                     VLOG_WARN("Cannot get XSTATS names for port: "
                               DPDK_PORT_ID_FMT, dev->port_id);
+                    regfree(&r);
                     goto out;
                 } else if (rte_xstats_len != dev->rte_xstats_names_size) {
                     VLOG_WARN("XSTATS size doesn't match for port: "
                               DPDK_PORT_ID_FMT, dev->port_id);
+                    regfree(&r);
                     goto out;
                 }
 
@@ -1648,12 +1672,7 @@  netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
                     for (uint32_t i = 0; i < rte_xstats_len; i++) {
                         id = rte_xstats[i].id;
                         name = netdev_dpdk_get_xstat_name(dev, id);
-                        /* We need to filter out everything except
-                         * dropped, error and management counters */
-                        if (string_ends_with(name, "_errors") ||
-                            strstr(name, "_management_") ||
-                            string_ends_with(name, "_dropped")) {
-
+                        if (regexec(&r, name, 0, NULL, 0) == 0) {
                             dev->rte_xstats_ids[xstats_no] = id;
                             xstats_no++;
                         }
@@ -1666,6 +1685,7 @@  netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
                 }
 
                 free(rte_xstats);
+                regfree(&r);
             }
         }
     } else {
diff --git a/lib/util.c b/lib/util.c
index 1195c79821..34755a4dad 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -405,19 +405,6 @@  ovs_strzcpy(char *dst, const char *src, size_t size)
     }
 }
 
-/*
- * Returns true if 'str' ends with given 'suffix'.
- */
-int
-string_ends_with(const char *str, const char *suffix)
-{
-    int str_len = strlen(str);
-    int suffix_len = strlen(suffix);
-
-    return (str_len >= suffix_len) &&
-           (0 == strcmp(str + (str_len - suffix_len), suffix));
-}
-
 /* Prints 'format' on stderr, formatting it like printf() does.  If 'err_no' is
  * nonzero, then it is formatted with ovs_retval_to_string() and appended to
  * the message inside parentheses.  Then, terminates with abort().
diff --git a/lib/util.h b/lib/util.h
index aea19d45fa..4ad6e28274 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -183,8 +183,6 @@  void free_cacheline(void *);
 void ovs_strlcpy(char *dst, const char *src, size_t size);
 void ovs_strzcpy(char *dst, const char *src, size_t size);
 
-int string_ends_with(const char *str, const char *suffix);
-
 void *xmalloc_pagealign(size_t) MALLOC_LIKE;
 void free_pagealign(void *);
 void *xmalloc_size_align(size_t, size_t) MALLOC_LIKE;