ch: Enable ETHERNET Network mode support

enable VIR_DOMAIN_NET_TYPE_ETHERNET network support for ch guests.

Tested with following interface config:

    <interface type='ethernet'>
      <target dev='chtap0' managed="yes"/>
      <model type='virtio'/>
      <driver queues='2'/>
    <interface>

Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Praveen K Paladugu 2024-01-16 15:25:43 -06:00 committed by Michal Privoznik
parent 05d46e4e08
commit 6316f26cd2
10 changed files with 462 additions and 136 deletions

View File

@ -21,6 +21,7 @@ src/bhyve/bhyve_process.c
src/ch/ch_conf.c
src/ch/ch_domain.c
src/ch/ch_driver.c
src/ch/ch_interface.c
src/ch/ch_monitor.c
src/ch/ch_process.c
src/conf/backup_conf.c

View File

@ -23,6 +23,7 @@
#include "virdomainobjlist.h"
#include "virthread.h"
#include "ch_capabilities.h"
#include "virebtables.h"
#define CH_DRIVER_NAME "CH"
#define CH_CMD "cloud-hypervisor"
@ -75,6 +76,9 @@ struct _virCHDriver
/* pid file FD, ensures two copies of the driver can't use the same root */
int lockFD;
/* Immutable pointer, lockless APIs. Pointless abstraction */
ebtablesContext *ebtables;
};
virCaps *virCHDriverCapsInit(void);

View File

@ -22,6 +22,7 @@
#include "ch_domain.h"
#include "domain_driver.h"
#include "domain_validate.h"
#include "virchrdev.h"
#include "virlog.h"
#include "virtime.h"
@ -355,3 +356,43 @@ virCHDomainObjFromDomain(virDomainPtr domain)
return vm;
}
int
virCHDomainValidateActualNetDef(virDomainNetDef *net)
{
virDomainNetType actualType = virDomainNetGetActualType(net);
/* hypervisor-agnostic validation */
if (virDomainActualNetDefValidate(net) < 0)
return -1;
/* CH specific validation */
switch (actualType) {
case VIR_DOMAIN_NET_TYPE_ETHERNET:
if (net->guestIP.nips > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("ethernet type supports a single guest ip"));
return -1;
}
break;
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_DIRECT:
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
default:
break;
}
return 0;
}

View File

@ -75,3 +75,6 @@ virCHDomainGetMachineName(virDomainObj *vm);
virDomainObj *
virCHDomainObjFromDomain(virDomainPtr domain);
int
virCHDomainValidateActualNetDef(virDomainNetDef *net);

99
src/ch/ch_interface.c Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright Microsoft Corp. 2023
*
* ch_interface.c: methods to connect guest interfaces to appropriate host
* backends
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "domain_conf.h"
#include "domain_interface.h"
#include "virebtables.h"
#include "viralloc.h"
#include "ch_interface.h"
#include "virjson.h"
#include "virlog.h"
#define VIR_FROM_THIS VIR_FROM_CH
VIR_LOG_INIT("ch.ch_interface");
/**
* virCHConnetNetworkInterfaces:
* @driver: pointer to ch driver object
* @vm: pointer to domain definition
* @net: pointer to a guest net
* @nicindexes: returned array of FDs of guest interfaces
* @nnicindexes: returned number of guest interfaces
*
*
* Returns 0 on success, -1 on error.
*/
int
virCHConnetNetworkInterfaces(virCHDriver *driver,
virDomainDef *vm,
virDomainNetDef *net,
int *tapfds, int **nicindexes, size_t *nnicindexes)
{
virDomainNetType actualType = virDomainNetGetActualType(net);
switch (actualType) {
case VIR_DOMAIN_NET_TYPE_ETHERNET:
if (virDomainInterfaceEthernetConnect(vm, net,
driver->ebtables, false,
driver->privileged, tapfds,
net->driver.virtio.queues) < 0)
return -1;
G_GNUC_FALLTHROUGH;
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_DIRECT:
if (nicindexes && nnicindexes && net->ifname) {
int nicindex = 0;
if (virNetDevGetIndex(net->ifname, &nicindex) < 0)
return -1;
VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
}
break;
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported Network type %1$d"), actualType);
return -1;
}
return 0;
}

35
src/ch/ch_interface.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright Microsoft Corp. 2023
*
* ch_interface.c: methods to connect guest interfaces to appropriate host
* backends
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ch_conf.h"
#include "virconftypes.h"
int
virCHConnetNetworkInterfaces(virCHDriver *driver,
virDomainDef *vmdef,
virDomainNetDef *netdef,
int *tapfds,
int **nicindexes,
size_t *nnicindexes);

View File

@ -24,7 +24,11 @@
#include <unistd.h>
#include <curl/curl.h>
#include "datatypes.h"
#include "ch_conf.h"
#include "ch_interface.h"
#include "ch_monitor.h"
#include "domain_interface.h"
#include "viralloc.h"
#include "vircommand.h"
#include "virerror.h"
@ -258,151 +262,95 @@ virCHMonitorBuildDisksJson(virJSONValue *content, virDomainDef *vmdef)
return 0;
}
static int
virCHMonitorBuildNetJson(virJSONValue *nets,
virDomainNetDef *netdef,
size_t *nnicindexes,
int **nicindexes)
/**
* virCHMonitorBuildNetJson:
* @net: pointer to a guest network definition
* @jsonstr: returned network json
*
* Build net json to send to CH
* Returns 0 on success or -1 in case of error
*/
int
virCHMonitorBuildNetJson(virDomainNetDef *net, char **jsonstr)
{
virDomainNetType netType = virDomainNetGetActualType(netdef);
char macaddr[VIR_MAC_STRING_BUFLEN];
g_autoptr(virJSONValue) net = NULL;
g_autoptr(virJSONValue) net_json = virJSONValueNewObject();
virDomainNetType actualType = virDomainNetGetActualType(net);
// check net type at first
net = virJSONValueNewObject();
if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET &&
net->guestIP.nips == 1) {
const virNetDevIPAddr *ip;
g_autofree char *addr = NULL;
virSocketAddr netmask;
g_autofree char *netmaskStr = NULL;
switch (netType) {
case VIR_DOMAIN_NET_TYPE_ETHERNET:
if (netdef->guestIP.nips == 1) {
const virNetDevIPAddr *ip = netdef->guestIP.ips[0];
g_autofree char *addr = NULL;
virSocketAddr netmask;
g_autofree char *netmaskStr = NULL;
ip = net->guestIP.ips[0];
if (!(addr = virSocketAddrFormat(&ip->address)))
return -1;
if (virJSONValueObjectAppendString(net, "ip", addr) < 0)
return -1;
if (!(addr = virSocketAddrFormat(&ip->address)))
return -1;
if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to translate net prefix %1$d to netmask"),
ip->prefix);
return -1;
}
if (!(netmaskStr = virSocketAddrFormat(&netmask)))
return -1;
if (virJSONValueObjectAppendString(net, "mask", netmaskStr) < 0)
return -1;
} else if (netdef->guestIP.nips > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("ethernet type supports a single guest ip"));
}
if (virJSONValueObjectAppendString(net_json, "ip", addr) < 0)
return -1;
/* network and bridge use a tap device, and direct uses a
* macvtap device
*/
if (nicindexes && nnicindexes && netdef->ifname) {
int nicindex = 0;
if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to translate net prefix %1$d to netmask"),
ip->prefix);
return -1;
}
if (virNetDevGetIndex(netdef->ifname, &nicindex) < 0)
return -1;
if (!(netmaskStr = virSocketAddrFormat(&netmask)))
return -1;
VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
}
break;
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
if ((virDomainChrType)netdef->data.vhostuser->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vhost_user type support UNIX socket in this CH"));
return -1;
} else {
if (virJSONValueObjectAppendString(net, "vhost_socket", netdef->data.vhostuser->data.nix.path) < 0)
return -1;
if (virJSONValueObjectAppendBoolean(net, "vhost_user", true) < 0)
return -1;
}
break;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_DIRECT:
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
default:
virReportEnumRangeError(virDomainNetType, netType);
if (virJSONValueObjectAppendString(net_json, "mask", netmaskStr) < 0)
return -1;
}
if (netdef->ifname != NULL) {
if (virJSONValueObjectAppendString(net, "tap", netdef->ifname) < 0)
return -1;
}
if (virJSONValueObjectAppendString(net, "mac", virMacAddrFormat(&netdef->mac, macaddr)) < 0)
if (virJSONValueObjectAppendString(net_json, "mac",
virMacAddrFormat(&net->mac, macaddr)) < 0)
return -1;
if (netdef->virtio != NULL) {
if (netdef->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
if (virJSONValueObjectAppendBoolean(net, "iommu", true) < 0)
if (net->virtio != NULL) {
if (net->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
if (virJSONValueObjectAppendBoolean(net_json, "iommu", true) < 0)
return -1;
}
}
if (netdef->driver.virtio.queues) {
if (virJSONValueObjectAppendNumberInt(net, "num_queues", netdef->driver.virtio.queues) < 0)
/* Cloud-Hypervisor expects number of queues. 1 for rx and 1 for tx.
* Multiply queue pairs by 2 to provide total number of queues to CH
*/
if (net->driver.virtio.queues) {
if (virJSONValueObjectAppendNumberInt(net_json, "num_queues",
2 * net->driver.virtio.queues) < 0)
return -1;
}
if (netdef->driver.virtio.rx_queue_size || netdef->driver.virtio.tx_queue_size) {
if (netdef->driver.virtio.rx_queue_size != netdef->driver.virtio.tx_queue_size) {
if (net->driver.virtio.rx_queue_size || net->driver.virtio.tx_queue_size) {
if (net->driver.virtio.rx_queue_size !=
net->driver.virtio.tx_queue_size) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
netdef->driver.virtio.rx_queue_size,
netdef->driver.virtio.tx_queue_size);
_("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
net->driver.virtio.rx_queue_size,
net->driver.virtio.tx_queue_size);
return -1;
}
if (virJSONValueObjectAppendNumberInt(net, "queue_size", netdef->driver.virtio.rx_queue_size) < 0)
if (virJSONValueObjectAppendNumberInt(net_json, "queue_size",
net->driver.virtio.rx_queue_size) < 0)
return -1;
}
if (virJSONValueArrayAppend(nets, &net) < 0)
if (net->mtu) {
if (virJSONValueObjectAppendNumberInt(net_json, "mtu", net->mtu) < 0)
return -1;
}
if (!(*jsonstr = virJSONValueToString(net_json, false)))
return -1;
return 0;
}
static int
virCHMonitorBuildNetsJson(virJSONValue *content,
virDomainDef *vmdef,
size_t *nnicindexes,
int **nicindexes)
{
g_autoptr(virJSONValue) nets = NULL;
size_t i;
if (vmdef->nnets > 0) {
nets = virJSONValueNewArray();
for (i = 0; i < vmdef->nnets; i++) {
if (virCHMonitorBuildNetJson(nets, vmdef->nets[i],
nnicindexes, nicindexes) < 0)
return -1;
}
if (virJSONValueObjectAppend(content, "net", &nets) < 0)
return -1;
}
return 0;
}
static int
virCHMonitorBuildDeviceJson(virJSONValue *devices,
virDomainHostdevDef *hostdevdef)
@ -456,11 +404,8 @@ virCHMonitorBuildDevicesJson(virJSONValue *content,
}
static int
virCHMonitorBuildVMJson(virCHDriver *driver,
virDomainDef *vmdef,
char **jsonstr,
size_t *nnicindexes,
int **nicindexes)
virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
char **jsonstr)
{
g_autoptr(virJSONValue) content = virJSONValueNewObject();
@ -490,10 +435,6 @@ virCHMonitorBuildVMJson(virCHDriver *driver,
if (virCHMonitorBuildDisksJson(content, vmdef) < 0)
return -1;
if (virCHMonitorBuildNetsJson(content, vmdef, nnicindexes, nicindexes) < 0)
return -1;
if (virCHMonitorBuildDevicesJson(content, vmdef) < 0)
return -1;
@ -877,10 +818,7 @@ virCHMonitorShutdownVMM(virCHMonitor *mon)
}
int
virCHMonitorCreateVM(virCHDriver *driver,
virCHMonitor *mon,
size_t *nnicindexes,
int **nicindexes)
virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon)
{
g_autofree char *url = NULL;
int responseCode = 0;
@ -892,8 +830,7 @@ virCHMonitorCreateVM(virCHDriver *driver,
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload,
nnicindexes, nicindexes) != 0)
if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload) != 0)
return -1;
VIR_WITH_OBJECT_LOCK_GUARD(mon) {

View File

@ -104,10 +104,7 @@ void virCHMonitorClose(virCHMonitor *mon);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCHMonitor, virCHMonitorClose);
int virCHMonitorCreateVM(virCHDriver *driver,
virCHMonitor *mon,
size_t *nnicindexes,
int **nicindexes);
int virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon);
int virCHMonitorBootVM(virCHMonitor *mon);
int virCHMonitorShutdownVM(virCHMonitor *mon);
int virCHMonitorRebootVM(virCHMonitor *mon);
@ -123,3 +120,5 @@ size_t virCHMonitorGetThreadInfo(virCHMonitor *mon, bool refresh,
virCHMonitorThreadInfo **threads);
int virCHMonitorGetIOThreads(virCHMonitor *mon,
virDomainIOThreadInfo ***iothreads);
int
virCHMonitorBuildNetJson(virDomainNetDef *netdef, char **jsonstr);

View File

@ -22,14 +22,19 @@
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "ch_domain.h"
#include "ch_monitor.h"
#include "ch_process.h"
#include "domain_cgroup.h"
#include "domain_interface.h"
#include "virerror.h"
#include "virfile.h"
#include "virjson.h"
#include "virlog.h"
#include "virstring.h"
#include "ch_interface.h"
#define VIR_FROM_THIS VIR_FROM_CH
@ -448,13 +453,192 @@ virCHProcessSetupVcpus(virDomainObj *vm)
return 0;
}
#define PKT_TIMEOUT_MS 500 /* ms */
static char *
chSocketRecv(int sock)
{
struct pollfd pfds[1];
char *buf = NULL;
size_t buf_len = 1024;
int ret;
buf = g_new0(char, buf_len);
pfds[0].fd = sock;
pfds[0].events = POLLIN;
do {
ret = poll(pfds, G_N_ELEMENTS(pfds), PKT_TIMEOUT_MS);
} while (ret < 0 && errno == EINTR);
if (ret <= 0) {
if (ret < 0) {
virReportSystemError(errno, _("Poll on sock %1$d failed"), sock);
} else if (ret == 0) {
virReportSystemError(errno, _("Poll on sock %1$d timed out"), sock);
}
return NULL;
}
do {
ret = recv(sock, buf, buf_len - 1, 0);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
virReportSystemError(errno, _("recv on sock %1$d failed"), sock);
return NULL;
}
return g_steal_pointer(&buf);
}
#undef PKT_TIMEOUT_MS
/**
* chProcessAddNetworkDevices:
* @driver: pointer to ch driver object
* @mon: pointer to the monitor object
* @vmdef: pointer to domain definition
* @nicindexes: returned array of FDs of guest interfaces
* @nnicindexes: returned number of network indexes
*
* Send tap fds to CH process via AddNet api. Capture the network indexes of
* guest interfaces in nicindexes.
*
* Returns 0 on success, -1 on error.
*/
static int
chProcessAddNetworkDevices(virCHDriver *driver,
virCHMonitor *mon,
virDomainDef *vmdef,
int **nicindexes,
size_t *nnicindexes)
{
size_t i;
VIR_AUTOCLOSE mon_sockfd = -1;
struct sockaddr_un server_addr;
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) http_headers = VIR_BUFFER_INITIALIZER;
if (!virBitmapIsBitSet(driver->chCaps, CH_MULTIFD_IN_ADDNET)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Guest networking is not supported by this version of ch"));
return -1;
}
mon_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (mon_sockfd < 0) {
virReportSystemError(errno, "%s", _("Failed to open a UNIX socket"));
return -1;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
if (virStrcpyStatic(server_addr.sun_path, mon->socketpath) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("UNIX socket path '%1$s' too long"), mon->socketpath);
return -1;
}
if (connect(mon_sockfd, (struct sockaddr *)&server_addr,
sizeof(server_addr)) == -1) {
virReportSystemError(errno, "%s", _("Failed to connect to mon socket"));
return -1;
}
virBufferAddLit(&http_headers, "PUT /api/v1/vm.add-net HTTP/1.1\r\n");
virBufferAddLit(&http_headers, "Host: localhost\r\n");
virBufferAddLit(&http_headers, "Content-Type: application/json\r\n");
for (i = 0; i < vmdef->nnets; i++) {
g_autofree int *tapfds = NULL;
g_autofree char *payload = NULL;
g_autofree char *response = NULL;
size_t j;
size_t tapfd_len;
int http_res;
int rc;
if (vmdef->nets[i]->driver.virtio.queues == 0) {
/* "queues" here refers to queue pairs. When 0, initialize
* queue pairs to 1.
*/
vmdef->nets[i]->driver.virtio.queues = 1;
}
tapfd_len = vmdef->nets[i]->driver.virtio.queues;
if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("net definition failed validation"));
return -1;
}
tapfds = g_new0(int, tapfd_len);
memset(tapfds, -1, (tapfd_len) * sizeof(int));
/* Connect Guest interfaces */
if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds,
nicindexes, nnicindexes) < 0)
return -1;
if (virCHMonitorBuildNetJson(vmdef->nets[i], &payload) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to build net json"));
return -1;
}
VIR_DEBUG("payload sent with net-add request to CH = %s", payload);
virBufferAsprintf(&buf, "%s", virBufferCurrentContent(&http_headers));
virBufferAsprintf(&buf, "Content-Length: %ld\r\n\r\n", strlen(payload));
virBufferAsprintf(&buf, "%s", payload);
payload = virBufferContentAndReset(&buf);
rc = virSocketSendMsgWithFDs(mon_sockfd, payload, tapfds, tapfd_len);
/* Close sent tap fds in Libvirt, as they have been dup()ed in CH */
for (j = 0; j < tapfd_len; j++) {
VIR_FORCE_CLOSE(tapfds[j]);
}
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to send net-add request to CH"));
return -1;
}
/* Process the response from CH */
response = chSocketRecv(mon_sockfd);
if (response == NULL) {
return -1;
}
/* Parse the HTTP response code */
rc = sscanf(response, "HTTP/1.%*d %d", &http_res);
if (rc != 1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to parse HTTP response code"));
return -1;
}
if (http_res != 204 && http_res != 200) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected response from CH: %1$d"), http_res);
return -1;
}
}
return 0;
}
/**
* virCHProcessStart:
* @driver: pointer to driver structure
* @vm: pointer to virtual machine structure
* @reason: reason for switching vm to running state
*
* Starts Cloud-Hypervisor listen on a local socket
* Starts Cloud-Hypervisor listening on a local socket
*
* Returns 0 on success or -1 in case of error
*/
@ -483,8 +667,7 @@ virCHProcessStart(virCHDriver *driver,
goto cleanup;
}
if (virCHMonitorCreateVM(driver, priv->monitor,
&nnicindexes, &nicindexes) < 0) {
if (virCHMonitorCreateVM(driver, priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to create guest VM"));
goto cleanup;
@ -495,6 +678,13 @@ virCHProcessStart(virCHDriver *driver,
vm->def->id = vm->pid;
priv->machineName = virCHDomainGetMachineName(vm);
if (chProcessAddNetworkDevices(driver, priv->monitor, vm->def,
&nicindexes, &nnicindexes) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed while adding guest interfaces"));
goto cleanup;
}
if (virDomainCgroupSetupCgroup("ch", vm,
nnicindexes, nicindexes,
&priv->cgroup,
@ -507,6 +697,10 @@ virCHProcessStart(virCHDriver *driver,
if (virCHProcessInitCpuAffinity(vm) < 0)
goto cleanup;
/* Bring up netdevs before starting CPUs */
if (virDomainInterfaceStartDevices(vm->def) < 0)
return -1;
if (virCHMonitorBootVM(priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to boot guest VM"));
@ -552,6 +746,9 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
int ret;
int retries = 0;
virCHDomainObjPrivate *priv = vm->privateData;
virCHDriverConfig *cfg = virCHDriverGetConfig(driver);
virDomainDef *def = vm->def;
size_t i;
VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d",
vm->def->name, (int)vm->pid, (int)reason);
@ -560,6 +757,14 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
g_clear_pointer(&priv->monitor, virCHMonitorClose);
}
/* de-activate netdevs after stopping vm */
ignore_value(virDomainInterfaceStopDevices(vm->def));
for (i = 0; i < def->nnets; i++) {
virDomainNetDef *net = def->nets[i];
virDomainInterfaceDeleteDevice(def, net, false, cfg->stateDir);
}
retry:
if ((ret = virDomainCgroupRemoveCgroup(vm,
priv->cgroup,

View File

@ -7,6 +7,8 @@ ch_driver_sources = [
'ch_domain.h',
'ch_driver.c',
'ch_driver.h',
'ch_interface.c',
'ch_interface.h',
'ch_monitor.c',
'ch_monitor.h',
'ch_process.c',