qemu: Set noqueue qdisc for TAP devices

By default, pfifo_fast queueing discipline (qdisc) is set on
newly created interfaces (including TAPs). This qdisc has three
queues and packets that want to be sent through given NIC are
placed into one of the queues based on TOS field. Queues are then
emptied based on their priority allowing interactive sessions
stay interactive whilst something else is downloading a large
file.

Obviously, this means that kernel has to be involved and some
locking has to happen (when placing packets into queues). If
virtualization is taken into account then the above algorithm
happens twice - once in the guest and the second time in the
host.

This is arguably not optimal as it burns host CPU cycles
needlessly. Guest already made it choice and sent packets in the
order it wants.

To resolve this, Linux kernel offers 'noqueue' qdisc which can be
applied on virtual interfaces and in fact for 'lo' it is by
default:

  lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue

Set it for other TAP devices we create for domains too. With this
change I was able to squeeze 1Mbps more from a macvtap attached
to a guest and to my 1Gbps LAN (as measured by iperf3).

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1329644
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
This commit is contained in:
Michal Privoznik 2020-10-08 16:22:50 +02:00
parent 01559528e5
commit 0b66196d86
6 changed files with 54 additions and 1 deletions

View File

@ -8267,6 +8267,8 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver,
}
}
qemuDomainInterfaceSetDefaultQDisc(driver, net);
if (net->mtu &&
virNetDevSetMTU(net->ifname, net->mtu) < 0)
goto cleanup;

View File

@ -11049,3 +11049,39 @@ qemuDomainFileWrapperFDClose(virDomainObjPtr vm,
}
return ret;
}
/**
* qemuDomainInterfaceSetDefaultQDisc:
* @driver: QEMU driver
* @net: domain interface
*
* Set the noqueue qdisc on @net if running as privileged. The
* noqueue qdisc is a lockless transmit and thus faster than the
* default pfifo_fast (at least in theory). But we can modify
* root qdisc only if we have CAP_NET_ADMIN.
*
* Returns: 0 on success,
* -1 otherwise.
*/
int
qemuDomainInterfaceSetDefaultQDisc(virQEMUDriverPtr driver,
virDomainNetDefPtr net)
{
virDomainNetType actualType = virDomainNetGetActualType(net);
if (!driver->privileged || !net->ifname)
return 0;
/* We want only those types which are represented as TAP
* devices in the host. */
if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET ||
actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE ||
actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
if (virNetDevSetRootQDisc(net->ifname, "noqueue") < 0)
return -1;
}
return 0;
}

View File

@ -1046,3 +1046,7 @@ qemuDomainOpenFile(virQEMUDriverPtr driver,
int
qemuDomainFileWrapperFDClose(virDomainObjPtr vm,
virFileWrapperFdPtr fd);
int
qemuDomainInterfaceSetDefaultQDisc(virQEMUDriverPtr driver,
virDomainNetDefPtr net);

View File

@ -1368,6 +1368,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
virNetDevSetMTU(net->ifname, net->mtu) < 0)
goto cleanup;
qemuDomainInterfaceSetDefaultQDisc(driver, net);
for (i = 0; i < tapfdSize; i++) {
if (qemuSecuritySetTapFDLabel(driver->securityManager,
vm->def, tapfd[i]) < 0)

View File

@ -313,6 +313,7 @@ int virNetDevRunEthernetScript(const char *ifname, const char *script)
G_GNUC_NO_INLINE;
int virNetDevSetRootQDisc(const char *ifname,
const char *qdisc);
const char *qdisc)
G_GNUC_NO_INLINE;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetDevRxFilter, virNetDevRxFilterFree);

View File

@ -286,3 +286,11 @@ qemuBuildTPMOpenBackendFDs(const char *tpmdev G_GNUC_UNUSED,
*cancelfd = 1731;
return 0;
}
int
virNetDevSetRootQDisc(const char *ifname G_GNUC_UNUSED,
const char *qdisc G_GNUC_UNUSED)
{
return 0;
}