virtio: RSS support
Add RSS support to make use of multiple queues.
With 4 RX queues and RSS enabled
ping from host to guest vm ip queue use
192.168.2.3 192.168.2.1 2
'' 192.168.2.10 0
'' 192.168.2.5 1
'' 192.168.2.105 3
With 4 RX queues and RSS disabled, queue 0 is always used for all of the above cases
Type: improvement
Change-Id: I3ca78fd83fce26cbe8f23fee0a9034cb572bacb7
Signed-off-by: Steven Luong <sluong@cisco.com>
diff --git a/src/vnet/devices/virtio/FEATURE.yaml b/src/vnet/devices/virtio/FEATURE.yaml
index 446a45b..8700238 100644
--- a/src/vnet/devices/virtio/FEATURE.yaml
+++ b/src/vnet/devices/virtio/FEATURE.yaml
@@ -13,6 +13,7 @@
- Support virtio 1.1 packed ring in vhost
- Support for tx queue size configuration (tested on host kernel 5.15
and qemu version 6.2.0)
+ - Support RSS (tested on ubuntu 23.10)
description: "Virtio implementation"
missing:
- API dump filtering by sw_if_index
diff --git a/src/vnet/devices/virtio/cli.c b/src/vnet/devices/virtio/cli.c
index c1b6c8b..c436460 100644
--- a/src/vnet/devices/virtio/cli.c
+++ b/src/vnet/devices/virtio/cli.c
@@ -62,6 +62,8 @@
args.bind = VIRTIO_BIND_FORCE;
else if (unformat (line_input, "bind"))
args.bind = VIRTIO_BIND_DEFAULT;
+ else if (unformat (line_input, "rss-enabled"))
+ args.rss_enabled = 1;
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
@@ -77,7 +79,7 @@
.path = "create interface virtio",
.short_help = "create interface virtio <pci-address> "
"[feature-mask <hex-mask>] [tx-queue-size <size>] "
- "[gso-enabled] [csum-enabled] "
+ "[gso-enabled] [csum-enabled] [rss-enabled] "
"[buffering [size <buffering-szie>]] [packed] [bind [force]]",
.function = virtio_pci_create_command_fn,
};
diff --git a/src/vnet/devices/virtio/pci.c b/src/vnet/devices/virtio/pci.c
index 6234f64..140cdb9 100644
--- a/src/vnet/devices/virtio/pci.c
+++ b/src/vnet/devices/virtio/pci.c
@@ -37,6 +37,13 @@
#define PCI_MSIX_ENABLE 0x8000
+static const u8 virtio_rss_key[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67,
+ 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb,
+ 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30,
+ 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+};
+
static pci_device_id_t virtio_pci_device_ids[] = {
{
.vendor_id = PCI_VENDOR_ID_VIRTIO,
@@ -584,6 +591,35 @@
return status;
}
+static int
+virtio_pci_enable_multiqueue_rss (vlib_main_t *vm, virtio_if_t *vif,
+ u16 num_queues)
+{
+ virtio_ctrl_msg_t mq_hdr;
+ virtio_net_rss_config *rss = (virtio_net_rss_config *) mq_hdr.data;
+ virtio_net_ctrl_ack_t status = VIRTIO_NET_ERR;
+
+ STATIC_ASSERT (sizeof (*rss) <= sizeof (mq_hdr.data),
+ "virtio_net_rss_config size too big");
+ mq_hdr.ctrl.class = VIRTIO_NET_CTRL_MQ;
+ mq_hdr.ctrl.cmd = VIRTIO_NET_CTRL_MQ_RSS_CONFIG;
+ mq_hdr.status = VIRTIO_NET_ERR;
+
+ rss->hash_types = VIRTIO_NET_HASH_TYPE_SUPPORTED;
+ rss->indirection_table_mask = VIRTIO_NET_RSS_MAX_TABLE_LEN - 1;
+ rss->unclassified_queue = 0;
+ for (int i = 0; i < VIRTIO_NET_RSS_MAX_TABLE_LEN; i++)
+ rss->indirection_table[i] = i % num_queues;
+ rss->max_tx_vq = num_queues;
+ rss->hash_key_length = VIRTIO_NET_RSS_MAX_KEY_SIZE;
+ clib_memcpy (rss->hash_key_data, virtio_rss_key,
+ VIRTIO_NET_RSS_MAX_KEY_SIZE);
+
+ status = virtio_pci_send_ctrl_msg (vm, vif, &mq_hdr, sizeof (*rss));
+ virtio_log_debug (vif, "multi-queue with rss enable %u queues", num_queues);
+ return status;
+}
+
static u8
virtio_pci_queue_size_valid (u16 qsz)
{
@@ -933,6 +969,9 @@
| VIRTIO_FEATURE (VIRTIO_F_ANY_LAYOUT)
| VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC);
+ if (vif->rss_enabled)
+ supported_features |= VIRTIO_FEATURE (VIRTIO_NET_F_RSS);
+
if (vif->is_modern)
supported_features |= VIRTIO_FEATURE (VIRTIO_F_VERSION_1);
@@ -1375,6 +1414,7 @@
vif->dev_instance = vif - vim->interfaces;
vif->per_interface_next_index = ~0;
vif->pci_addr.as_u32 = args->addr;
+ vif->rss_enabled = args->rss_enabled;
if (args->virtio_flags & VIRTIO_FLAG_PACKED)
vif->is_packed = 1;
@@ -1536,8 +1576,16 @@
if ((vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_CTRL_VQ)) &&
(vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_MQ)))
{
- if (virtio_pci_enable_multiqueue (vm, vif, vif->max_queue_pairs))
- virtio_log_warning (vif, "multiqueue is not set");
+ if (vif->features & VIRTIO_FEATURE (VIRTIO_NET_F_RSS))
+ {
+ if (virtio_pci_enable_multiqueue_rss (vm, vif, vif->max_queue_pairs))
+ virtio_log_warning (vif, "multiqueue with rss is not set");
+ }
+ else
+ {
+ if (virtio_pci_enable_multiqueue (vm, vif, vif->max_queue_pairs))
+ virtio_log_warning (vif, "multiqueue is not set");
+ }
}
return;
diff --git a/src/vnet/devices/virtio/pci.h b/src/vnet/devices/virtio/pci.h
index 5eb80f8..745ad6f 100644
--- a/src/vnet/devices/virtio/pci.h
+++ b/src/vnet/devices/virtio/pci.h
@@ -60,6 +60,9 @@
/* If multiqueue is provided by host, then we support it. */
#define VIRTIO_NET_CTRL_MQ 4
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
+#define VIRTIO_NET_CTRL_MQ_RSS_CONFIG 1
+#define VIRTIO_NET_CTRL_MQ_HASH_CONFIG 2
+
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
@@ -139,14 +142,77 @@
u64 queue_device; /* read-write */
} virtio_pci_common_cfg_t;
+#define foreach_virtio_net_hash_report_type \
+ _ (NONE, 0) \
+ _ (IPV4, 1) \
+ _ (TCPV4, 2) \
+ _ (UDPV4, 3) \
+ _ (IPV6, 4) \
+ _ (TCPV6, 5) \
+ _ (UDPV6, 6) \
+ _ (IPV6_EX, 7) \
+ _ (TCPV6_EX, 8) \
+ _ (UDPV6_EX, 9)
+
+typedef enum
+{
+#define _(n, i) VIRTIO_NET_HASH_REPORT_##n = i,
+ foreach_virtio_net_hash_report_type
+#undef _
+} virtio_net_hash_report_type_t;
+
typedef struct
{
u8 mac[6];
u16 status;
u16 max_virtqueue_pairs;
u16 mtu;
+ u32 speed;
+ u8 duplex;
+ u8 rss_max_key_size;
+ u16 rss_max_indirection_table_length;
+ u32 supported_hash_types;
} virtio_net_config_t;
+#define VIRTIO_NET_RSS_MAX_TABLE_LEN 128
+#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
+
+#define foreach_virtio_net_hash_type \
+ _ (IPV4, 0) \
+ _ (TCPV4, 1) \
+ _ (UDPV4, 2) \
+ _ (IPV6, 3) \
+ _ (TCPV6, 4) \
+ _ (UDPV6, 5) \
+ _ (IPV6_EX, 6) \
+ _ (TCPV6_EX, 7) \
+ _ (UDPV6_EX, 8)
+
+typedef enum
+{
+#define _(n, i) VIRTIO_NET_HASH_TYPE_##n = (1 << i),
+ foreach_virtio_net_hash_type
+#undef _
+} virtio_net_hash_type_t;
+
+#define VIRTIO_NET_HASH_TYPE_SUPPORTED \
+ (VIRTIO_NET_HASH_TYPE_IPV4 | VIRTIO_NET_HASH_TYPE_TCPV4 | \
+ VIRTIO_NET_HASH_TYPE_UDPV4 | VIRTIO_NET_HASH_TYPE_IPV6 | \
+ VIRTIO_NET_HASH_TYPE_TCPV6 | VIRTIO_NET_HASH_TYPE_UDPV6 | \
+ VIRTIO_NET_HASH_TYPE_IPV6_EX | VIRTIO_NET_HASH_TYPE_TCPV6_EX | \
+ VIRTIO_NET_HASH_TYPE_UDPV6_EX)
+
+typedef struct
+{
+ u32 hash_types;
+ u16 indirection_table_mask;
+ u16 unclassified_queue;
+ u16 indirection_table[VIRTIO_NET_RSS_MAX_TABLE_LEN];
+ u16 max_tx_vq;
+ u8 hash_key_length;
+ u8 hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE];
+} virtio_net_rss_config;
+
/*
* Control virtqueue data structures
*
@@ -210,13 +276,14 @@
void (*device_debug_config_space) (vlib_main_t * vm, virtio_if_t * vif);
} virtio_pci_func_t;
-#define foreach_virtio_flags \
- _ (GSO, 0) \
- _ (CSUM_OFFLOAD, 1) \
- _ (GRO_COALESCE, 2) \
- _ (PACKED, 3) \
- _ (IN_ORDER, 4) \
- _ (BUFFERING, 5)
+#define foreach_virtio_flags \
+ _ (GSO, 0) \
+ _ (CSUM_OFFLOAD, 1) \
+ _ (GRO_COALESCE, 2) \
+ _ (PACKED, 3) \
+ _ (IN_ORDER, 4) \
+ _ (BUFFERING, 5) \
+ _ (RSS, 6)
typedef enum
{
@@ -243,6 +310,7 @@
u64 features;
u8 gso_enabled;
u8 checksum_offload_enabled;
+ u8 rss_enabled;
u32 tx_queue_size;
virtio_bind_t bind;
u32 buffering_size;
diff --git a/src/vnet/devices/virtio/virtio.api b/src/vnet/devices/virtio/virtio.api
index a11492e..14a5849 100644
--- a/src/vnet/devices/virtio/virtio.api
+++ b/src/vnet/devices/virtio/virtio.api
@@ -63,6 +63,7 @@
VIRTIO_API_FLAG_PACKED = 8, /* enable packed ring support, provided it is available from backend */
VIRTIO_API_FLAG_IN_ORDER = 16, /* enable in order support, provided it is available from backend */
VIRTIO_API_FLAG_BUFFERING = 32 [backwards_compatible], /* enable buffering to handle backend jitter/delays */
+ VIRTIO_API_FLAG_RSS = 64 [backwards_compatible], /* enable rss support */
};
/** \brief Initialize a new virtio pci interface with the given parameters
diff --git a/src/vnet/devices/virtio/virtio.c b/src/vnet/devices/virtio/virtio.c
index d2302fa..840936a 100644
--- a/src/vnet/devices/virtio/virtio.c
+++ b/src/vnet/devices/virtio/virtio.c
@@ -404,6 +404,7 @@
vlib_cli_output (vm, " csum-enabled %d", vif->csum_offload_enabled);
vlib_cli_output (vm, " packet-coalesce %d", vif->packet_coalesce);
vlib_cli_output (vm, " packet-buffering %d", vif->packet_buffering);
+ vlib_cli_output (vm, " rss-enabled %d", vif->rss_enabled);
if (type & (VIRTIO_IF_TYPE_TAP | VIRTIO_IF_TYPE_PCI))
vlib_cli_output (vm, " Mac Address: %U", format_ethernet_address,
vif->mac_addr);
diff --git a/src/vnet/devices/virtio/virtio.h b/src/vnet/devices/virtio/virtio.h
index 431b1d2..a8e2588 100644
--- a/src/vnet/devices/virtio/virtio.h
+++ b/src/vnet/devices/virtio/virtio.h
@@ -140,6 +140,7 @@
vnet_virtio_vring_t *txq_vrings;
int gso_enabled;
int csum_offload_enabled;
+ int rss_enabled;
union
{
int *tap_fds;
diff --git a/src/vnet/devices/virtio/virtio_api.c b/src/vnet/devices/virtio/virtio_api.c
index 3197a2f..2e25eff 100644
--- a/src/vnet/devices/virtio/virtio_api.c
+++ b/src/vnet/devices/virtio/virtio_api.c
@@ -111,18 +111,18 @@
STATIC_ASSERT (((int) VIRTIO_API_FLAG_BUFFERING ==
(int) VIRTIO_FLAG_BUFFERING),
"virtio buffering api flag mismatch");
+ STATIC_ASSERT (((int) VIRTIO_API_FLAG_RSS == (int) VIRTIO_FLAG_RSS),
+ "virtio rss api flag mismatch");
ap->virtio_flags = clib_net_to_host_u32 (mp->virtio_flags);
ap->features = clib_net_to_host_u64 (mp->features);
if (ap->virtio_flags & VIRTIO_API_FLAG_GSO)
ap->gso_enabled = 1;
- else
- ap->gso_enabled = 0;
if (ap->virtio_flags & VIRTIO_API_FLAG_CSUM_OFFLOAD)
ap->checksum_offload_enabled = 1;
- else
- ap->checksum_offload_enabled = 0;
+ if (ap->virtio_flags & VIRTIO_API_FLAG_RSS)
+ ap->rss_enabled = 1;
virtio_pci_create_if (vm, ap);
diff --git a/src/vnet/devices/virtio/virtio_std.h b/src/vnet/devices/virtio/virtio_std.h
index ec988c0..66b8bac 100644
--- a/src/vnet/devices/virtio/virtio_std.h
+++ b/src/vnet/devices/virtio/virtio_std.h
@@ -15,49 +15,61 @@
#ifndef __VIRTIO_STD_H__
#define __VIRTIO_STD_H__
-#define foreach_virtio_net_features \
- _ (VIRTIO_NET_F_CSUM, 0) /* Host handles pkts w/ partial csum */ \
- _ (VIRTIO_NET_F_GUEST_CSUM, 1) /* Guest handles pkts w/ partial csum */ \
- _ (VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, 2) /* Dynamic offload configuration. */ \
- _ (VIRTIO_NET_F_MTU, 3) /* Initial MTU advice. */ \
- _ (VIRTIO_NET_F_MAC, 5) /* Host has given MAC address. */ \
- _ (VIRTIO_NET_F_GSO, 6) /* Host handles pkts w/ any GSO. */ \
- _ (VIRTIO_NET_F_GUEST_TSO4, 7) /* Guest can handle TSOv4 in. */ \
- _ (VIRTIO_NET_F_GUEST_TSO6, 8) /* Guest can handle TSOv6 in. */ \
- _ (VIRTIO_NET_F_GUEST_ECN, 9) /* Guest can handle TSO[6] w/ ECN in. */ \
- _ (VIRTIO_NET_F_GUEST_UFO, 10) /* Guest can handle UFO in. */ \
- _ (VIRTIO_NET_F_HOST_TSO4, 11) /* Host can handle TSOv4 in. */ \
- _ (VIRTIO_NET_F_HOST_TSO6, 12) /* Host can handle TSOv6 in. */ \
- _ (VIRTIO_NET_F_HOST_ECN, 13) /* Host can handle TSO[6] w/ ECN in. */ \
- _ (VIRTIO_NET_F_HOST_UFO, 14) /* Host can handle UFO in. */ \
- _ (VIRTIO_NET_F_MRG_RXBUF, 15) /* Host can merge receive buffers. */ \
- _ (VIRTIO_NET_F_STATUS, 16) /* virtio_net_config.status available */ \
- _ (VIRTIO_NET_F_CTRL_VQ, 17) /* Control channel available */ \
- _ (VIRTIO_NET_F_CTRL_RX, 18) /* Control channel RX mode support */ \
- _ (VIRTIO_NET_F_CTRL_VLAN, 19) /* Control channel VLAN filtering */ \
- _ (VIRTIO_NET_F_CTRL_RX_EXTRA, 20) /* Extra RX mode control support */ \
- _ (VIRTIO_NET_F_GUEST_ANNOUNCE, 21) /* Guest can announce device on the network */ \
- _ (VIRTIO_NET_F_MQ, 22) /* Device supports Receive Flow Steering */ \
- _ (VIRTIO_NET_F_CTRL_MAC_ADDR, 23) /* Set MAC address */ \
- _ (VIRTIO_F_NOTIFY_ON_EMPTY, 24) \
- _ (VHOST_F_LOG_ALL, 26) /* Log all write descriptors */ \
- _ (VIRTIO_F_ANY_LAYOUT, 27) /* Can the device handle any descriptor layout */ \
- _ (VIRTIO_RING_F_INDIRECT_DESC, 28) /* Support indirect buffer descriptors */ \
- _ (VIRTIO_RING_F_EVENT_IDX, 29) /* The Guest publishes the used index for which it expects an interrupt \
- * at the end of the avail ring. Host should ignore the avail->flags field. */ \
-/* The Host publishes the avail index for which it expects a kick \
- * at the end of the used ring. Guest should ignore the used->flags field. */ \
- _ (VHOST_USER_F_PROTOCOL_FEATURES, 30) \
- _ (VIRTIO_F_VERSION_1, 32) /* v1.0 compliant. */ \
- _ (VIRTIO_F_IOMMU_PLATFORM, 33) \
- _ (VIRTIO_F_RING_PACKED, 34) \
- _ (VIRTIO_F_IN_ORDER, 35) /* all buffers are used by the device in the */ \
- /* same order in which they have been made available */ \
+#define foreach_virtio_net_features \
+ _ (VIRTIO_NET_F_CSUM, 0) /* Host handles pkts w/ partial csum */ \
+ _ (VIRTIO_NET_F_GUEST_CSUM, 1) /* Guest handles pkts w/ partial csum */ \
+ _ (VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \
+ 2) /* Dynamic offload configuration. */ \
+ _ (VIRTIO_NET_F_MTU, 3) /* Initial MTU advice. */ \
+ _ (VIRTIO_NET_F_MAC, 5) /* Host has given MAC address. */ \
+ _ (VIRTIO_NET_F_GSO, 6) /* Host handles pkts w/ any GSO. */ \
+ _ (VIRTIO_NET_F_GUEST_TSO4, 7) /* Guest can handle TSOv4 in. */ \
+ _ (VIRTIO_NET_F_GUEST_TSO6, 8) /* Guest can handle TSOv6 in. */ \
+ _ (VIRTIO_NET_F_GUEST_ECN, 9) /* Guest can handle TSO[6] w/ ECN in. */ \
+ _ (VIRTIO_NET_F_GUEST_UFO, 10) /* Guest can handle UFO in. */ \
+ _ (VIRTIO_NET_F_HOST_TSO4, 11) /* Host can handle TSOv4 in. */ \
+ _ (VIRTIO_NET_F_HOST_TSO6, 12) /* Host can handle TSOv6 in. */ \
+ _ (VIRTIO_NET_F_HOST_ECN, 13) /* Host can handle TSO[6] w/ ECN in. */ \
+ _ (VIRTIO_NET_F_HOST_UFO, 14) /* Host can handle UFO in. */ \
+ _ (VIRTIO_NET_F_MRG_RXBUF, 15) /* Host can merge receive buffers. */ \
+ _ (VIRTIO_NET_F_STATUS, 16) /* virtio_net_config.status available */ \
+ _ (VIRTIO_NET_F_CTRL_VQ, 17) /* Control channel available */ \
+ _ (VIRTIO_NET_F_CTRL_RX, 18) /* Control channel RX mode support */ \
+ _ (VIRTIO_NET_F_CTRL_VLAN, 19) /* Control channel VLAN filtering */ \
+ _ (VIRTIO_NET_F_CTRL_RX_EXTRA, 20) /* Extra RX mode control support */ \
+ _ (VIRTIO_NET_F_GUEST_ANNOUNCE, \
+ 21) /* Guest can announce device on the network */ \
+ _ (VIRTIO_NET_F_MQ, 22) /* Device supports Receive Flow Steering */ \
+ _ (VIRTIO_NET_F_CTRL_MAC_ADDR, 23) /* Set MAC address */ \
+ _ (VIRTIO_F_NOTIFY_ON_EMPTY, 24) \
+ _ (VHOST_F_LOG_ALL, 26) /* Log all write descriptors */ \
+ _ (VIRTIO_F_ANY_LAYOUT, \
+ 27) /* Can the device handle any descriptor layout */ \
+ _ (VIRTIO_RING_F_INDIRECT_DESC, \
+ 28) /* Support indirect buffer descriptors */ \
+ _ (VIRTIO_RING_F_EVENT_IDX, \
+ 29) /* The Guest publishes the used index for which it expects an \
+ * interrupt at the end of the avail ring. Host should ignore the \
+ * avail->flags field. */ \
+ /* The Host publishes the avail index for which it expects a kick \
+ * at the end of the used ring. Guest should ignore the used->flags field. \
+ */ \
+ _ (VHOST_USER_F_PROTOCOL_FEATURES, 30) \
+ _ (VIRTIO_F_VERSION_1, 32) /* v1.0 compliant. */ \
+ _ (VIRTIO_F_IOMMU_PLATFORM, 33) \
+ _ (VIRTIO_F_RING_PACKED, 34) \
+ _ (VIRTIO_F_IN_ORDER, 35) /* all buffers are used by the device in the */ \
+ /* same order in which they have been made available */ \
_ (VIRTIO_F_ORDER_PLATFORM, 36) /* memory accesses by the driver and the */ \
- /* device are ordered in a way described by the platfor */ \
- _ (VIRTIO_F_NOTIFICATION_DATA, 38) /* the driver passes extra data (besides */ \
- /* identifying the virtqueue) in its device notifications. */ \
- _ (VIRTIO_NET_F_SPEED_DUPLEX, 63) /* Device set linkspeed and duplex */
+ /* device are ordered in a way described by the platfor */ \
+ _ (VIRTIO_F_NOTIFICATION_DATA, \
+ 38) /* the driver passes extra data (besides */ \
+ /* identifying the virtqueue) in its device notifications. */ \
+ _ (VIRTIO_NET_F_RING_RESET, 40) /* Device supports individual ring reset */ \
+ _ (VIRTIO_NET_F_HASH_REPORT, \
+ 57) /* Device supports per packet hash value */ \
+ _ (VIRTIO_NET_F_RSS, 60) /* Device supports RSS */ \
+ _ (VIRTIO_NET_F_SPEED_DUPLEX, 63) /* Device set linkspeed and duplex */
typedef enum
{