Signed-off-by: Eric Farman <farman@xxxxxxxxxxxxxxxxxx> --- src/Makefile.am | 1 + src/libvirt_private.syms | 19 +++ src/util/virhost.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhost.h | 72 ++++++++++++ src/util/virhostdev.c | 155 ++++++++++++++++++++++++ src/util/virhostdev.h | 16 +++ tests/qemuxml2argvmock.c | 9 ++ 7 files changed, 573 insertions(+) create mode 100644 src/util/virhost.c create mode 100644 src/util/virhost.h diff --git a/src/Makefile.am b/src/Makefile.am index 8ee5567..11b7dc4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -122,6 +122,7 @@ UTIL_SOURCES = \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ util/virhook.c util/virhook.h \ + util/virhost.c util/virhost.h \ util/virhostcpu.c util/virhostcpu.h util/virhostcpupriv.h \ util/virhostdev.c util/virhostdev.h \ util/virhostmem.c util/virhostmem.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b4210f4..c152e76 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1671,6 +1671,23 @@ virHookInitialize; virHookPresent; +# util/virhost.h +virHostDeviceFileIterate; +virHostDeviceFree; +virHostDeviceGetName; +virHostDeviceListAdd; +virHostDeviceListCount; +virHostDeviceListDel; +virHostDeviceListFind; +virHostDeviceListFindIndex; +virHostDeviceListGet; +virHostDeviceListNew; +virHostDeviceListSteal; +virHostDeviceNew; +virHostDeviceSetUsedBy; +virHostOpenVhostSCSI; + + # util/virhostdev.h virHostdevFindUSBDevice; virHostdevManagerGetDefault; @@ -1678,10 +1695,12 @@ virHostdevPCINodeDeviceDetach; virHostdevPCINodeDeviceReAttach; virHostdevPCINodeDeviceReset; virHostdevPrepareDomainDevices; +virHostdevPrepareHostDevices; virHostdevPreparePCIDevices; virHostdevPrepareSCSIDevices; virHostdevPrepareUSBDevices; virHostdevReAttachDomainDevices; +virHostdevReAttachHostDevices; virHostdevReAttachPCIDevices; virHostdevReAttachSCSIDevices; virHostdevReAttachUSBDevices; diff --git a/src/util/virhost.c b/src/util/virhost.c new file mode 100644 index 0000000..dd0d982 --- /dev/null +++ b/src/util/virhost.c @@ -0,0 +1,301 @@ +/* + * virscsi.c: helper APIs for managing scsi_host devices + * + * Copyright (C) 2016 IBM Corporation + * + * 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/>. + * + * Authors: + * Eric Farman <farman@xxxxxxxxxxxxxxxxxx> + */ + +#include <config.h> +#include <fcntl.h> + +#include "virhost.h" +#include "virlog.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virstring.h" + +VIR_LOG_INIT("util.host"); + +#define SYSFS_VHOST_SCSI_DEVICES "/sys/kernel/config/target/vhost/" +#define VHOST_SCSI_DEVICE "/dev/vhost-scsi" + +struct _virUsedByInfo { + char *drvname; /* which driver */ + char *domname; /* which domain */ +}; +typedef struct _virUsedByInfo *virUsedByInfoPtr; + +struct _virHostDevice { + char *name; /* naa.<wwn> */ + char *path; + virUsedByInfoPtr *used_by; /* driver:domain(s) using this dev */ + size_t n_used_by; /* how many domains are using this dev */ +}; + +struct _virHostDeviceList { + virObjectLockable parent; + size_t count; + virHostDevicePtr *devs; +}; + +static virClassPtr virHostDeviceListClass; + +static void +virHostDeviceListDispose(void *obj) +{ + virHostDeviceListPtr list = obj; + size_t i; + + for (i = 0; i < list->count; i++) + virHostDeviceFree(list->devs[i]); + + VIR_FREE(list->devs); +} + + +static int +virHostOnceInit(void) +{ + if (!(virHostDeviceListClass = virClassNew(virClassForObjectLockable(), + "virHostDeviceList", + sizeof(virHostDeviceList), + virHostDeviceListDispose))) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virHost) + +/* For virReportOOMError() and virReportSystemError() */ +#define VIR_FROM_THIS VIR_FROM_NONE + +int +virHostOpenVhostSCSI(int *vhostfd) +{ + if (!virFileExists(VHOST_SCSI_DEVICE)) + goto error; + + *vhostfd = open(VHOST_SCSI_DEVICE, O_RDWR); + + if (*vhostfd < 0) { + virReportSystemError(errno, _("Failed to open %s"), VHOST_SCSI_DEVICE); + goto error; + } + + return 0; + + error: + VIR_FORCE_CLOSE(*vhostfd); + + return -1; +} + +static void +virHostDeviceUsedByInfoFree(virUsedByInfoPtr used_by) +{ + VIR_FREE(used_by->drvname); + VIR_FREE(used_by->domname); + VIR_FREE(used_by); +} + +void +virHostDeviceListDel(virHostDeviceListPtr list, + virHostDevicePtr dev, + const char *drvname, + const char *domname) +{ + virHostDevicePtr tmp = NULL; + size_t i; + + for (i = 0; i < dev->n_used_by; i++) { + if (STREQ_NULLABLE(dev->used_by[i]->drvname, drvname) && + STREQ_NULLABLE(dev->used_by[i]->domname, domname)) { + if (dev->n_used_by > 1) { + virHostDeviceUsedByInfoFree(dev->used_by[i]); + VIR_DELETE_ELEMENT(dev->used_by, i, dev->n_used_by); + } else { + tmp = virHostDeviceListSteal(list, dev); + virHostDeviceFree(tmp); + } + break; + } + } +} + +int +virHostDeviceListFindIndex(virHostDeviceListPtr list, virHostDevicePtr dev) +{ + size_t i; + + for (i = 0; i < list->count; i++) { + virHostDevicePtr other = list->devs[i]; + if (STREQ_NULLABLE(other->name, dev->name)) + return i; + } + return -1; +} + +virHostDevicePtr +virHostDeviceListGet(virHostDeviceListPtr list, int idx) +{ + if (idx >= list->count || idx < 0) + return NULL; + + return list->devs[idx]; +} + +size_t +virHostDeviceListCount(virHostDeviceListPtr list) +{ + return list->count; +} + +virHostDevicePtr +virHostDeviceListSteal(virHostDeviceListPtr list, + virHostDevicePtr dev) +{ + virHostDevicePtr ret = NULL; + size_t i; + + for (i = 0; i < list->count; i++) { + if (STREQ_NULLABLE(list->devs[i]->name, dev->name)) { + ret = list->devs[i]; + VIR_DELETE_ELEMENT(list->devs, i, list->count); + break; + } + } + + return ret; +} + +virHostDevicePtr +virHostDeviceListFind(virHostDeviceListPtr list, virHostDevicePtr dev) +{ + int idx; + + if ((idx = virHostDeviceListFindIndex(list, dev)) >= 0) + return list->devs[idx]; + else + return NULL; +} + +int +virHostDeviceListAdd(virHostDeviceListPtr list, + virHostDevicePtr dev) +{ + if (virHostDeviceListFind(list, dev)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Device %s is already in use"), dev->name); + return -1; + } + return VIR_APPEND_ELEMENT(list->devs, list->count, dev); +} + +virHostDeviceListPtr +virHostDeviceListNew(void) +{ + virHostDeviceListPtr list; + + if (virHostInitialize() < 0) + return NULL; + + if (!(list = virObjectLockableNew(virHostDeviceListClass))) + return NULL; + + return list; +} + +int +virHostDeviceSetUsedBy(virHostDevicePtr dev, + const char *drvname, + const char *domname) +{ + virUsedByInfoPtr copy; + if (VIR_ALLOC(copy) < 0) + return -1; + if (VIR_STRDUP(copy->drvname, drvname) < 0 || + VIR_STRDUP(copy->domname, domname) < 0) + goto cleanup; + + if (VIR_APPEND_ELEMENT(dev->used_by, dev->n_used_by, copy) < 0) + goto cleanup; + + return 0; + + cleanup: + virHostDeviceUsedByInfoFree(copy); + return -1; +} + +int +virHostDeviceFileIterate(virHostDevicePtr dev, + virHostDeviceFileActor actor, + void *opaque) +{ + return (actor)(dev, dev->path, opaque); +} + +const char * +virHostDeviceGetName(virHostDevicePtr dev) +{ + return dev->name; +} + +virHostDevicePtr +virHostDeviceNew(const char *name) +{ + virHostDevicePtr dev; + + if (VIR_ALLOC(dev) < 0) + return NULL; + + if (VIR_STRDUP(dev->name, name) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("dev->name buffer overflow: %s"), + name); + goto error; + } + + if (virAsprintf(&dev->path, "%s/%s", + SYSFS_VHOST_SCSI_DEVICES, name) < 0) + goto cleanup; + + VIR_DEBUG("%s: initialized", dev->name); + + cleanup: + return dev; + + error: + virHostDeviceFree(dev); + dev = NULL; + goto cleanup; +} + +void +virHostDeviceFree(virHostDevicePtr dev) +{ + if (!dev) + return; + VIR_DEBUG("%s: freeing", dev->name); + VIR_FREE(dev); +} + + diff --git a/src/util/virhost.h b/src/util/virhost.h new file mode 100644 index 0000000..6d7b790 --- /dev/null +++ b/src/util/virhost.h @@ -0,0 +1,72 @@ +/* + * virhost.h: helper APIs for managing host scsi_host devices + * + * Copyright (C) 2016 IBM Corporation + * + * 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/>. + * + * Authors: + * Eric Farman <farman@xxxxxxxxxxxxxxxxxx> + */ + +#ifndef __VIR_HOST_H__ +# define __VIR_HOST_H__ + +# include "internal.h" +# include "virobject.h" +# include "virutil.h" + +typedef struct _virHostDevice virHostDevice; +typedef virHostDevice *virHostDevicePtr; +typedef struct _virHostDeviceAddress virHostDeviceAddress; +typedef virHostDeviceAddress *virHostDeviceAddressPtr; +typedef struct _virHostDeviceList virHostDeviceList; +typedef virHostDeviceList *virHostDeviceListPtr; + +struct _virHostDeviceAddress { + char *wwpn; +}; + +typedef int (*virHostDeviceFileActor)(virHostDevicePtr dev, + const char *name, void *opaque); + +int virHostDeviceFileIterate(virHostDevicePtr dev, + virHostDeviceFileActor actor, + void *opaque); +const char *virHostDeviceGetName(virHostDevicePtr dev); +virHostDevicePtr virHostDeviceListGet(virHostDeviceListPtr list, + int idx); +size_t virHostDeviceListCount(virHostDeviceListPtr list); +virHostDevicePtr virHostDeviceListSteal(virHostDeviceListPtr list, + virHostDevicePtr dev); +int virHostDeviceListFindIndex(virHostDeviceListPtr list, + virHostDevicePtr dev); +virHostDevicePtr virHostDeviceListFind(virHostDeviceListPtr list, + virHostDevicePtr dev); +int virHostDeviceListAdd(virHostDeviceListPtr list, + virHostDevicePtr dev); +void virHostDeviceListDel(virHostDeviceListPtr list, + virHostDevicePtr dev, + const char *drvname, + const char *domname); +virHostDeviceListPtr virHostDeviceListNew(void); +virHostDevicePtr virHostDeviceNew(const char *name); +int virHostDeviceSetUsedBy(virHostDevicePtr dev, + const char *drvname, + const char *domname); +void virHostDeviceFree(virHostDevicePtr dev); +int virHostOpenVhostSCSI(int *vhostfd); + +#endif /* __VIR_HOST_H__ */ diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 9c2262e..b92e246 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -146,6 +146,7 @@ virHostdevManagerDispose(void *obj) virObjectUnref(hostdevMgr->inactivePCIHostdevs); virObjectUnref(hostdevMgr->activeUSBHostdevs); virObjectUnref(hostdevMgr->activeSCSIHostdevs); + virObjectUnref(hostdevMgr->activeHostHostdevs); VIR_FREE(hostdevMgr->stateDir); } @@ -170,6 +171,9 @@ virHostdevManagerNew(void) if (!(hostdevMgr->activeSCSIHostdevs = virSCSIDeviceListNew())) goto error; + if (!(hostdevMgr->activeHostHostdevs = virHostDeviceListNew())) + goto error; + if (privileged) { if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0) goto error; @@ -1472,6 +1476,102 @@ virHostdevPrepareSCSIDevices(virHostdevManagerPtr mgr, return -1; } +int +virHostdevPrepareHostDevices(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i, j; + int count; + virHostDeviceListPtr list; + virHostDevicePtr tmp; + + if (!nhostdevs) + return 0; + + /* To prevent situation where scsi_host device is assigned to two domains + * we need to keep a list of currently assigned scsi_host devices. + * This is done in several loops which cannot be joined into one big + * loop. See virHostdevPreparePCIDevices() + */ + if (!(list = virHostDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virDomainHostdevSubsysHostPtr hostsrc = &hostdev->source.subsys.u.host; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_HOST) + continue; + + if (hostsrc->protocol != VIR_DOMAIN_HOSTDEV_SUBSYS_HOST_PROTOCOL_TYPE_VHOST) { + continue; /* Not supported */ + } else { + virHostDevicePtr host; + if (!(host = virHostDeviceNew(hostsrc->wwpn))) + goto cleanup; + + if (virHostDeviceListAdd(list, host) < 0) { + virHostDeviceFree(host); + goto cleanup; + } + } + } + + /* Loop 2: Mark devices in temporary list as used by @name + * and add them to driver list. However, if something goes + * wrong, perform rollback. + */ + virObjectLock(mgr->activeHostHostdevs); + count = virHostDeviceListCount(list); + + for (i = 0; i < count; i++) { + virHostDevicePtr host = virHostDeviceListGet(list, i); + if ((tmp = virHostDeviceListFind(mgr->activeHostHostdevs, host))) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI_host device %s is already in use by " + "another domain"), + virHostDeviceGetName(tmp)); + goto error; + } else { + if (virHostDeviceSetUsedBy(host, drv_name, dom_name) < 0) + goto error; + + VIR_DEBUG("Adding %s to activeHostHostdevs", virHostDeviceGetName(host)); + + if (virHostDeviceListAdd(mgr->activeHostHostdevs, host) < 0) + goto error; + } + } + + virObjectUnlock(mgr->activeHostHostdevs); + + /* Loop 3: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * when freeing temporary list. + */ + while (virHostDeviceListCount(list) > 0) { + tmp = virHostDeviceListGet(list, 0); + virHostDeviceListSteal(list, tmp); + } + + virObjectUnref(list); + return 0; + error: + for (j = 0; j < i; j++) { + tmp = virHostDeviceListGet(list, i); + virHostDeviceListSteal(mgr->activeHostHostdevs, tmp); + } + virObjectUnlock(mgr->activeHostHostdevs); + cleanup: + virObjectUnref(list); + return -1; +} + void virHostdevReAttachUSBDevices(virHostdevManagerPtr mgr, const char *drv_name, @@ -1604,6 +1704,61 @@ virHostdevReAttachSCSIDevices(virHostdevManagerPtr mgr, virObjectUnlock(mgr->activeSCSIHostdevs); } +void +virHostdevReAttachHostDevices(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + virHostDevicePtr host, tmp; + + + if (!nhostdevs) + return; + + virObjectLock(mgr->activeHostHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virDomainHostdevSubsysHostPtr hostsrc = &hostdev->source.subsys.u.host; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_HOST) + continue; + + if (hostsrc->protocol != VIR_DOMAIN_HOSTDEV_SUBSYS_HOST_PROTOCOL_TYPE_VHOST) + continue; /* Not supported */ + + if (!(host = virHostDeviceNew(hostsrc->wwpn))) { + VIR_WARN("Unable to reattach SCSI_host device %s on domain %s", + hostsrc->wwpn, dom_name); + virObjectUnlock(mgr->activeHostHostdevs); + return; + } + + /* Only delete the devices which are marked as being used by @name, + * because qemuProcessStart could fail half way through. */ + + if (!(tmp = virHostDeviceListFind(mgr->activeHostHostdevs, host))) { + VIR_WARN("Unable to find device %s " + "in list of active SCSI_host devices", + hostsrc->wwpn); + virHostDeviceFree(host); + virObjectUnlock(mgr->activeHostHostdevs); + return; + } + + VIR_DEBUG("Removing %s dom=%s from activeHostHostdevs", + hostsrc->wwpn, dom_name); + + virHostDeviceListDel(mgr->activeHostHostdevs, tmp, + drv_name, dom_name); + virHostDeviceFree(host); + } + virObjectUnlock(mgr->activeHostHostdevs); +} + int virHostdevPCINodeDeviceDetach(virHostdevManagerPtr mgr, virPCIDevicePtr pci) diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h index f2f51bd..19cef7e 100644 --- a/src/util/virhostdev.h +++ b/src/util/virhostdev.h @@ -30,6 +30,7 @@ # include "virpci.h" # include "virusb.h" # include "virscsi.h" +# include "virhost.h" # include "domain_conf.h" typedef enum { @@ -53,6 +54,7 @@ struct _virHostdevManager { virPCIDeviceListPtr inactivePCIHostdevs; virUSBDeviceListPtr activeUSBHostdevs; virSCSIDeviceListPtr activeSCSIHostdevs; + virHostDeviceListPtr activeHostHostdevs; }; virHostdevManagerPtr virHostdevManagerGetDefault(void); @@ -87,6 +89,13 @@ virHostdevPrepareSCSIDevices(virHostdevManagerPtr hostdev_mgr, virDomainHostdevDefPtr *hostdevs, int nhostdevs) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +int +virHostdevPrepareHostDevices(virHostdevManagerPtr hostdev_mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); void virHostdevReAttachPCIDevices(virHostdevManagerPtr hostdev_mgr, const char *drv_name, @@ -109,6 +118,13 @@ virHostdevReAttachSCSIDevices(virHostdevManagerPtr hostdev_mgr, virDomainHostdevDefPtr *hostdevs, int nhostdevs) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +void +virHostdevReAttachHostDevices(virHostdevManagerPtr hostdev_mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int virHostdevUpdateActivePCIDevices(virHostdevManagerPtr mgr, virDomainHostdevDefPtr *hostdevs, diff --git a/tests/qemuxml2argvmock.c b/tests/qemuxml2argvmock.c index 78a224b..2c85140 100644 --- a/tests/qemuxml2argvmock.c +++ b/tests/qemuxml2argvmock.c @@ -24,6 +24,7 @@ #include "viralloc.h" #include "vircommand.h" #include "vircrypto.h" +#include "virhost.h" #include "virmock.h" #include "virnetdev.h" #include "virnetdevip.h" @@ -107,6 +108,14 @@ virSCSIDeviceGetSgName(const char *sysfs_prefix ATTRIBUTE_UNUSED, } int +virHostOpenVhostSCSI(int *vhostfd) +{ + *vhostfd = STDERR_FILENO + 1; + + return 0; +} + +int virNetDevTapCreate(char **ifname, const char *tunpath ATTRIBUTE_UNUSED, int *tapfd, -- 1.9.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list