/**
* Generate a name for net client
*
- * Only net clients created with the legacy -net option need this. Naming is
- * mandatory for net clients created with -netdev.
+ * Only net clients created with the legacy -net option and NICs need this.
*/
static char *assign_name(NetClientState *nc1, const char *model)
{
if (nc == nc1) {
continue;
}
- /* For compatibility only bump id for net clients on a vlan */
- if (strcmp(nc->model, model) == 0 &&
- net_hub_id_for_client(nc, NULL) == 0) {
+ if (strcmp(nc->model, model) == 0) {
id++;
}
}
const char *name,
void *opaque)
{
- NetClientState *nc;
+ NetClientState **peers = conf->peers.ncs;
NICState *nic;
+ int i, queues = MAX(1, conf->queues);
assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
assert(info->size >= sizeof(NICState));
- nc = qemu_new_net_client(info, conf->peer, model, name);
-
- nic = qemu_get_nic(nc);
+ nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
+ nic->ncs = (void *)nic + info->size;
nic->conf = conf;
nic->opaque = opaque;
+ for (i = 0; i < queues; i++) {
+ qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
+ NULL);
+ nic->ncs[i].queue_index = i;
+ }
+
return nic;
}
+NetClientState *qemu_get_subqueue(NICState *nic, int queue_index)
+{
+ return nic->ncs + queue_index;
+}
+
NetClientState *qemu_get_queue(NICState *nic)
{
- return &nic->nc;
+ return qemu_get_subqueue(nic, 0);
}
NICState *qemu_get_nic(NetClientState *nc)
{
- return DO_UPCAST(NICState, nc, nc);
+ NetClientState *nc0 = nc - nc->queue_index;
+
+ return (NICState *)((void *)nc0 - nc->info->size);
}
void *qemu_get_nic_opaque(NetClientState *nc)
void qemu_del_net_client(NetClientState *nc)
{
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ int queues, i;
+
+ /* If the NetClientState belongs to a multiqueue backend, we will change all
+ * other NetClientStates also.
+ */
+ queues = qemu_find_net_clients_except(nc->name, ncs,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ assert(queues != 0);
+
/* If there is a peer NIC, delete and cleanup client, but do not free. */
if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
NICState *nic = qemu_get_nic(nc->peer);
return;
}
nic->peer_deleted = true;
- /* Let NIC know peer is gone. */
- nc->peer->link_down = true;
+
+ for (i = 0; i < queues; i++) {
+ ncs[i]->peer->link_down = true;
+ }
+
if (nc->peer->info->link_status_changed) {
nc->peer->info->link_status_changed(nc->peer);
}
- qemu_cleanup_net_client(nc);
+
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ }
+
return;
}
assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
- qemu_cleanup_net_client(nc);
- qemu_free_net_client(nc);
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ qemu_free_net_client(ncs[i]);
+ }
}
void qemu_del_nic(NICState *nic)
{
- NetClientState *nc = qemu_get_queue(nic);
+ int i, queues = MAX(nic->conf->queues, 1);
+
/* If this is a peer NIC and peer has already been deleted, free it now. */
- if (nc->peer && nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
- NICState *nic = qemu_get_nic(nc);
- if (nic->peer_deleted) {
- qemu_free_net_client(nc->peer);
+ if (nic->peer_deleted) {
+ for (i = 0; i < queues; i++) {
+ qemu_free_net_client(qemu_get_subqueue(nic, i)->peer);
}
}
- qemu_cleanup_net_client(nc);
- qemu_free_net_client(nc);
+ for (i = queues - 1; i >= 0; i--) {
+ NetClientState *nc = qemu_get_subqueue(nic, i);
+
+ qemu_cleanup_net_client(nc);
+ qemu_free_net_client(nc);
+ }
+
+ g_free(nic);
}
void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
QTAILQ_FOREACH(nc, &net_clients, next) {
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
- func(qemu_get_nic(nc), opaque);
+ if (nc->queue_index == 0) {
+ func(qemu_get_nic(nc), opaque);
+ }
}
}
}
{
nc->receive_disabled = 0;
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ if (net_hub_flush(nc->peer)) {
+ qemu_notify_event();
+ }
+ return;
+ }
if (qemu_net_queue_flush(nc->send_queue)) {
/* We emptied the queue successfully, signal to the IO thread to repoll
* the file descriptor (for tap, for example).
static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
int iovcnt)
{
- uint8_t buffer[4096];
+ uint8_t buffer[NET_BUFSIZE];
size_t offset;
offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
void print_net_client(Monitor *mon, NetClientState *nc)
{
- monitor_printf(mon, "%s: type=%s,%s\n", nc->name,
- NetClientOptionsKind_lookup[nc->info->type], nc->info_str);
+ monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
+ nc->queue_index,
+ NetClientOptionsKind_lookup[nc->info->type],
+ nc->info_str);
}
void do_info_network(Monitor *mon, const QDict *qdict)
void qmp_set_link(const char *name, bool up, Error **errp)
{
- NetClientState *nc = NULL;
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ NetClientState *nc;
+ int queues, i;
- QTAILQ_FOREACH(nc, &net_clients, next) {
- if (!strcmp(nc->name, name)) {
- goto done;
- }
- }
-done:
- if (!nc) {
+ queues = qemu_find_net_clients_except(name, ncs,
+ NET_CLIENT_OPTIONS_KIND_MAX,
+ MAX_QUEUE_NUM);
+
+ if (queues == 0) {
error_set(errp, QERR_DEVICE_NOT_FOUND, name);
return;
}
+ nc = ncs[0];
- nc->link_down = !up;
+ for (i = 0; i < queues; i++) {
+ ncs[i]->link_down = !up;
+ }
if (nc->info->link_status_changed) {
nc->info->link_status_changed(nc);
void net_cleanup(void)
{
- NetClientState *nc, *next_vc;
+ NetClientState *nc;
- QTAILQ_FOREACH_SAFE(nc, &net_clients, next, next_vc) {
+ /* We may del multiple entries during qemu_del_net_client(),
+ * so QTAILQ_FOREACH_SAFE() is also not safe here.
+ */
+ while (!QTAILQ_EMPTY(&net_clients)) {
+ nc = QTAILQ_FIRST(&net_clients);
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
qemu_del_nic(qemu_get_nic(nc));
} else {