Hi David,
On 10/7/22 13:16, David Marchand wrote:
Request per virtqueue statistics from the vhost library and expose them
as per port OVS custom stats.
Thanks for the patch!
Note:
- the vhost stats API is experimental at the moment, this patch is
sent as a demonstrator,
I'm going to send a patch to mark the API stable, since it has been used
in Vhost PMD for a while, and the API seems good for OVS
- we may drop maintaining rx stats in OVS itself and instead aggregate
the per vq stats, this is something to investigate,
- a unit test is missing,
Signed-off-by: David Marchand <[email protected]>
---
lib/netdev-dpdk.c | 203 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 194 insertions(+), 9 deletions(-)
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 132ebb2843..3db5944977 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -532,11 +532,20 @@ struct netdev_dpdk {
);
PADDED_MEMBERS(CACHE_LINE_SIZE,
- /* Names of all XSTATS counters */
- struct rte_eth_xstat_name *rte_xstats_names;
- int rte_xstats_names_size;
- int rte_xstats_ids_size;
- uint64_t *rte_xstats_ids;
+ union {
+ struct {
+ /* Names of all XSTATS counters */
+ struct rte_eth_xstat_name *rte_xstats_names;
+ int rte_xstats_names_size;
+ int rte_xstats_ids_size;
+ uint64_t *rte_xstats_ids;
+ };
+ struct {
+ /* Names of all vhost stats */
+ struct rte_vhost_stat_name *vhost_stat_names;
+ int vhost_stat_size;
+ };
+ };
);
};
@@ -552,6 +561,7 @@ static int netdev_dpdk_get_sw_custom_stats(const struct netdev *,
struct netdev_custom_stats *);
static void netdev_dpdk_configure_xstats(struct netdev_dpdk *dev);
static void netdev_dpdk_clear_xstats(struct netdev_dpdk *dev);
+static void netdev_dpdk_vhost_clear_stats(struct netdev_dpdk *dev);
int netdev_dpdk_get_vid(const struct netdev_dpdk *dev);
@@ -1586,6 +1596,7 @@ netdev_dpdk_vhost_destruct(struct netdev *netdev)
dev->vhost_id = NULL;
rte_free(dev->vhost_rxq_enabled);
+ netdev_dpdk_vhost_clear_stats(dev);
common_destruct(dev);
ovs_mutex_unlock(&dpdk_mutex);
@@ -3039,6 +3050,80 @@ netdev_dpdk_vhost_get_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)
+{
+ netdev_dpdk_get_sw_custom_stats(netdev, custom_stats);
+
+#ifdef ALLOW_EXPERIMENTAL_API
+ struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
+
+ ovs_mutex_lock(&dev->mutex);
+
+ int vid = netdev_dpdk_get_vid(dev);
+
+ if (vid >= 0 && dev->vhost_stat_size > 0) {
+ struct rte_vhost_stat *vhost_stats;
+ int stat_offset;
+ int sw_stats_size;
+
+ vhost_stats = xcalloc(dev->vhost_stat_size, sizeof *vhost_stats);
+
+ stat_offset = 0;
+
+ for (int q = 0; q < dev->up.n_rxq; q++) {
+ int qid = q * VIRTIO_QNUM + VIRTIO_TXQ;
+ int err;
+
+ err = rte_vhost_vring_stats_get(vid, qid,
+ &vhost_stats[stat_offset],
+ dev->vhost_stat_size
+ - stat_offset);
+ if (err < 0 || stat_offset + err > dev->vhost_stat_size) {
I don't know if it would have a noticeable impact, but maybe you could
use OVS_UNLIKELY?
+ goto fail;
+ }
+ stat_offset += err;
+ }
+
+ for (int q = 0; q < dev->up.n_txq; q++) {
+ int qid = q * VIRTIO_QNUM;
+ int err;
+
+ err = rte_vhost_vring_stats_get(vid, qid,
+ &vhost_stats[stat_offset],
+ dev->vhost_stat_size
+ - stat_offset);
+ if (err < 0 || stat_offset + err > dev->vhost_stat_size) {
+ goto fail;
+ }
+ stat_offset += err;
+ }
+
+ sw_stats_size = custom_stats->size;
+ custom_stats->size += dev->vhost_stat_size;
+ custom_stats->counters = xrealloc(custom_stats->counters,
+ custom_stats->size *
+ sizeof *custom_stats->counters);
+
+ for (int i = 0; i < stat_offset; i++) {
+ ovs_strlcpy(custom_stats->counters[sw_stats_size + i].name,
+ dev->vhost_stat_names[i].name,
+ NETDEV_CUSTOM_STATS_NAME_SIZE);
+ custom_stats->counters[sw_stats_size + i].value =
+ vhost_stats[i].value;
+ }
+
+fail:
+ free(vhost_stats);
+ }
+
+ ovs_mutex_unlock(&dev->mutex);
+#endif
+
+ return 0;
+}
+
static void
netdev_dpdk_convert_xstats(struct netdev_stats *stats,
const struct rte_eth_xstat *xstats,
@@ -5014,12 +5099,107 @@ out:
return err;
}
+static void
+netdev_dpdk_vhost_clear_stats(struct netdev_dpdk *dev)
+ OVS_REQUIRES(dev->mutex)
+{
+ free(dev->vhost_stat_names);
+ dev->vhost_stat_names = NULL;
+ dev->vhost_stat_size = 0;
+};
+
+static void
+netdev_dpdk_vhost_configure_stats(struct netdev_dpdk *dev OVS_UNUSED)
+ OVS_REQUIRES(dev->mutex)
+{
+#ifdef ALLOW_EXPERIMENTAL_API
+ struct rte_vhost_stat_name *vhost_stat_names = NULL;
+ int vhost_stat_size;
+ int stat_offset;
+ int vid;
+
+ vid = netdev_dpdk_get_vid(dev);
+ if (vid < 0) {
+ goto fail;
+ }
+
+ vhost_stat_size = 0;
+
+ for (int q = 0; q < dev->up.n_rxq; q++) {
+ int qid = q * VIRTIO_QNUM + VIRTIO_TXQ;
+ int err;
+
+ err = rte_vhost_vring_stats_get_names(vid, qid, NULL, 0);
+ if (err < 0) {
+ goto fail;
+ }
+ vhost_stat_size += err;
+ }
+
+ for (int q = 0; q < dev->up.n_txq; q++) {
+ int qid = q * VIRTIO_QNUM;
+ int err;
+
+ err = rte_vhost_vring_stats_get_names(vid, qid, NULL, 0);
+ if (err < 0) {
+ goto fail;
+ }
+ vhost_stat_size += err;
+ }
+
+ vhost_stat_names = xcalloc(vhost_stat_size, sizeof *vhost_stat_names);
+
+ stat_offset = 0;
+
+ for (int q = 0; q < dev->up.n_rxq; q++) {
+ int qid = q * VIRTIO_QNUM + VIRTIO_TXQ;
+ int err;
+
+ err = rte_vhost_vring_stats_get_names(vid, qid,
+ &vhost_stat_names[stat_offset],
+ vhost_stat_size - stat_offset);
+ if (err < 0 || stat_offset + err > vhost_stat_size) {
+ goto fail;
+ }
+ stat_offset += err;
+ }
+
+ for (int q = 0; q < dev->up.n_txq; q++) {
+ int qid = q * VIRTIO_QNUM;
+ int err;
+
+ err = rte_vhost_vring_stats_get_names(vid, qid,
+ &vhost_stat_names[stat_offset],
+ vhost_stat_size - stat_offset);
+ if (err < 0 || stat_offset + err > vhost_stat_size) {
+ goto fail;
+ }
+ stat_offset += err;
+ }
+
+ dev->vhost_stat_names = vhost_stat_names;
+ vhost_stat_names = NULL;
+ dev->vhost_stat_size = vhost_stat_size;
+
+fail:
+ free(vhost_stat_names);
+#endif
+}
+
static int
dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
OVS_REQUIRES(dev->mutex)
{
- dev->up.n_txq = dev->requested_n_txq;
- dev->up.n_rxq = dev->requested_n_rxq;
+ if (dev->up.n_rxq != dev->requested_n_rxq
+ || dev->up.n_txq != dev->requested_n_txq
+ || dev->vhost_stat_size <= 0) {
+
+ dev->up.n_txq = dev->requested_n_txq;
+ dev->up.n_rxq = dev->requested_n_rxq;
+
+ netdev_dpdk_vhost_clear_stats(dev);
+ netdev_dpdk_vhost_configure_stats(dev);
+ }
/* Always keep RX queue 0 enabled for implementations that won't
* report vring states. */
@@ -5090,6 +5270,11 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev
*netdev)
/* Register client-mode device. */
vhost_flags |= RTE_VHOST_USER_CLIENT;
+#ifdef ALLOW_EXPERIMENTAL_API
+ /* Extended per vq statistics. */
+ vhost_flags |= RTE_VHOST_USER_NET_STATS_ENABLE;
+#endif
+
/* There is no support for multi-segments buffers. */
vhost_flags |= RTE_VHOST_USER_LINEARBUF_SUPPORT;
@@ -5489,7 +5674,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,
@@ -5505,7 +5690,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,
The patch looks good to me.
Thanks,
Maxime
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev