In order to hotplug a network/bridge backed NIC, we need to first create
the tap file descriptor, add the tap interface to the bridge and then
pass the file descriptor to the qemu process using the 'getfd' monitor
command.
Once the tapfd has been accepted, we create the network backend using
host_net_add, supplying the name assigned to the tapfd. If this fails,
we need to close the tapfd in qemu using the 'closefd' monitor command.
* src/qemu_driver.c: add support for tapfd based hotplug in
qemudDomainAttachNetDevice()
---
src/qemu_driver.c | 73 +++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index a7861be..23cbd8f 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -4620,13 +4620,15 @@ static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr
conn,
}
static int qemudDomainAttachNetDevice(virConnectPtr conn,
+ struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev,
unsigned int qemuCmdFlags)
{
virDomainNetDefPtr net =
dev->data.net;
char *cmd = NULL, *reply = NULL, *remove_cmd = NULL;
- int i;
+ char *tapfd_name = NULL, *tapfd_close = NULL;
+ int i, tapfd = -1;
unsigned domain, bus, slot;
if (!(qemuCmdFlags & QEMUD_CMD_FLAG_HOST_NET_ADD)) {
@@ -4637,10 +4639,16 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
- qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
- _("network device type '%s' cannot be
attached"),
- virDomainNetTypeToString(net->type));
- return -1;
+ if (vm->monitor_chr->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
+ qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+ _("network device type '%s' cannot be attached:
"
+ "qemu is not using a unix socket monitor"),
+ virDomainNetTypeToString(net->type));
+ return -1;
+ }
+
+ if ((tapfd = qemudNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0)
+ return -1;
}
if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0)
@@ -4658,27 +4666,55 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
if (vm->def->nets[i]->vlan >= net->vlan)
net->vlan = vm->def->nets[i]->vlan;
- if (qemuBuildHostNetStr(conn, net,
- "host_net_add ", ' ', net->vlan, NULL,
&cmd) < 0)
- goto cleanup;
+ if (tapfd != -1) {
+ if (virAsprintf(&tapfd_name, "fd-%s", net->hostnet_name) <
0)
+ goto no_memory;
+
+ if (virAsprintf(&tapfd_close, "closefd %s", tapfd_name) < 0)
+ goto no_memory;
+
+ if (virAsprintf(&cmd, "getfd %s", tapfd_name) < 0)
+ goto no_memory;
+
+ if (qemudMonitorCommandWithFd(vm, cmd, tapfd, &reply) < 0) {
+ qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+ _("failed to pass fd to qemu with '%s'"),
cmd);
+ goto cleanup;
+ }
+
+ DEBUG("%s: getfd reply: %s", vm->def->name, reply);
+
+ VIR_FREE(reply);
+ VIR_FREE(cmd);
+ }
+
+ if (qemuBuildHostNetStr(conn, net, "host_net_add ", ' ',
+ net->vlan, tapfd_name, &cmd) < 0)
+ goto try_tapfd_close;
remove_cmd = NULL;
if (net->vlan >= 0 && net->hostnet_name &&
virAsprintf(&remove_cmd, "host_net_remove %d %s",
net->vlan, net->hostnet_name) < 0) {
- goto no_memory;
+ virReportOOMError(conn);
+ goto try_tapfd_close;
}
if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
_("failed to add network backend with '%s'"),
cmd);
- goto cleanup;
+ goto try_tapfd_close;
}
DEBUG("%s: host_net_add reply: %s", vm->def->name, reply);
VIR_FREE(reply);
VIR_FREE(cmd);
+ VIR_FREE(tapfd_name);
+ VIR_FREE(tapfd_close);
+ if (tapfd != -1)
+ close(tapfd);
+ tapfd = -1;
if (qemuBuildNicStr(conn, net,
"pci_add pci_addr=auto ", ' ', net->vlan,
&cmd) < 0)
@@ -4719,12 +4755,27 @@ try_remove:
VIR_DEBUG("%s: host_net_remove reply: %s\n", vm->def->name,
reply);
goto cleanup;
+try_tapfd_close:
+ VIR_FREE(reply);
+
+ if (tapfd_close) {
+ if (qemudMonitorCommand(vm, tapfd_close, &reply) < 0)
+ VIR_WARN(_("Failed to close tapfd with '%s'\n"),
tapfd_close);
+ else
+ VIR_DEBUG("%s: closefd: %s\n", vm->def->name, reply);
+ }
+ goto cleanup;
+
no_memory:
virReportOOMError(conn);
cleanup:
VIR_FREE(cmd);
VIR_FREE(reply);
VIR_FREE(remove_cmd);
+ VIR_FREE(tapfd_close);
+ VIR_FREE(tapfd_name);
+ if (tapfd != -1)
+ close(tapfd);
return -1;
}
@@ -4854,7 +4905,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
goto cleanup;
}
} else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
- ret = qemudDomainAttachNetDevice(dom->conn, vm, dev, qemuCmdFlags);
+ ret = qemudDomainAttachNetDevice(dom->conn, driver, vm, dev, qemuCmdFlags);
} else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS
&&
dev->data.hostdev->source.subsys.type ==
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
--
1.6.2.5