qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

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>

authored by

Yuri Benditovich and committed by
Jason Wang
59079029 3f429a34

+174 -9
+3
hw/net/trace-events
··· 381 381 virtio_net_announce_timer(int round) "%d" 382 382 virtio_net_handle_announce(int round) "%d" 383 383 virtio_net_post_load_device(void) 384 + virtio_net_rss_disable(void) 385 + virtio_net_rss_error(const char *msg, uint32_t value) "%s, value 0x%08x" 386 + virtio_net_rss_enable(uint32_t p1, uint16_t p2, uint8_t p3) "hashes 0x%x, table of %d, key of %d" 384 387 385 388 # tulip.c 386 389 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
+158 -9
hw/net/virtio-net.c
··· 77 77 tso/gso/gro 'off'. */ 78 78 #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000 79 79 80 + #define VIRTIO_NET_RSS_SUPPORTED_HASHES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \ 81 + VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \ 82 + VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \ 83 + VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \ 84 + VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \ 85 + VIRTIO_NET_RSS_HASH_TYPE_UDPv6 | \ 86 + VIRTIO_NET_RSS_HASH_TYPE_IP_EX | \ 87 + VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \ 88 + VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) 89 + 80 90 /* temporary until standard header include it */ 81 91 #if !defined(VIRTIO_NET_HDR_F_RSC_INFO) 82 92 ··· 108 118 .end = endof(struct virtio_net_config, mtu)}, 109 119 {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX, 110 120 .end = endof(struct virtio_net_config, duplex)}, 121 + {.flags = 1ULL << VIRTIO_NET_F_RSS, 122 + .end = endof(struct virtio_net_config, supported_hash_types)}, 111 123 {} 112 124 }; 113 125 ··· 138 150 memcpy(netcfg.mac, n->mac, ETH_ALEN); 139 151 virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed); 140 152 netcfg.duplex = n->net_conf.duplex; 153 + netcfg.rss_max_key_size = VIRTIO_NET_RSS_MAX_KEY_SIZE; 154 + virtio_stw_p(vdev, &netcfg.rss_max_indirection_table_length, 155 + VIRTIO_NET_RSS_MAX_TABLE_LEN); 156 + virtio_stl_p(vdev, &netcfg.supported_hash_types, 157 + VIRTIO_NET_RSS_SUPPORTED_HASHES); 141 158 memcpy(config, &netcfg, n->config_size); 142 159 } 143 160 ··· 701 718 return features; 702 719 } 703 720 721 + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); 704 722 features = vhost_net_get_features(get_vhost_net(nc->peer), features); 705 723 vdev->backend_features = features; 706 724 ··· 860 878 } 861 879 862 880 virtio_net_set_multiqueue(n, 881 + virtio_has_feature(features, VIRTIO_NET_F_RSS) || 863 882 virtio_has_feature(features, VIRTIO_NET_F_MQ)); 864 883 865 884 virtio_net_set_mrg_rx_bufs(n, ··· 1136 1155 } 1137 1156 } 1138 1157 1158 + static void virtio_net_disable_rss(VirtIONet *n) 1159 + { 1160 + if (n->rss_data.enabled) { 1161 + trace_virtio_net_rss_disable(); 1162 + } 1163 + n->rss_data.enabled = false; 1164 + } 1165 + 1166 + static uint16_t virtio_net_handle_rss(VirtIONet *n, 1167 + struct iovec *iov, unsigned int iov_cnt) 1168 + { 1169 + VirtIODevice *vdev = VIRTIO_DEVICE(n); 1170 + struct virtio_net_rss_config cfg; 1171 + size_t s, offset = 0, size_get; 1172 + uint16_t queues, i; 1173 + struct { 1174 + uint16_t us; 1175 + uint8_t b; 1176 + } QEMU_PACKED temp; 1177 + const char *err_msg = ""; 1178 + uint32_t err_value = 0; 1179 + 1180 + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) { 1181 + err_msg = "RSS is not negotiated"; 1182 + goto error; 1183 + } 1184 + size_get = offsetof(struct virtio_net_rss_config, indirection_table); 1185 + s = iov_to_buf(iov, iov_cnt, offset, &cfg, size_get); 1186 + if (s != size_get) { 1187 + err_msg = "Short command buffer"; 1188 + err_value = (uint32_t)s; 1189 + goto error; 1190 + } 1191 + n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types); 1192 + n->rss_data.indirections_len = 1193 + virtio_lduw_p(vdev, &cfg.indirection_table_mask); 1194 + n->rss_data.indirections_len++; 1195 + if (!is_power_of_2(n->rss_data.indirections_len)) { 1196 + err_msg = "Invalid size of indirection table"; 1197 + err_value = n->rss_data.indirections_len; 1198 + goto error; 1199 + } 1200 + if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { 1201 + err_msg = "Too large indirection table"; 1202 + err_value = n->rss_data.indirections_len; 1203 + goto error; 1204 + } 1205 + n->rss_data.default_queue = 1206 + virtio_lduw_p(vdev, &cfg.unclassified_queue); 1207 + if (n->rss_data.default_queue >= n->max_queues) { 1208 + err_msg = "Invalid default queue"; 1209 + err_value = n->rss_data.default_queue; 1210 + goto error; 1211 + } 1212 + offset += size_get; 1213 + size_get = sizeof(uint16_t) * n->rss_data.indirections_len; 1214 + g_free(n->rss_data.indirections_table); 1215 + n->rss_data.indirections_table = g_malloc(size_get); 1216 + if (!n->rss_data.indirections_table) { 1217 + err_msg = "Can't allocate indirections table"; 1218 + err_value = n->rss_data.indirections_len; 1219 + goto error; 1220 + } 1221 + s = iov_to_buf(iov, iov_cnt, offset, 1222 + n->rss_data.indirections_table, size_get); 1223 + if (s != size_get) { 1224 + err_msg = "Short indirection table buffer"; 1225 + err_value = (uint32_t)s; 1226 + goto error; 1227 + } 1228 + for (i = 0; i < n->rss_data.indirections_len; ++i) { 1229 + uint16_t val = n->rss_data.indirections_table[i]; 1230 + n->rss_data.indirections_table[i] = virtio_lduw_p(vdev, &val); 1231 + } 1232 + offset += size_get; 1233 + size_get = sizeof(temp); 1234 + s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get); 1235 + if (s != size_get) { 1236 + err_msg = "Can't get queues"; 1237 + err_value = (uint32_t)s; 1238 + goto error; 1239 + } 1240 + queues = virtio_lduw_p(vdev, &temp.us); 1241 + if (queues == 0 || queues > n->max_queues) { 1242 + err_msg = "Invalid number of queues"; 1243 + err_value = queues; 1244 + goto error; 1245 + } 1246 + if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) { 1247 + err_msg = "Invalid key size"; 1248 + err_value = temp.b; 1249 + goto error; 1250 + } 1251 + if (!temp.b && n->rss_data.hash_types) { 1252 + err_msg = "No key provided"; 1253 + err_value = 0; 1254 + goto error; 1255 + } 1256 + if (!temp.b && !n->rss_data.hash_types) { 1257 + virtio_net_disable_rss(n); 1258 + return queues; 1259 + } 1260 + offset += size_get; 1261 + size_get = temp.b; 1262 + s = iov_to_buf(iov, iov_cnt, offset, n->rss_data.key, size_get); 1263 + if (s != size_get) { 1264 + err_msg = "Can get key buffer"; 1265 + err_value = (uint32_t)s; 1266 + goto error; 1267 + } 1268 + n->rss_data.enabled = true; 1269 + trace_virtio_net_rss_enable(n->rss_data.hash_types, 1270 + n->rss_data.indirections_len, 1271 + temp.b); 1272 + return queues; 1273 + error: 1274 + trace_virtio_net_rss_error(err_msg, err_value); 1275 + virtio_net_disable_rss(n); 1276 + return 0; 1277 + } 1278 + 1139 1279 static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, 1140 1280 struct iovec *iov, unsigned int iov_cnt) 1141 1281 { 1142 1282 VirtIODevice *vdev = VIRTIO_DEVICE(n); 1143 - struct virtio_net_ctrl_mq mq; 1144 - size_t s; 1145 1283 uint16_t queues; 1146 1284 1147 - s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq)); 1148 - if (s != sizeof(mq)) { 1149 - return VIRTIO_NET_ERR; 1150 - } 1285 + virtio_net_disable_rss(n); 1286 + if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) { 1287 + queues = virtio_net_handle_rss(n, iov, iov_cnt); 1288 + } else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 1289 + struct virtio_net_ctrl_mq mq; 1290 + size_t s; 1291 + if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ)) { 1292 + return VIRTIO_NET_ERR; 1293 + } 1294 + s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq)); 1295 + if (s != sizeof(mq)) { 1296 + return VIRTIO_NET_ERR; 1297 + } 1298 + queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); 1151 1299 1152 - if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 1300 + } else { 1153 1301 return VIRTIO_NET_ERR; 1154 1302 } 1155 - 1156 - queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); 1157 1303 1158 1304 if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || 1159 1305 queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || ··· 3111 3257 g_free(n->vqs); 3112 3258 qemu_del_nic(n->nic); 3113 3259 virtio_net_rsc_cleanup(n); 3260 + g_free(n->rss_data.indirections_table); 3114 3261 virtio_cleanup(vdev); 3115 3262 } 3116 3263 ··· 3212 3359 DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features, 3213 3360 VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true), 3214 3361 DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false), 3362 + DEFINE_PROP_BIT64("rss", VirtIONet, host_features, 3363 + VIRTIO_NET_F_RSS, false), 3215 3364 DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features, 3216 3365 VIRTIO_NET_F_RSC_EXT, false), 3217 3366 DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
+13
include/hw/virtio/virtio-net.h
··· 126 126 /* Maximum packet size we can receive from tap device: header + 64k */ 127 127 #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB)) 128 128 129 + #define VIRTIO_NET_RSS_MAX_KEY_SIZE 40 130 + #define VIRTIO_NET_RSS_MAX_TABLE_LEN 128 131 + 132 + typedef struct VirtioNetRssData { 133 + bool enabled; 134 + uint32_t hash_types; 135 + uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; 136 + uint16_t indirections_len; 137 + uint16_t *indirections_table; 138 + uint16_t default_queue; 139 + } VirtioNetRssData; 140 + 129 141 typedef struct VirtIONetQueue { 130 142 VirtQueue *rx_vq; 131 143 VirtQueue *tx_vq; ··· 199 211 bool failover; 200 212 DeviceListener primary_listener; 201 213 Notifier migration_state; 214 + VirtioNetRssData rss_data; 202 215 }; 203 216 204 217 void virtio_net_set_netclient_name(VirtIONet *n, const char *name,