diff mbox series

[ovs-dev,1/4] netdev-dpdk: Introduce per rxq/txq Vhost-user statistics

Message ID 20211124212400.70613-2-maxime.coquelin@redhat.com
State Changes Requested
Headers show
Series dpif-netdev: Hash-based Tx packet steering | expand

Checks

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

Commit Message

Maxime Coquelin Nov. 24, 2021, 9:23 p.m. UTC
HXPS feature will enable steering Tx packets on transmist
queues based on their hashes. In order to test the feature,
it is needed to be able to get the per-queue statistics for
Vhost-user ports.

This patch introduces "bytes", "packets" and "error"
per-queue custom statistics for Vhost-user ports.

Suggested-by David Marchand <david.marchand@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/netdev-dpdk.c | 143 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 135 insertions(+), 8 deletions(-)

Comments

David Marchand Dec. 7, 2021, 8:37 p.m. UTC | #1
Hey Maxime,

On Wed, Nov 24, 2021 at 10:24 PM Maxime Coquelin
<maxime.coquelin@redhat.com> wrote:
>
> HXPS feature will enable steering Tx packets on transmist

transmit*

> queues based on their hashes. In order to test the feature,

"their hashes" is ambiguous.

> it is needed to be able to get the per-queue statistics for
> Vhost-user ports.

This is a general comment, for consistency, I'd write vhost, all lowercase.


>
> This patch introduces "bytes", "packets" and "error"
> per-queue custom statistics for Vhost-user ports.
>
> Suggested-by David Marchand <david.marchand@redhat.com>
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
> ---
>  lib/netdev-dpdk.c | 143 +++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 135 insertions(+), 8 deletions(-)
>
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index ca92c947a..e80d5b4ab 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -192,6 +192,13 @@ static const struct vhost_device_ops virtio_net_device_ops =
>      .guest_notified = vhost_guest_notified,
>  };
>
> +/* Custome software per-queue stats for Vhost ports */

Custom*

> +struct netdev_dpdk_vhost_q_stats {
> +    uint64_t bytes;
> +    uint64_t packets;
> +    uint64_t errors;
> +};
> +
>  /* Custom software stats for dpdk ports */
>  struct netdev_dpdk_sw_stats {
>      /* No. of retries when unable to transmit. */
> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
>      uint64_t rx_qos_drops;
>      /* Packet drops in HWOL processing. */
>      uint64_t tx_invalid_hwol_drops;
> +    /* Per-queue Vhost Tx stats */
> +    struct netdev_dpdk_vhost_q_stats *txq;
> +    /* Per-queue Vhost Rx stats */
> +    struct netdev_dpdk_vhost_q_stats *rxq;

Here, we add "driver" specific stats, while netdev_dpdk_sw_stats
struct carries OVS "own" stats.
This netdev_dpdk_sw_stats struct is converted by
netdev_dpdk_get_sw_custom_stats and there is a small framework on
adding custom OVS stats (using some macros "trick").

I'd rather leave netdev_dpdk_sw_stats struct untouched for consistency.
Pointers to vhost specific stats can be added to the netdev_dpdk
struct (we have some spare space after the pointer to
netdev_dpdk_sw_stats).


>  };
>
>  enum dpdk_dev_type {
> @@ -1276,6 +1287,13 @@ common_construct(struct netdev *netdev, dpdk_port_t port_no,
>      dev->sw_stats = xzalloc(sizeof *dev->sw_stats);
>      dev->sw_stats->tx_retries = (dev->type == DPDK_DEV_VHOST) ? 0 : UINT64_MAX;
>
> +    if (dev->type == DPDK_DEV_VHOST) {
> +        dev->sw_stats->txq = xcalloc(netdev->n_txq,
> +                                     sizeof *dev->sw_stats->txq);
> +        dev->sw_stats->rxq = xcalloc(netdev->n_rxq,
> +                                     sizeof *dev->sw_stats->rxq);
> +    }
> +
>      return 0;
>  }
>
> @@ -2353,17 +2371,21 @@ netdev_dpdk_vhost_update_rx_size_counters(struct netdev_stats *stats,
>  }
>
>  static inline void
> -netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
> +netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev, int qid,
>                                       struct dp_packet **packets, int count,
>                                       int qos_drops)
>  {
>      struct netdev_stats *stats = &dev->stats;
> +    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->rxq[qid];

reverse xmas tree?


>      struct dp_packet *packet;
>      unsigned int packet_size;
>      int i;
>
>      stats->rx_packets += count;
> +    q_stats->packets += count;
>      stats->rx_dropped += qos_drops;
> +    q_stats->errors += qos_drops;
> +
>      for (i = 0; i < count; i++) {
>          packet = packets[i];
>          packet_size = dp_packet_size(packet);
> @@ -2374,6 +2396,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>               * further processing. */
>              stats->rx_errors++;
>              stats->rx_length_errors++;
> +            q_stats->errors++;
>              continue;
>          }
>
> @@ -2385,6 +2408,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>          }
>
>          stats->rx_bytes += packet_size;
> +        q_stats->bytes += packet_size;
>      }
>
>      if (OVS_UNLIKELY(qos_drops)) {
> @@ -2437,7 +2461,7 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
>      }
>
>      rte_spinlock_lock(&dev->stats_lock);
> -    netdev_dpdk_vhost_update_rx_counters(dev, batch->packets,
> +    netdev_dpdk_vhost_update_rx_counters(dev, qid, batch->packets,
>                                           nb_rx, qos_drops);
>      rte_spinlock_unlock(&dev->stats_lock);
>
> @@ -2551,7 +2575,7 @@ netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts,
>  }
>
>  static inline void
> -netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
> +netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev, int qid,
>                                       struct dp_packet **packets,
>                                       int attempted,
>                                       struct netdev_dpdk_sw_stats *sw_stats_add)
> @@ -2561,14 +2585,20 @@ netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
>                    sw_stats_add->tx_failure_drops +
>                    sw_stats_add->tx_invalid_hwol_drops;
>      struct netdev_stats *stats = &dev->stats;
> +    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->txq[qid];

reverse xmas tree?


>      int sent = attempted - dropped;
>      int i;
>
>      stats->tx_packets += sent;
> +    q_stats->packets += sent;
>      stats->tx_dropped += dropped;
> +    q_stats->errors += dropped;
>
>      for (i = 0; i < sent; i++) {
> -        stats->tx_bytes += dp_packet_size(packets[i]);
> +        uint64_t bytes = dp_packet_size(packets[i]);
> +
> +        stats->tx_bytes += bytes;
> +        q_stats->bytes += bytes;
>      }
>
>      if (OVS_UNLIKELY(dropped || sw_stats_add->tx_retries)) {
> @@ -2656,7 +2686,7 @@ __netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
>      sw_stats_add.tx_retries = MIN(retries, max_retries);
>
>      rte_spinlock_lock(&dev->stats_lock);
> -    netdev_dpdk_vhost_update_tx_counters(dev, pkts, total_packets,
> +    netdev_dpdk_vhost_update_tx_counters(dev, qid, pkts, total_packets,
>                                           &sw_stats_add);
>      rte_spinlock_unlock(&dev->stats_lock);
>
> @@ -3286,6 +3316,72 @@ netdev_dpdk_get_sw_custom_stats(const struct netdev *netdev,
>      return 0;
>  }
>
> +static int
> +netdev_dpdk_vhost_get_custom_stats(const struct netdev *netdev,
> +                                struct netdev_custom_stats *custom_stats)
> +{
> +    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> +    int sw_stats_size, i, j;
> +
> +    netdev_dpdk_get_sw_custom_stats(netdev, custom_stats);
> +
> +    ovs_mutex_lock(&dev->mutex);
> +
> +    sw_stats_size = custom_stats->size;
> +    custom_stats->size += netdev->n_rxq * sizeof(*dev->sw_stats->rxq) /
> +                          sizeof(uint64_t);
> +    custom_stats->size += netdev->n_txq * sizeof(*dev->sw_stats->txq) /
> +                          sizeof(uint64_t);
> +
> +    custom_stats->counters = xrealloc(custom_stats->counters,
> +                                      custom_stats->size *
> +                                      sizeof *custom_stats->counters);

Names and values must be synced in the code below and this might get
broken later.

We could reuse the same kind of "trick" than for sw_stats.
This should make adding stats less error prone.

WDYT of (just build tested) diff:


diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index e80d5b4ab5..8ad9b785f7 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -3327,52 +3327,53 @@ netdev_dpdk_vhost_get_custom_stats(const
struct netdev *netdev,

     ovs_mutex_lock(&dev->mutex);

-    sw_stats_size = custom_stats->size;
-    custom_stats->size += netdev->n_rxq * sizeof(*dev->sw_stats->rxq) /
-                          sizeof(uint64_t);
-    custom_stats->size += netdev->n_txq * sizeof(*dev->sw_stats->txq) /
-                          sizeof(uint64_t);
+#define VHOST_Q_STATS \
+    VHOST_Q_STAT(bytes) \
+    VHOST_Q_STAT(packets) \
+    VHOST_Q_STAT(errors)

+    sw_stats_size = custom_stats->size;
+#define VHOST_Q_STAT(NAME) + netdev->n_rxq
+    custom_stats->size += VHOST_Q_STATS;
+#undef VHOST_Q_STAT
+#define VHOST_Q_STAT(NAME) + netdev->n_txq
+    custom_stats->size += VHOST_Q_STATS;
+#undef VHOST_Q_STAT
     custom_stats->counters = xrealloc(custom_stats->counters,
                                       custom_stats->size *
                                       sizeof *custom_stats->counters);

     j = 0;
     for (i = 0; i < netdev->n_rxq; i++) {
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_bytes", i);
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_packets", i);
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_errors", i);
+#define VHOST_Q_STAT(NAME) \
+        snprintf(custom_stats->counters[sw_stats_size + j++].name, \
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_"#NAME, i);
+        VHOST_Q_STATS
+#undef VHOST_Q_STAT
     }
-
     for (i = 0; i < netdev->n_txq; i++) {
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_bytes", i);
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_packets", i);
-        snprintf(custom_stats->counters[sw_stats_size + j++].name,
-                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_errors", i);
+#define VHOST_Q_STAT(NAME) \
+        snprintf(custom_stats->counters[sw_stats_size + j++].name, \
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_"#NAME, i);
+        VHOST_Q_STATS
+#undef VHOST_Q_STAT
     }

     rte_spinlock_lock(&dev->stats_lock);

     j = 0;
     for (i = 0; i < netdev->n_rxq; i++) {
-        struct netdev_dpdk_vhost_q_stats *rxq_stats = &dev->sw_stats->rxq[i];
-
-        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->bytes;
-        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->packets;
-        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->errors;
+#define VHOST_Q_STAT(NAME) \
+        custom_stats->counters[sw_stats_size + j++].value =
dev->sw_stats->rxq[i].NAME;
+        VHOST_Q_STATS
+#undef VHOST_Q_STAT
     }

     for (i = 0; i < netdev->n_txq; i++) {
-        struct netdev_dpdk_vhost_q_stats *txq_stats = &dev->sw_stats->txq[i];
-
-        custom_stats->counters[sw_stats_size + j++].value = txq_stats->bytes;
-        custom_stats->counters[sw_stats_size + j++].value = txq_stats->packets;
-        custom_stats->counters[sw_stats_size + j++].value = txq_stats->errors;
+#define VHOST_Q_STAT(NAME) \
+        custom_stats->counters[sw_stats_size + j++].value =
dev->sw_stats->txq[i].NAME;
+        VHOST_Q_STATS
+#undef VHOST_Q_STAT
     }

     rte_spinlock_unlock(&dev->stats_lock);



> +
> +    j = 0;
> +    for (i = 0; i < netdev->n_rxq; i++) {
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_bytes", i);
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_packets", i);
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_errors", i);
> +    }
> +
> +    for (i = 0; i < netdev->n_txq; i++) {
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_bytes", i);
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_packets", i);
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_errors", i);
> +    }
> +
> +    rte_spinlock_lock(&dev->stats_lock);
> +
> +    j = 0;
> +    for (i = 0; i < netdev->n_rxq; i++) {
> +        struct netdev_dpdk_vhost_q_stats *rxq_stats = &dev->sw_stats->rxq[i];
> +
> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->bytes;
> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->packets;
> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->errors;
> +    }
> +
> +    for (i = 0; i < netdev->n_txq; i++) {
> +        struct netdev_dpdk_vhost_q_stats *txq_stats = &dev->sw_stats->txq[i];
> +
> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->bytes;
> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->packets;
> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->errors;
> +    }
> +
> +    rte_spinlock_unlock(&dev->stats_lock);
> +
> +    ovs_mutex_unlock(&dev->mutex);
> +
> +    return 0;
> +}
> +
>  static int
>  netdev_dpdk_get_features(const struct netdev *netdev,
>                           enum netdev_features *current,
> @@ -5043,9 +5139,12 @@ static int
>  dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
>      OVS_REQUIRES(dev->mutex)
>  {
> +    int old_n_txq = dev->up.n_txq;
> +    int old_n_rxq = dev->up.n_rxq;
> +    int err;
> +
>      dev->up.n_txq = dev->requested_n_txq;
>      dev->up.n_rxq = dev->requested_n_rxq;
> -    int err;
>
>      /* Always keep RX queue 0 enabled for implementations that won't
>       * report vring states. */
> @@ -5063,6 +5162,34 @@ dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
>
>      netdev_dpdk_remap_txqs(dev);
>
> +    if (dev->up.n_txq != old_n_txq) {
> +        struct netdev_dpdk_vhost_q_stats *old_txq_stats, *new_txq_stats;
> +
> +        new_txq_stats = xcalloc(dev->up.n_txq, sizeof *dev->sw_stats->txq);
> +        rte_spinlock_lock(&dev->stats_lock);
> +        old_txq_stats = dev->sw_stats->txq;
> +        memcpy(new_txq_stats, old_txq_stats,
> +                MIN(dev->up.n_txq, old_n_txq) * sizeof *dev->sw_stats->txq);
> +        dev->sw_stats->txq = new_txq_stats;

I prefer realloc and setting extra space to 0, but it is equivalent,
no strong opinion on the implementation.


> +        rte_spinlock_unlock(&dev->stats_lock);
> +        free(old_txq_stats);
> +
> +    }
> +
> +    if (dev->up.n_rxq != old_n_rxq) {
> +        struct netdev_dpdk_vhost_q_stats *old_rxq_stats, *new_rxq_stats;
> +
> +        new_rxq_stats = xcalloc(dev->up.n_rxq, sizeof *dev->sw_stats->rxq);
> +        rte_spinlock_lock(&dev->stats_lock);
> +        old_rxq_stats = dev->sw_stats->rxq;
> +        memcpy(new_rxq_stats, old_rxq_stats,
> +                MIN(dev->up.n_rxq, old_n_rxq) * sizeof *dev->sw_stats->rxq);
> +        dev->sw_stats->rxq = new_rxq_stats;
> +        rte_spinlock_unlock(&dev->stats_lock);
> +        free(old_rxq_stats);
> +
> +    }
> +

I did not test your patch yet (will do later this week once I get to
the end of this series).
However I think we are missing some reset to 0 for per q stats in
netdev_dpdk_update_flags__.



>      err = netdev_dpdk_mempool_configure(dev);
>      if (!err) {
>          /* A new mempool was created or re-used. */
> @@ -5467,7 +5594,7 @@ static const struct netdev_class dpdk_vhost_class = {
>      .send = netdev_dpdk_vhost_send,
>      .get_carrier = netdev_dpdk_vhost_get_carrier,
>      .get_stats = netdev_dpdk_vhost_get_stats,
> -    .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
> +    .get_custom_stats = netdev_dpdk_vhost_get_custom_stats,
>      .get_status = netdev_dpdk_vhost_user_get_status,
>      .reconfigure = netdev_dpdk_vhost_reconfigure,
>      .rxq_recv = netdev_dpdk_vhost_rxq_recv,
> @@ -5483,7 +5610,7 @@ static const struct netdev_class dpdk_vhost_client_class = {
>      .send = netdev_dpdk_vhost_send,
>      .get_carrier = netdev_dpdk_vhost_get_carrier,
>      .get_stats = netdev_dpdk_vhost_get_stats,
> -    .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
> +    .get_custom_stats = netdev_dpdk_vhost_get_custom_stats,
>      .get_status = netdev_dpdk_vhost_user_get_status,
>      .reconfigure = netdev_dpdk_vhost_client_reconfigure,
>      .rxq_recv = netdev_dpdk_vhost_rxq_recv,
> --
> 2.31.1
>
Maxime Coquelin Dec. 7, 2021, 10:11 p.m. UTC | #2
Hi David,

On 12/7/21 21:37, David Marchand wrote:
> Hey Maxime,
> 
> On Wed, Nov 24, 2021 at 10:24 PM Maxime Coquelin
> <maxime.coquelin@redhat.com> wrote:
>>
>> HXPS feature will enable steering Tx packets on transmist
> 
> transmit*
> 
>> queues based on their hashes. In order to test the feature,
> 
> "their hashes" is ambiguous.

s/their hashes/the packets hashes/ ?

>> it is needed to be able to get the per-queue statistics for
>> Vhost-user ports.
> 
> This is a general comment, for consistency, I'd write vhost, all lowercase.

Ok, I will make it consistent in next revision.

> 
>>
>> This patch introduces "bytes", "packets" and "error"
>> per-queue custom statistics for Vhost-user ports.
>>
>> Suggested-by David Marchand <david.marchand@redhat.com>
>> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
>> ---
>>   lib/netdev-dpdk.c | 143 +++++++++++++++++++++++++++++++++++++++++++---
>>   1 file changed, 135 insertions(+), 8 deletions(-)
>>
>> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
>> index ca92c947a..e80d5b4ab 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -192,6 +192,13 @@ static const struct vhost_device_ops virtio_net_device_ops =
>>       .guest_notified = vhost_guest_notified,
>>   };
>>
>> +/* Custome software per-queue stats for Vhost ports */
> 
> Custom*
> 
>> +struct netdev_dpdk_vhost_q_stats {
>> +    uint64_t bytes;
>> +    uint64_t packets;
>> +    uint64_t errors;
>> +};
>> +
>>   /* Custom software stats for dpdk ports */
>>   struct netdev_dpdk_sw_stats {
>>       /* No. of retries when unable to transmit. */
>> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
>>       uint64_t rx_qos_drops;
>>       /* Packet drops in HWOL processing. */
>>       uint64_t tx_invalid_hwol_drops;
>> +    /* Per-queue Vhost Tx stats */
>> +    struct netdev_dpdk_vhost_q_stats *txq;
>> +    /* Per-queue Vhost Rx stats */
>> +    struct netdev_dpdk_vhost_q_stats *rxq;
> 
> Here, we add "driver" specific stats, while netdev_dpdk_sw_stats
> struct carries OVS "own" stats.
> This netdev_dpdk_sw_stats struct is converted by
> netdev_dpdk_get_sw_custom_stats and there is a small framework on
> adding custom OVS stats (using some macros "trick").
> 
> I'd rather leave netdev_dpdk_sw_stats struct untouched for consistency.
> Pointers to vhost specific stats can be added to the netdev_dpdk
> struct (we have some spare space after the pointer to
> netdev_dpdk_sw_stats).

That makes sense, it is indeed better to have it out of
netdev_dpdk_sw_stats struct.

> 
>>   };
>>
>>   enum dpdk_dev_type {
>> @@ -1276,6 +1287,13 @@ common_construct(struct netdev *netdev, dpdk_port_t port_no,
>>       dev->sw_stats = xzalloc(sizeof *dev->sw_stats);
>>       dev->sw_stats->tx_retries = (dev->type == DPDK_DEV_VHOST) ? 0 : UINT64_MAX;
>>
>> +    if (dev->type == DPDK_DEV_VHOST) {
>> +        dev->sw_stats->txq = xcalloc(netdev->n_txq,
>> +                                     sizeof *dev->sw_stats->txq);
>> +        dev->sw_stats->rxq = xcalloc(netdev->n_rxq,
>> +                                     sizeof *dev->sw_stats->rxq);
>> +    }
>> +
>>       return 0;
>>   }
>>
>> @@ -2353,17 +2371,21 @@ netdev_dpdk_vhost_update_rx_size_counters(struct netdev_stats *stats,
>>   }
>>
>>   static inline void
>> -netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>> +netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev, int qid,
>>                                        struct dp_packet **packets, int count,
>>                                        int qos_drops)
>>   {
>>       struct netdev_stats *stats = &dev->stats;
>> +    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->rxq[qid];
> 
> reverse xmas tree?

Will fix it here and elsewhere.

> 
>>       struct dp_packet *packet;
>>       unsigned int packet_size;
>>       int i;
>>
>>       stats->rx_packets += count;
>> +    q_stats->packets += count;
>>       stats->rx_dropped += qos_drops;
>> +    q_stats->errors += qos_drops;
>> +
>>       for (i = 0; i < count; i++) {
>>           packet = packets[i];
>>           packet_size = dp_packet_size(packet);
>> @@ -2374,6 +2396,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>>                * further processing. */
>>               stats->rx_errors++;
>>               stats->rx_length_errors++;
>> +            q_stats->errors++;
>>               continue;
>>           }
>>
>> @@ -2385,6 +2408,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>>           }
>>
>>           stats->rx_bytes += packet_size;
>> +        q_stats->bytes += packet_size;
>>       }
>>
>>       if (OVS_UNLIKELY(qos_drops)) {
>> @@ -2437,7 +2461,7 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
>>       }
>>
>>       rte_spinlock_lock(&dev->stats_lock);
>> -    netdev_dpdk_vhost_update_rx_counters(dev, batch->packets,
>> +    netdev_dpdk_vhost_update_rx_counters(dev, qid, batch->packets,
>>                                            nb_rx, qos_drops);
>>       rte_spinlock_unlock(&dev->stats_lock);
>>
>> @@ -2551,7 +2575,7 @@ netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts,
>>   }
>>
>>   static inline void
>> -netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
>> +netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev, int qid,
>>                                        struct dp_packet **packets,
>>                                        int attempted,
>>                                        struct netdev_dpdk_sw_stats *sw_stats_add)
>> @@ -2561,14 +2585,20 @@ netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
>>                     sw_stats_add->tx_failure_drops +
>>                     sw_stats_add->tx_invalid_hwol_drops;
>>       struct netdev_stats *stats = &dev->stats;
>> +    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->txq[qid];
> 
> reverse xmas tree?
> 
> 
>>       int sent = attempted - dropped;
>>       int i;
>>
>>       stats->tx_packets += sent;
>> +    q_stats->packets += sent;
>>       stats->tx_dropped += dropped;
>> +    q_stats->errors += dropped;
>>
>>       for (i = 0; i < sent; i++) {
>> -        stats->tx_bytes += dp_packet_size(packets[i]);
>> +        uint64_t bytes = dp_packet_size(packets[i]);
>> +
>> +        stats->tx_bytes += bytes;
>> +        q_stats->bytes += bytes;
>>       }
>>
>>       if (OVS_UNLIKELY(dropped || sw_stats_add->tx_retries)) {
>> @@ -2656,7 +2686,7 @@ __netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
>>       sw_stats_add.tx_retries = MIN(retries, max_retries);
>>
>>       rte_spinlock_lock(&dev->stats_lock);
>> -    netdev_dpdk_vhost_update_tx_counters(dev, pkts, total_packets,
>> +    netdev_dpdk_vhost_update_tx_counters(dev, qid, pkts, total_packets,
>>                                            &sw_stats_add);
>>       rte_spinlock_unlock(&dev->stats_lock);
>>
>> @@ -3286,6 +3316,72 @@ netdev_dpdk_get_sw_custom_stats(const struct netdev *netdev,
>>       return 0;
>>   }
>>
>> +static int
>> +netdev_dpdk_vhost_get_custom_stats(const struct netdev *netdev,
>> +                                struct netdev_custom_stats *custom_stats)
>> +{
>> +    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
>> +    int sw_stats_size, i, j;
>> +
>> +    netdev_dpdk_get_sw_custom_stats(netdev, custom_stats);
>> +
>> +    ovs_mutex_lock(&dev->mutex);
>> +
>> +    sw_stats_size = custom_stats->size;
>> +    custom_stats->size += netdev->n_rxq * sizeof(*dev->sw_stats->rxq) /
>> +                          sizeof(uint64_t);
>> +    custom_stats->size += netdev->n_txq * sizeof(*dev->sw_stats->txq) /
>> +                          sizeof(uint64_t);
>> +
>> +    custom_stats->counters = xrealloc(custom_stats->counters,
>> +                                      custom_stats->size *
>> +                                      sizeof *custom_stats->counters);
> 
> Names and values must be synced in the code below and this might get
> broken later.
> 
> We could reuse the same kind of "trick" than for sw_stats.
> This should make adding stats less error prone.
> 
> WDYT of (just build tested) diff:
> 

I think that sounds reasonable. It will bring consistency with sw_stats
and I agree that it will be less error prone.

> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index e80d5b4ab5..8ad9b785f7 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -3327,52 +3327,53 @@ netdev_dpdk_vhost_get_custom_stats(const
> struct netdev *netdev,
> 
>       ovs_mutex_lock(&dev->mutex);
> 
> -    sw_stats_size = custom_stats->size;
> -    custom_stats->size += netdev->n_rxq * sizeof(*dev->sw_stats->rxq) /
> -                          sizeof(uint64_t);
> -    custom_stats->size += netdev->n_txq * sizeof(*dev->sw_stats->txq) /
> -                          sizeof(uint64_t);
> +#define VHOST_Q_STATS \
> +    VHOST_Q_STAT(bytes) \
> +    VHOST_Q_STAT(packets) \
> +    VHOST_Q_STAT(errors)
> 
> +    sw_stats_size = custom_stats->size;
> +#define VHOST_Q_STAT(NAME) + netdev->n_rxq
> +    custom_stats->size += VHOST_Q_STATS;
> +#undef VHOST_Q_STAT
> +#define VHOST_Q_STAT(NAME) + netdev->n_txq
> +    custom_stats->size += VHOST_Q_STATS;
> +#undef VHOST_Q_STAT
>       custom_stats->counters = xrealloc(custom_stats->counters,
>                                         custom_stats->size *
>                                         sizeof *custom_stats->counters);
> 
>       j = 0;
>       for (i = 0; i < netdev->n_rxq; i++) {
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_bytes", i);
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_packets", i);
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_errors", i);
> +#define VHOST_Q_STAT(NAME) \
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name, \
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_"#NAME, i);
> +        VHOST_Q_STATS
> +#undef VHOST_Q_STAT
>       }
> -
>       for (i = 0; i < netdev->n_txq; i++) {
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_bytes", i);
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_packets", i);
> -        snprintf(custom_stats->counters[sw_stats_size + j++].name,
> -                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_errors", i);
> +#define VHOST_Q_STAT(NAME) \
> +        snprintf(custom_stats->counters[sw_stats_size + j++].name, \
> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_"#NAME, i);
> +        VHOST_Q_STATS
> +#undef VHOST_Q_STAT
>       }
> 
>       rte_spinlock_lock(&dev->stats_lock);
> 
>       j = 0;
>       for (i = 0; i < netdev->n_rxq; i++) {
> -        struct netdev_dpdk_vhost_q_stats *rxq_stats = &dev->sw_stats->rxq[i];
> -
> -        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->bytes;
> -        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->packets;
> -        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->errors;
> +#define VHOST_Q_STAT(NAME) \
> +        custom_stats->counters[sw_stats_size + j++].value =
> dev->sw_stats->rxq[i].NAME;
> +        VHOST_Q_STATS
> +#undef VHOST_Q_STAT
>       }
> 
>       for (i = 0; i < netdev->n_txq; i++) {
> -        struct netdev_dpdk_vhost_q_stats *txq_stats = &dev->sw_stats->txq[i];
> -
> -        custom_stats->counters[sw_stats_size + j++].value = txq_stats->bytes;
> -        custom_stats->counters[sw_stats_size + j++].value = txq_stats->packets;
> -        custom_stats->counters[sw_stats_size + j++].value = txq_stats->errors;
> +#define VHOST_Q_STAT(NAME) \
> +        custom_stats->counters[sw_stats_size + j++].value =
> dev->sw_stats->txq[i].NAME;
> +        VHOST_Q_STATS
> +#undef VHOST_Q_STAT
>       }
> 
>       rte_spinlock_unlock(&dev->stats_lock);
> 
> 
> 
>> +
>> +    j = 0;
>> +    for (i = 0; i < netdev->n_rxq; i++) {
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_bytes", i);
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_packets", i);
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_errors", i);
>> +    }
>> +
>> +    for (i = 0; i < netdev->n_txq; i++) {
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_bytes", i);
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_packets", i);
>> +        snprintf(custom_stats->counters[sw_stats_size + j++].name,
>> +                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_errors", i);
>> +    }
>> +
>> +    rte_spinlock_lock(&dev->stats_lock);
>> +
>> +    j = 0;
>> +    for (i = 0; i < netdev->n_rxq; i++) {
>> +        struct netdev_dpdk_vhost_q_stats *rxq_stats = &dev->sw_stats->rxq[i];
>> +
>> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->bytes;
>> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->packets;
>> +        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->errors;
>> +    }
>> +
>> +    for (i = 0; i < netdev->n_txq; i++) {
>> +        struct netdev_dpdk_vhost_q_stats *txq_stats = &dev->sw_stats->txq[i];
>> +
>> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->bytes;
>> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->packets;
>> +        custom_stats->counters[sw_stats_size + j++].value = txq_stats->errors;
>> +    }
>> +
>> +    rte_spinlock_unlock(&dev->stats_lock);
>> +
>> +    ovs_mutex_unlock(&dev->mutex);
>> +
>> +    return 0;
>> +}
>> +
>>   static int
>>   netdev_dpdk_get_features(const struct netdev *netdev,
>>                            enum netdev_features *current,
>> @@ -5043,9 +5139,12 @@ static int
>>   dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
>>       OVS_REQUIRES(dev->mutex)
>>   {
>> +    int old_n_txq = dev->up.n_txq;
>> +    int old_n_rxq = dev->up.n_rxq;
>> +    int err;
>> +
>>       dev->up.n_txq = dev->requested_n_txq;
>>       dev->up.n_rxq = dev->requested_n_rxq;
>> -    int err;
>>
>>       /* Always keep RX queue 0 enabled for implementations that won't
>>        * report vring states. */
>> @@ -5063,6 +5162,34 @@ dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
>>
>>       netdev_dpdk_remap_txqs(dev);
>>
>> +    if (dev->up.n_txq != old_n_txq) {
>> +        struct netdev_dpdk_vhost_q_stats *old_txq_stats, *new_txq_stats;
>> +
>> +        new_txq_stats = xcalloc(dev->up.n_txq, sizeof *dev->sw_stats->txq);
>> +        rte_spinlock_lock(&dev->stats_lock);
>> +        old_txq_stats = dev->sw_stats->txq;
>> +        memcpy(new_txq_stats, old_txq_stats,
>> +                MIN(dev->up.n_txq, old_n_txq) * sizeof *dev->sw_stats->txq);
>> +        dev->sw_stats->txq = new_txq_stats;
> 
> I prefer realloc and setting extra space to 0, but it is equivalent,
> no strong opinion on the implementation.

I initialy did it the same way you suggest, but then thought it would be
better to avoid calling xrealloc() with the spinlock held. What do you
think?

> 
>> +        rte_spinlock_unlock(&dev->stats_lock);
>> +        free(old_txq_stats);
>> +
>> +    }
>> +
>> +    if (dev->up.n_rxq != old_n_rxq) {
>> +        struct netdev_dpdk_vhost_q_stats *old_rxq_stats, *new_rxq_stats;
>> +
>> +        new_rxq_stats = xcalloc(dev->up.n_rxq, sizeof *dev->sw_stats->rxq);
>> +        rte_spinlock_lock(&dev->stats_lock);
>> +        old_rxq_stats = dev->sw_stats->rxq;
>> +        memcpy(new_rxq_stats, old_rxq_stats,
>> +                MIN(dev->up.n_rxq, old_n_rxq) * sizeof *dev->sw_stats->rxq);
>> +        dev->sw_stats->rxq = new_rxq_stats;
>> +        rte_spinlock_unlock(&dev->stats_lock);
>> +        free(old_rxq_stats);
>> +
>> +    }
>> +
> 
> I did not test your patch yet (will do later this week once I get to
> the end of this series).
> However I think we are missing some reset to 0 for per q stats in
> netdev_dpdk_update_flags__.

Hmm... I think you're right, good catch!

Thanks for the review,
Maxime
David Marchand Dec. 8, 2021, 12:48 p.m. UTC | #3
On Tue, Dec 7, 2021 at 11:11 PM Maxime Coquelin
<maxime.coquelin@redhat.com> wrote:
> On 12/7/21 21:37, David Marchand wrote:
> > On Wed, Nov 24, 2021 at 10:24 PM Maxime Coquelin
> > <maxime.coquelin@redhat.com> wrote:
> >>
> >> HXPS feature will enable steering Tx packets on transmist
> >
> > transmit*
> >
> >> queues based on their hashes. In order to test the feature,
> >
> > "their hashes" is ambiguous.
>
> s/their hashes/the packets hashes/ ?

Yep.


> >> @@ -5063,6 +5162,34 @@ dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
> >>
> >>       netdev_dpdk_remap_txqs(dev);
> >>
> >> +    if (dev->up.n_txq != old_n_txq) {
> >> +        struct netdev_dpdk_vhost_q_stats *old_txq_stats, *new_txq_stats;
> >> +
> >> +        new_txq_stats = xcalloc(dev->up.n_txq, sizeof *dev->sw_stats->txq);
> >> +        rte_spinlock_lock(&dev->stats_lock);
> >> +        old_txq_stats = dev->sw_stats->txq;
> >> +        memcpy(new_txq_stats, old_txq_stats,
> >> +                MIN(dev->up.n_txq, old_n_txq) * sizeof *dev->sw_stats->txq);
> >> +        dev->sw_stats->txq = new_txq_stats;
> >
> > I prefer realloc and setting extra space to 0, but it is equivalent,
> > no strong opinion on the implementation.
>
> I initialy did it the same way you suggest, but then thought it would be
> better to avoid calling xrealloc() with the spinlock held. What do you
> think?

Indeed, your implementation is better.
David Marchand Dec. 8, 2021, 8:25 p.m. UTC | #4
On Wed, Nov 24, 2021 at 10:24 PM Maxime Coquelin
<maxime.coquelin@redhat.com> wrote:
> @@ -2385,6 +2408,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>          }
>
>          stats->rx_bytes += packet_size;
> +        q_stats->bytes += packet_size;
>      }
>
>      if (OVS_UNLIKELY(qos_drops)) {
> @@ -2437,7 +2461,7 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
>      }
>
>      rte_spinlock_lock(&dev->stats_lock);
> -    netdev_dpdk_vhost_update_rx_counters(dev, batch->packets,
> +    netdev_dpdk_vhost_update_rx_counters(dev, qid, batch->packets,

After some testing, instead of qid here, we should use rxq->queue_id :-)
The tx part is ok.


>                                           nb_rx, qos_drops);
>      rte_spinlock_unlock(&dev->stats_lock);
>

[snip]


With this fix, I start a vm with a 4 rxq vhost port.
This vhost port has a virtio counterpart bound to the vm virtio-net kmod.

After a while, I can see some stats for rx and tx packets:
# ovs-vsctl get interface vhost4 statistics | sed -e 's#[{}]##g' -e
's#, #\n#g' | grep -v '=0$'
rx_1_to_64_packets=15
rx_256_to_511_packets=25
rx_65_to_127_packets=35
rx_bytes=12185
rx_packets=75
rx_q1_bytes=242
rx_q1_packets=3
rx_q2_bytes=242
rx_q2_packets=3
rx_q3_bytes=11701
rx_q3_packets=69
tx_bytes=2414
tx_packets=20
tx_q0_bytes=2414
tx_q0_packets=20

All good, and per q stats nicely sums to the same value than the "global" stats.

I then stop the vm and look at stats:
# ovs-vsctl get interface vhost4 statistics | sed -e 's#[{}]##g' -e
's#, #\n#g' | grep -v '=0$'
rx_1_to_64_packets=15
rx_256_to_511_packets=26
rx_65_to_127_packets=38
rx_bytes=12776
rx_packets=79
tx_bytes=2414
tx_dropped=1
tx_packets=20
tx_q0_bytes=2414
tx_q0_packets=20


So we end up with misalignment of "global" stats and per q stats in
this situation.
Can we do something about it?

I am unsure about resetting all stats to 0, but it could be the
simpler solution...
Maxime Coquelin Dec. 9, 2021, 10:19 a.m. UTC | #5
On 12/8/21 21:25, David Marchand wrote:
> On Wed, Nov 24, 2021 at 10:24 PM Maxime Coquelin
> <maxime.coquelin@redhat.com> wrote:
>> @@ -2385,6 +2408,7 @@ netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
>>           }
>>
>>           stats->rx_bytes += packet_size;
>> +        q_stats->bytes += packet_size;
>>       }
>>
>>       if (OVS_UNLIKELY(qos_drops)) {
>> @@ -2437,7 +2461,7 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
>>       }
>>
>>       rte_spinlock_lock(&dev->stats_lock);
>> -    netdev_dpdk_vhost_update_rx_counters(dev, batch->packets,
>> +    netdev_dpdk_vhost_update_rx_counters(dev, qid, batch->packets,
> 
> After some testing, instead of qid here, we should use rxq->queue_id :-)
> The tx part is ok.

Oops. Yes, it needs to be rxq->queue_id.

> 
>>                                            nb_rx, qos_drops);
>>       rte_spinlock_unlock(&dev->stats_lock);
>>
> 
> [snip]
> 
> 
> With this fix, I start a vm with a 4 rxq vhost port.
> This vhost port has a virtio counterpart bound to the vm virtio-net kmod.
> 
> After a while, I can see some stats for rx and tx packets:
> # ovs-vsctl get interface vhost4 statistics | sed -e 's#[{}]##g' -e
> 's#, #\n#g' | grep -v '=0$'
> rx_1_to_64_packets=15
> rx_256_to_511_packets=25
> rx_65_to_127_packets=35
> rx_bytes=12185
> rx_packets=75
> rx_q1_bytes=242
> rx_q1_packets=3
> rx_q2_bytes=242
> rx_q2_packets=3
> rx_q3_bytes=11701
> rx_q3_packets=69
> tx_bytes=2414
> tx_packets=20
> tx_q0_bytes=2414
> tx_q0_packets=20
> 
> All good, and per q stats nicely sums to the same value than the "global" stats.
> 
> I then stop the vm and look at stats:
> # ovs-vsctl get interface vhost4 statistics | sed -e 's#[{}]##g' -e
> 's#, #\n#g' | grep -v '=0$'
> rx_1_to_64_packets=15
> rx_256_to_511_packets=26
> rx_65_to_127_packets=38
> rx_bytes=12776
> rx_packets=79
> tx_bytes=2414
> tx_dropped=1
> tx_packets=20
> tx_q0_bytes=2414
> tx_q0_packets=20
> 
> 
> So we end up with misalignment of "global" stats and per q stats in
> this situation.
> Can we do something about it?
> 
> I am unsure about resetting all stats to 0, but it could be the
> simpler solution...

I would vote for resetting all counters on number of queues changes,
both per-queue and global, as discussed off-list. That's the only way to
keep consistency between global and per-queue counters.

Maxime

>
Maxime Coquelin Dec. 9, 2021, 2:51 p.m. UTC | #6
On 12/7/21 21:37, David Marchand wrote:
>> +struct netdev_dpdk_vhost_q_stats {
>> +    uint64_t bytes;
>> +    uint64_t packets;
>> +    uint64_t errors;
>> +};
>> +
>>   /* Custom software stats for dpdk ports */
>>   struct netdev_dpdk_sw_stats {
>>       /* No. of retries when unable to transmit. */
>> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
>>       uint64_t rx_qos_drops;
>>       /* Packet drops in HWOL processing. */
>>       uint64_t tx_invalid_hwol_drops;
>> +    /* Per-queue Vhost Tx stats */
>> +    struct netdev_dpdk_vhost_q_stats *txq;
>> +    /* Per-queue Vhost Rx stats */
>> +    struct netdev_dpdk_vhost_q_stats *rxq;
> |Here, we add "driver" specific stats, while netdev_dpdk_sw_stats struct 
> carries OVS "own" stats. This netdev_dpdk_sw_stats struct is converted 
> by netdev_dpdk_get_sw_custom_stats and there is a small framework on 
> adding custom OVS stats (using some macros "trick"). I'd rather leave 
> netdev_dpdk_sw_stats struct untouched for consistency. Pointers to vhost 
> specific stats can be added to the netdev_dpdk struct (we have some 
> spare space after the pointer to netdev_dpdk_sw_stats). |

I checked with pahole, and the comment stating "/*36 pad bytes here.*/"
is outdated. Since it is difficult to maintain it given it needs to be
updated when struct netdev_stats is modified, I will just remove it in
next revision.

Maxime
David Marchand Dec. 9, 2021, 3:04 p.m. UTC | #7
On Thu, Dec 9, 2021 at 3:51 PM Maxime Coquelin
<maxime.coquelin@redhat.com> wrote:
> On 12/7/21 21:37, David Marchand wrote:
> >> +struct netdev_dpdk_vhost_q_stats {
> >> +    uint64_t bytes;
> >> +    uint64_t packets;
> >> +    uint64_t errors;
> >> +};
> >> +
> >>   /* Custom software stats for dpdk ports */
> >>   struct netdev_dpdk_sw_stats {
> >>       /* No. of retries when unable to transmit. */
> >> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
> >>       uint64_t rx_qos_drops;
> >>       /* Packet drops in HWOL processing. */
> >>       uint64_t tx_invalid_hwol_drops;
> >> +    /* Per-queue Vhost Tx stats */
> >> +    struct netdev_dpdk_vhost_q_stats *txq;
> >> +    /* Per-queue Vhost Rx stats */
> >> +    struct netdev_dpdk_vhost_q_stats *rxq;
> > |Here, we add "driver" specific stats, while netdev_dpdk_sw_stats struct
> > carries OVS "own" stats. This netdev_dpdk_sw_stats struct is converted
> > by netdev_dpdk_get_sw_custom_stats and there is a small framework on
> > adding custom OVS stats (using some macros "trick"). I'd rather leave
> > netdev_dpdk_sw_stats struct untouched for consistency. Pointers to vhost
> > specific stats can be added to the netdev_dpdk struct (we have some
> > spare space after the pointer to netdev_dpdk_sw_stats). |
>
> I checked with pahole, and the comment stating "/*36 pad bytes here.*/"
> is outdated. Since it is difficult to maintain it given it needs to be
> updated when struct netdev_stats is modified, I will just remove it in
> next revision.

Yeah... I don't like those comments which become obsolete too easily.
+1
Ilya Maximets Dec. 9, 2021, 4:29 p.m. UTC | #8
On 12/9/21 16:04, David Marchand wrote:
> On Thu, Dec 9, 2021 at 3:51 PM Maxime Coquelin
> <maxime.coquelin@redhat.com> wrote:
>> On 12/7/21 21:37, David Marchand wrote:
>>>> +struct netdev_dpdk_vhost_q_stats {
>>>> +    uint64_t bytes;
>>>> +    uint64_t packets;
>>>> +    uint64_t errors;
>>>> +};
>>>> +
>>>>   /* Custom software stats for dpdk ports */
>>>>   struct netdev_dpdk_sw_stats {
>>>>       /* No. of retries when unable to transmit. */
>>>> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
>>>>       uint64_t rx_qos_drops;
>>>>       /* Packet drops in HWOL processing. */
>>>>       uint64_t tx_invalid_hwol_drops;
>>>> +    /* Per-queue Vhost Tx stats */
>>>> +    struct netdev_dpdk_vhost_q_stats *txq;
>>>> +    /* Per-queue Vhost Rx stats */
>>>> +    struct netdev_dpdk_vhost_q_stats *rxq;
>>> |Here, we add "driver" specific stats, while netdev_dpdk_sw_stats struct
>>> carries OVS "own" stats. This netdev_dpdk_sw_stats struct is converted
>>> by netdev_dpdk_get_sw_custom_stats and there is a small framework on
>>> adding custom OVS stats (using some macros "trick"). I'd rather leave
>>> netdev_dpdk_sw_stats struct untouched for consistency. Pointers to vhost
>>> specific stats can be added to the netdev_dpdk struct (we have some
>>> spare space after the pointer to netdev_dpdk_sw_stats). |
>>
>> I checked with pahole, and the comment stating "/*36 pad bytes here.*/"
>> is outdated. Since it is difficult to maintain it given it needs to be
>> updated when struct netdev_stats is modified, I will just remove it in
>> next revision.
> 
> Yeah... I don't like those comments which become obsolete too easily.
> +1

For the reference:  
d9d73f84ea22 ("Revert "dpif_netdev: Refactor dp_netdev_pmd_thread structure."")

This one needs a follow up. :)
Not sure if you want to go into that rabbit hole right now though.

Best regards, Ilya Maximets.
Maxime Coquelin Dec. 9, 2021, 4:33 p.m. UTC | #9
On 12/9/21 17:29, Ilya Maximets wrote:
> On 12/9/21 16:04, David Marchand wrote:
>> On Thu, Dec 9, 2021 at 3:51 PM Maxime Coquelin
>> <maxime.coquelin@redhat.com> wrote:
>>> On 12/7/21 21:37, David Marchand wrote:
>>>>> +struct netdev_dpdk_vhost_q_stats {
>>>>> +    uint64_t bytes;
>>>>> +    uint64_t packets;
>>>>> +    uint64_t errors;
>>>>> +};
>>>>> +
>>>>>    /* Custom software stats for dpdk ports */
>>>>>    struct netdev_dpdk_sw_stats {
>>>>>        /* No. of retries when unable to transmit. */
>>>>> @@ -206,6 +213,10 @@ struct netdev_dpdk_sw_stats {
>>>>>        uint64_t rx_qos_drops;
>>>>>        /* Packet drops in HWOL processing. */
>>>>>        uint64_t tx_invalid_hwol_drops;
>>>>> +    /* Per-queue Vhost Tx stats */
>>>>> +    struct netdev_dpdk_vhost_q_stats *txq;
>>>>> +    /* Per-queue Vhost Rx stats */
>>>>> +    struct netdev_dpdk_vhost_q_stats *rxq;
>>>> |Here, we add "driver" specific stats, while netdev_dpdk_sw_stats struct
>>>> carries OVS "own" stats. This netdev_dpdk_sw_stats struct is converted
>>>> by netdev_dpdk_get_sw_custom_stats and there is a small framework on
>>>> adding custom OVS stats (using some macros "trick"). I'd rather leave
>>>> netdev_dpdk_sw_stats struct untouched for consistency. Pointers to vhost
>>>> specific stats can be added to the netdev_dpdk struct (we have some
>>>> spare space after the pointer to netdev_dpdk_sw_stats). |
>>>
>>> I checked with pahole, and the comment stating "/*36 pad bytes here.*/"
>>> is outdated. Since it is difficult to maintain it given it needs to be
>>> updated when struct netdev_stats is modified, I will just remove it in
>>> next revision.
>>
>> Yeah... I don't like those comments which become obsolete too easily.
>> +1
> 
> For the reference:
> d9d73f84ea22 ("Revert "dpif_netdev: Refactor dp_netdev_pmd_thread structure."")
> 
> This one needs a follow up. :)
> Not sure if you want to go into that rabbit hole right now though.

Right now, not really. :)
But I can for sure take the action for the next release.

Regards,
Maxime

> Best regards, Ilya Maximets.
>
diff mbox series

Patch

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index ca92c947a..e80d5b4ab 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -192,6 +192,13 @@  static const struct vhost_device_ops virtio_net_device_ops =
     .guest_notified = vhost_guest_notified,
 };
 
+/* Custome software per-queue stats for Vhost ports */
+struct netdev_dpdk_vhost_q_stats {
+    uint64_t bytes;
+    uint64_t packets;
+    uint64_t errors;
+};
+
 /* Custom software stats for dpdk ports */
 struct netdev_dpdk_sw_stats {
     /* No. of retries when unable to transmit. */
@@ -206,6 +213,10 @@  struct netdev_dpdk_sw_stats {
     uint64_t rx_qos_drops;
     /* Packet drops in HWOL processing. */
     uint64_t tx_invalid_hwol_drops;
+    /* Per-queue Vhost Tx stats */
+    struct netdev_dpdk_vhost_q_stats *txq;
+    /* Per-queue Vhost Rx stats */
+    struct netdev_dpdk_vhost_q_stats *rxq;
 };
 
 enum dpdk_dev_type {
@@ -1276,6 +1287,13 @@  common_construct(struct netdev *netdev, dpdk_port_t port_no,
     dev->sw_stats = xzalloc(sizeof *dev->sw_stats);
     dev->sw_stats->tx_retries = (dev->type == DPDK_DEV_VHOST) ? 0 : UINT64_MAX;
 
+    if (dev->type == DPDK_DEV_VHOST) {
+        dev->sw_stats->txq = xcalloc(netdev->n_txq,
+                                     sizeof *dev->sw_stats->txq);
+        dev->sw_stats->rxq = xcalloc(netdev->n_rxq,
+                                     sizeof *dev->sw_stats->rxq);
+    }
+
     return 0;
 }
 
@@ -2353,17 +2371,21 @@  netdev_dpdk_vhost_update_rx_size_counters(struct netdev_stats *stats,
 }
 
 static inline void
-netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
+netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev, int qid,
                                      struct dp_packet **packets, int count,
                                      int qos_drops)
 {
     struct netdev_stats *stats = &dev->stats;
+    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->rxq[qid];
     struct dp_packet *packet;
     unsigned int packet_size;
     int i;
 
     stats->rx_packets += count;
+    q_stats->packets += count;
     stats->rx_dropped += qos_drops;
+    q_stats->errors += qos_drops;
+
     for (i = 0; i < count; i++) {
         packet = packets[i];
         packet_size = dp_packet_size(packet);
@@ -2374,6 +2396,7 @@  netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
              * further processing. */
             stats->rx_errors++;
             stats->rx_length_errors++;
+            q_stats->errors++;
             continue;
         }
 
@@ -2385,6 +2408,7 @@  netdev_dpdk_vhost_update_rx_counters(struct netdev_dpdk *dev,
         }
 
         stats->rx_bytes += packet_size;
+        q_stats->bytes += packet_size;
     }
 
     if (OVS_UNLIKELY(qos_drops)) {
@@ -2437,7 +2461,7 @@  netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
     }
 
     rte_spinlock_lock(&dev->stats_lock);
-    netdev_dpdk_vhost_update_rx_counters(dev, batch->packets,
+    netdev_dpdk_vhost_update_rx_counters(dev, qid, batch->packets,
                                          nb_rx, qos_drops);
     rte_spinlock_unlock(&dev->stats_lock);
 
@@ -2551,7 +2575,7 @@  netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts,
 }
 
 static inline void
-netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
+netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev, int qid,
                                      struct dp_packet **packets,
                                      int attempted,
                                      struct netdev_dpdk_sw_stats *sw_stats_add)
@@ -2561,14 +2585,20 @@  netdev_dpdk_vhost_update_tx_counters(struct netdev_dpdk *dev,
                   sw_stats_add->tx_failure_drops +
                   sw_stats_add->tx_invalid_hwol_drops;
     struct netdev_stats *stats = &dev->stats;
+    struct netdev_dpdk_vhost_q_stats *q_stats = &dev->sw_stats->txq[qid];
     int sent = attempted - dropped;
     int i;
 
     stats->tx_packets += sent;
+    q_stats->packets += sent;
     stats->tx_dropped += dropped;
+    q_stats->errors += dropped;
 
     for (i = 0; i < sent; i++) {
-        stats->tx_bytes += dp_packet_size(packets[i]);
+        uint64_t bytes = dp_packet_size(packets[i]);
+
+        stats->tx_bytes += bytes;
+        q_stats->bytes += bytes;
     }
 
     if (OVS_UNLIKELY(dropped || sw_stats_add->tx_retries)) {
@@ -2656,7 +2686,7 @@  __netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
     sw_stats_add.tx_retries = MIN(retries, max_retries);
 
     rte_spinlock_lock(&dev->stats_lock);
-    netdev_dpdk_vhost_update_tx_counters(dev, pkts, total_packets,
+    netdev_dpdk_vhost_update_tx_counters(dev, qid, pkts, total_packets,
                                          &sw_stats_add);
     rte_spinlock_unlock(&dev->stats_lock);
 
@@ -3286,6 +3316,72 @@  netdev_dpdk_get_sw_custom_stats(const struct netdev *netdev,
     return 0;
 }
 
+static int
+netdev_dpdk_vhost_get_custom_stats(const struct netdev *netdev,
+                                struct netdev_custom_stats *custom_stats)
+{
+    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
+    int sw_stats_size, i, j;
+
+    netdev_dpdk_get_sw_custom_stats(netdev, custom_stats);
+
+    ovs_mutex_lock(&dev->mutex);
+
+    sw_stats_size = custom_stats->size;
+    custom_stats->size += netdev->n_rxq * sizeof(*dev->sw_stats->rxq) /
+                          sizeof(uint64_t);
+    custom_stats->size += netdev->n_txq * sizeof(*dev->sw_stats->txq) /
+                          sizeof(uint64_t);
+
+    custom_stats->counters = xrealloc(custom_stats->counters,
+                                      custom_stats->size *
+                                      sizeof *custom_stats->counters);
+
+    j = 0;
+    for (i = 0; i < netdev->n_rxq; i++) {
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_bytes", i);
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_packets", i);
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "rx_q%d_errors", i);
+    }
+
+    for (i = 0; i < netdev->n_txq; i++) {
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_bytes", i);
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_packets", i);
+        snprintf(custom_stats->counters[sw_stats_size + j++].name,
+                NETDEV_CUSTOM_STATS_NAME_SIZE, "tx_q%d_errors", i);
+    }
+
+    rte_spinlock_lock(&dev->stats_lock);
+
+    j = 0;
+    for (i = 0; i < netdev->n_rxq; i++) {
+        struct netdev_dpdk_vhost_q_stats *rxq_stats = &dev->sw_stats->rxq[i];
+
+        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->bytes;
+        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->packets;
+        custom_stats->counters[sw_stats_size + j++].value = rxq_stats->errors;
+    }
+
+    for (i = 0; i < netdev->n_txq; i++) {
+        struct netdev_dpdk_vhost_q_stats *txq_stats = &dev->sw_stats->txq[i];
+
+        custom_stats->counters[sw_stats_size + j++].value = txq_stats->bytes;
+        custom_stats->counters[sw_stats_size + j++].value = txq_stats->packets;
+        custom_stats->counters[sw_stats_size + j++].value = txq_stats->errors;
+    }
+
+    rte_spinlock_unlock(&dev->stats_lock);
+
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
 static int
 netdev_dpdk_get_features(const struct netdev *netdev,
                          enum netdev_features *current,
@@ -5043,9 +5139,12 @@  static int
 dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
     OVS_REQUIRES(dev->mutex)
 {
+    int old_n_txq = dev->up.n_txq;
+    int old_n_rxq = dev->up.n_rxq;
+    int err;
+
     dev->up.n_txq = dev->requested_n_txq;
     dev->up.n_rxq = dev->requested_n_rxq;
-    int err;
 
     /* Always keep RX queue 0 enabled for implementations that won't
      * report vring states. */
@@ -5063,6 +5162,34 @@  dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
 
     netdev_dpdk_remap_txqs(dev);
 
+    if (dev->up.n_txq != old_n_txq) {
+        struct netdev_dpdk_vhost_q_stats *old_txq_stats, *new_txq_stats;
+
+        new_txq_stats = xcalloc(dev->up.n_txq, sizeof *dev->sw_stats->txq);
+        rte_spinlock_lock(&dev->stats_lock);
+        old_txq_stats = dev->sw_stats->txq;
+        memcpy(new_txq_stats, old_txq_stats,
+                MIN(dev->up.n_txq, old_n_txq) * sizeof *dev->sw_stats->txq);
+        dev->sw_stats->txq = new_txq_stats;
+        rte_spinlock_unlock(&dev->stats_lock);
+        free(old_txq_stats);
+
+    }
+
+    if (dev->up.n_rxq != old_n_rxq) {
+        struct netdev_dpdk_vhost_q_stats *old_rxq_stats, *new_rxq_stats;
+
+        new_rxq_stats = xcalloc(dev->up.n_rxq, sizeof *dev->sw_stats->rxq);
+        rte_spinlock_lock(&dev->stats_lock);
+        old_rxq_stats = dev->sw_stats->rxq;
+        memcpy(new_rxq_stats, old_rxq_stats,
+                MIN(dev->up.n_rxq, old_n_rxq) * sizeof *dev->sw_stats->rxq);
+        dev->sw_stats->rxq = new_rxq_stats;
+        rte_spinlock_unlock(&dev->stats_lock);
+        free(old_rxq_stats);
+
+    }
+
     err = netdev_dpdk_mempool_configure(dev);
     if (!err) {
         /* A new mempool was created or re-used. */
@@ -5467,7 +5594,7 @@  static const struct netdev_class dpdk_vhost_class = {
     .send = netdev_dpdk_vhost_send,
     .get_carrier = netdev_dpdk_vhost_get_carrier,
     .get_stats = netdev_dpdk_vhost_get_stats,
-    .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
+    .get_custom_stats = netdev_dpdk_vhost_get_custom_stats,
     .get_status = netdev_dpdk_vhost_user_get_status,
     .reconfigure = netdev_dpdk_vhost_reconfigure,
     .rxq_recv = netdev_dpdk_vhost_rxq_recv,
@@ -5483,7 +5610,7 @@  static const struct netdev_class dpdk_vhost_client_class = {
     .send = netdev_dpdk_vhost_send,
     .get_carrier = netdev_dpdk_vhost_get_carrier,
     .get_stats = netdev_dpdk_vhost_get_stats,
-    .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
+    .get_custom_stats = netdev_dpdk_vhost_get_custom_stats,
     .get_status = netdev_dpdk_vhost_user_get_status,
     .reconfigure = netdev_dpdk_vhost_client_reconfigure,
     .rxq_recv = netdev_dpdk_vhost_rxq_recv,