virtio-net: implement RSS configuration command
Optionally report RSS feature. Handle RSS configuration command and keep RSS parameters in virtio-net device context. Signed-off-by: Yuri Benditovich <yuri.benditovich@daynix.com> Signed-off-by: Jason Wang <jasowang@redhat.com>
This commit is contained in:
parent
3f429a3400
commit
590790297c
@ -381,6 +381,9 @@ virtio_net_announce_notify(void) ""
|
||||
virtio_net_announce_timer(int round) "%d"
|
||||
virtio_net_handle_announce(int round) "%d"
|
||||
virtio_net_post_load_device(void)
|
||||
virtio_net_rss_disable(void)
|
||||
virtio_net_rss_error(const char *msg, uint32_t value) "%s, value 0x%08x"
|
||||
virtio_net_rss_enable(uint32_t p1, uint16_t p2, uint8_t p3) "hashes 0x%x, table of %d, key of %d"
|
||||
|
||||
# tulip.c
|
||||
tulip_reg_write(uint64_t addr, const char *name, int size, uint64_t val) "addr 0x%02"PRIx64" (%s) size %d value 0x%08"PRIx64
|
||||
|
@ -77,6 +77,16 @@
|
||||
tso/gso/gro 'off'. */
|
||||
#define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
|
||||
|
||||
#define VIRTIO_NET_RSS_SUPPORTED_HASHES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_UDPv6 | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_IP_EX | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \
|
||||
VIRTIO_NET_RSS_HASH_TYPE_UDP_EX)
|
||||
|
||||
/* temporary until standard header include it */
|
||||
#if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
|
||||
|
||||
@ -108,6 +118,8 @@ static VirtIOFeature feature_sizes[] = {
|
||||
.end = endof(struct virtio_net_config, mtu)},
|
||||
{.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
|
||||
.end = endof(struct virtio_net_config, duplex)},
|
||||
{.flags = 1ULL << VIRTIO_NET_F_RSS,
|
||||
.end = endof(struct virtio_net_config, supported_hash_types)},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -138,6 +150,11 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
memcpy(netcfg.mac, n->mac, ETH_ALEN);
|
||||
virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
|
||||
netcfg.duplex = n->net_conf.duplex;
|
||||
netcfg.rss_max_key_size = VIRTIO_NET_RSS_MAX_KEY_SIZE;
|
||||
virtio_stw_p(vdev, &netcfg.rss_max_indirection_table_length,
|
||||
VIRTIO_NET_RSS_MAX_TABLE_LEN);
|
||||
virtio_stl_p(vdev, &netcfg.supported_hash_types,
|
||||
VIRTIO_NET_RSS_SUPPORTED_HASHES);
|
||||
memcpy(config, &netcfg, n->config_size);
|
||||
}
|
||||
|
||||
@ -701,6 +718,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
|
||||
return features;
|
||||
}
|
||||
|
||||
virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
|
||||
features = vhost_net_get_features(get_vhost_net(nc->peer), features);
|
||||
vdev->backend_features = features;
|
||||
|
||||
@ -860,6 +878,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
|
||||
}
|
||||
|
||||
virtio_net_set_multiqueue(n,
|
||||
virtio_has_feature(features, VIRTIO_NET_F_RSS) ||
|
||||
virtio_has_feature(features, VIRTIO_NET_F_MQ));
|
||||
|
||||
virtio_net_set_mrg_rx_bufs(n,
|
||||
@ -1136,25 +1155,152 @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_net_disable_rss(VirtIONet *n)
|
||||
{
|
||||
if (n->rss_data.enabled) {
|
||||
trace_virtio_net_rss_disable();
|
||||
}
|
||||
n->rss_data.enabled = false;
|
||||
}
|
||||
|
||||
static uint16_t virtio_net_handle_rss(VirtIONet *n,
|
||||
struct iovec *iov, unsigned int iov_cnt)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||
struct virtio_net_rss_config cfg;
|
||||
size_t s, offset = 0, size_get;
|
||||
uint16_t queues, i;
|
||||
struct {
|
||||
uint16_t us;
|
||||
uint8_t b;
|
||||
} QEMU_PACKED temp;
|
||||
const char *err_msg = "";
|
||||
uint32_t err_value = 0;
|
||||
|
||||
if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) {
|
||||
err_msg = "RSS is not negotiated";
|
||||
goto error;
|
||||
}
|
||||
size_get = offsetof(struct virtio_net_rss_config, indirection_table);
|
||||
s = iov_to_buf(iov, iov_cnt, offset, &cfg, size_get);
|
||||
if (s != size_get) {
|
||||
err_msg = "Short command buffer";
|
||||
err_value = (uint32_t)s;
|
||||
goto error;
|
||||
}
|
||||
n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types);
|
||||
n->rss_data.indirections_len =
|
||||
virtio_lduw_p(vdev, &cfg.indirection_table_mask);
|
||||
n->rss_data.indirections_len++;
|
||||
if (!is_power_of_2(n->rss_data.indirections_len)) {
|
||||
err_msg = "Invalid size of indirection table";
|
||||
err_value = n->rss_data.indirections_len;
|
||||
goto error;
|
||||
}
|
||||
if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) {
|
||||
err_msg = "Too large indirection table";
|
||||
err_value = n->rss_data.indirections_len;
|
||||
goto error;
|
||||
}
|
||||
n->rss_data.default_queue =
|
||||
virtio_lduw_p(vdev, &cfg.unclassified_queue);
|
||||
if (n->rss_data.default_queue >= n->max_queues) {
|
||||
err_msg = "Invalid default queue";
|
||||
err_value = n->rss_data.default_queue;
|
||||
goto error;
|
||||
}
|
||||
offset += size_get;
|
||||
size_get = sizeof(uint16_t) * n->rss_data.indirections_len;
|
||||
g_free(n->rss_data.indirections_table);
|
||||
n->rss_data.indirections_table = g_malloc(size_get);
|
||||
if (!n->rss_data.indirections_table) {
|
||||
err_msg = "Can't allocate indirections table";
|
||||
err_value = n->rss_data.indirections_len;
|
||||
goto error;
|
||||
}
|
||||
s = iov_to_buf(iov, iov_cnt, offset,
|
||||
n->rss_data.indirections_table, size_get);
|
||||
if (s != size_get) {
|
||||
err_msg = "Short indirection table buffer";
|
||||
err_value = (uint32_t)s;
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i < n->rss_data.indirections_len; ++i) {
|
||||
uint16_t val = n->rss_data.indirections_table[i];
|
||||
n->rss_data.indirections_table[i] = virtio_lduw_p(vdev, &val);
|
||||
}
|
||||
offset += size_get;
|
||||
size_get = sizeof(temp);
|
||||
s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get);
|
||||
if (s != size_get) {
|
||||
err_msg = "Can't get queues";
|
||||
err_value = (uint32_t)s;
|
||||
goto error;
|
||||
}
|
||||
queues = virtio_lduw_p(vdev, &temp.us);
|
||||
if (queues == 0 || queues > n->max_queues) {
|
||||
err_msg = "Invalid number of queues";
|
||||
err_value = queues;
|
||||
goto error;
|
||||
}
|
||||
if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) {
|
||||
err_msg = "Invalid key size";
|
||||
err_value = temp.b;
|
||||
goto error;
|
||||
}
|
||||
if (!temp.b && n->rss_data.hash_types) {
|
||||
err_msg = "No key provided";
|
||||
err_value = 0;
|
||||
goto error;
|
||||
}
|
||||
if (!temp.b && !n->rss_data.hash_types) {
|
||||
virtio_net_disable_rss(n);
|
||||
return queues;
|
||||
}
|
||||
offset += size_get;
|
||||
size_get = temp.b;
|
||||
s = iov_to_buf(iov, iov_cnt, offset, n->rss_data.key, size_get);
|
||||
if (s != size_get) {
|
||||
err_msg = "Can get key buffer";
|
||||
err_value = (uint32_t)s;
|
||||
goto error;
|
||||
}
|
||||
n->rss_data.enabled = true;
|
||||
trace_virtio_net_rss_enable(n->rss_data.hash_types,
|
||||
n->rss_data.indirections_len,
|
||||
temp.b);
|
||||
return queues;
|
||||
error:
|
||||
trace_virtio_net_rss_error(err_msg, err_value);
|
||||
virtio_net_disable_rss(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
|
||||
struct iovec *iov, unsigned int iov_cnt)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||
struct virtio_net_ctrl_mq mq;
|
||||
size_t s;
|
||||
uint16_t queues;
|
||||
|
||||
s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
|
||||
if (s != sizeof(mq)) {
|
||||
virtio_net_disable_rss(n);
|
||||
if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
|
||||
queues = virtio_net_handle_rss(n, iov, iov_cnt);
|
||||
} else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
|
||||
struct virtio_net_ctrl_mq mq;
|
||||
size_t s;
|
||||
if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ)) {
|
||||
return VIRTIO_NET_ERR;
|
||||
}
|
||||
s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
|
||||
if (s != sizeof(mq)) {
|
||||
return VIRTIO_NET_ERR;
|
||||
}
|
||||
queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
|
||||
|
||||
} else {
|
||||
return VIRTIO_NET_ERR;
|
||||
}
|
||||
|
||||
if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
|
||||
return VIRTIO_NET_ERR;
|
||||
}
|
||||
|
||||
queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
|
||||
|
||||
if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
|
||||
queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
|
||||
queues > n->max_queues ||
|
||||
@ -3111,6 +3257,7 @@ static void virtio_net_device_unrealize(DeviceState *dev)
|
||||
g_free(n->vqs);
|
||||
qemu_del_nic(n->nic);
|
||||
virtio_net_rsc_cleanup(n);
|
||||
g_free(n->rss_data.indirections_table);
|
||||
virtio_cleanup(vdev);
|
||||
}
|
||||
|
||||
@ -3212,6 +3359,8 @@ static Property virtio_net_properties[] = {
|
||||
DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
|
||||
DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
|
||||
DEFINE_PROP_BIT64("rss", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_RSS, false),
|
||||
DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_RSC_EXT, false),
|
||||
DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
|
||||
|
@ -126,6 +126,18 @@ typedef struct VirtioNetRscChain {
|
||||
/* Maximum packet size we can receive from tap device: header + 64k */
|
||||
#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB))
|
||||
|
||||
#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
|
||||
#define VIRTIO_NET_RSS_MAX_TABLE_LEN 128
|
||||
|
||||
typedef struct VirtioNetRssData {
|
||||
bool enabled;
|
||||
uint32_t hash_types;
|
||||
uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE];
|
||||
uint16_t indirections_len;
|
||||
uint16_t *indirections_table;
|
||||
uint16_t default_queue;
|
||||
} VirtioNetRssData;
|
||||
|
||||
typedef struct VirtIONetQueue {
|
||||
VirtQueue *rx_vq;
|
||||
VirtQueue *tx_vq;
|
||||
@ -199,6 +211,7 @@ struct VirtIONet {
|
||||
bool failover;
|
||||
DeviceListener primary_listener;
|
||||
Notifier migration_state;
|
||||
VirtioNetRssData rss_data;
|
||||
};
|
||||
|
||||
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
|
||||
|
Loading…
Reference in New Issue
Block a user