util: Management routines for scsi_host devices

For a new hostdev type='scsi_host' we have a number of
required functions for managing, adding, and removing the
host device to/from guests.  Provide the basic infrastructure
for these tasks.

The name "SCSIVHost" (and its variants) is chosen to avoid
conflicts with existing code named "SCSIHost" to refer to
a hostdev type='scsi' protcol='none'.

Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
This commit is contained in:
Eric Farman 2016-11-21 22:58:17 -05:00 committed by John Ferlan
parent fc0e627bac
commit 629544be0f
8 changed files with 561 additions and 0 deletions

View File

@ -237,6 +237,7 @@ src/util/virqemu.c
src/util/virrandom.c
src/util/virrotatingfile.c
src/util/virscsi.c
src/util/virscsivhost.c
src/util/virsecret.c
src/util/virsexpr.c
src/util/virsocketaddr.c

View File

@ -163,6 +163,7 @@ UTIL_SOURCES = \
util/virrandom.h util/virrandom.c \
util/virrotatingfile.h util/virrotatingfile.c \
util/virscsi.c util/virscsi.h \
util/virscsivhost.c util/virscsivhost.h \
util/virseclabel.c util/virseclabel.h \
util/virsecret.c util/virsecret.h \
util/virsexpr.c util/virsexpr.h \

View File

@ -1685,10 +1685,12 @@ virHostdevPCINodeDeviceReset;
virHostdevPrepareDomainDevices;
virHostdevPreparePCIDevices;
virHostdevPrepareSCSIDevices;
virHostdevPrepareSCSIVHostDevices;
virHostdevPrepareUSBDevices;
virHostdevReAttachDomainDevices;
virHostdevReAttachPCIDevices;
virHostdevReAttachSCSIDevices;
virHostdevReAttachSCSIVHostDevices;
virHostdevReAttachUSBDevices;
virHostdevUpdateActiveDomainDevices;
virHostdevUpdateActivePCIDevices;
@ -2306,6 +2308,22 @@ virSCSIDeviceNew;
virSCSIDeviceSetUsedBy;
# util/virscsivhost.h
virSCSIVHostDeviceFileIterate;
virSCSIVHostDeviceFree;
virSCSIVHostDeviceGetName;
virSCSIVHostDeviceListAdd;
virSCSIVHostDeviceListCount;
virSCSIVHostDeviceListDel;
virSCSIVHostDeviceListFind;
virSCSIVHostDeviceListGet;
virSCSIVHostDeviceListNew;
virSCSIVHostDeviceListSteal;
virSCSIVHostDeviceNew;
virSCSIVHostDeviceSetUsedBy;
virSCSIVHostOpenVhostSCSI;
# util/virseclabel.h
virSecurityDeviceLabelDefFree;
virSecurityDeviceLabelDefNew;

View File

@ -146,6 +146,7 @@ virHostdevManagerDispose(void *obj)
virObjectUnref(hostdevMgr->inactivePCIHostdevs);
virObjectUnref(hostdevMgr->activeUSBHostdevs);
virObjectUnref(hostdevMgr->activeSCSIHostdevs);
virObjectUnref(hostdevMgr->activeSCSIVHostHostdevs);
VIR_FREE(hostdevMgr->stateDir);
}
@ -170,6 +171,9 @@ virHostdevManagerNew(void)
if (!(hostdevMgr->activeSCSIHostdevs = virSCSIDeviceListNew()))
goto error;
if (!(hostdevMgr->activeSCSIVHostHostdevs = virSCSIVHostDeviceListNew()))
goto error;
if (privileged) {
if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0)
goto error;
@ -1484,6 +1488,102 @@ virHostdevPrepareSCSIDevices(virHostdevManagerPtr mgr,
return -1;
}
int
virHostdevPrepareSCSIVHostDevices(virHostdevManagerPtr mgr,
const char *drv_name,
const char *dom_name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
size_t i, j;
int count;
virSCSIVHostDeviceListPtr list;
virSCSIVHostDevicePtr host, 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 = virSCSIVHostDeviceListNew()))
goto cleanup;
/* Loop 1: build temporary list */
for (i = 0; i < nhostdevs; i++) {
virDomainHostdevDefPtr hostdev = hostdevs[i];
virDomainHostdevSubsysSCSIVHostPtr hostsrc = &hostdev->source.subsys.u.scsi_host;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST)
continue;
if (hostsrc->protocol != VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_VHOST)
continue; /* Not supported */
if (!(host = virSCSIVHostDeviceNew(hostsrc->wwpn)))
goto cleanup;
if (virSCSIVHostDeviceListAdd(list, host) < 0) {
virSCSIVHostDeviceFree(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->activeSCSIVHostHostdevs);
count = virSCSIVHostDeviceListCount(list);
for (i = 0; i < count; i++) {
host = virSCSIVHostDeviceListGet(list, i);
if ((tmp = virSCSIVHostDeviceListFind(mgr->activeSCSIVHostHostdevs,
host))) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("SCSI_host device %s is already in use by "
"another domain"),
virSCSIVHostDeviceGetName(tmp));
goto error;
} else {
if (virSCSIVHostDeviceSetUsedBy(host, drv_name, dom_name) < 0)
goto error;
VIR_DEBUG("Adding %s to activeSCSIVHostHostdevs",
virSCSIVHostDeviceGetName(host));
if (virSCSIVHostDeviceListAdd(mgr->activeSCSIVHostHostdevs, host) < 0)
goto error;
}
}
virObjectUnlock(mgr->activeSCSIVHostHostdevs);
/* Loop 3: Temporary list was successfully merged with
* driver list, so steal all items to avoid freeing them
* when freeing temporary list.
*/
while (virSCSIVHostDeviceListCount(list) > 0) {
tmp = virSCSIVHostDeviceListGet(list, 0);
virSCSIVHostDeviceListSteal(list, tmp);
}
virObjectUnref(list);
return 0;
error:
for (j = 0; j < i; j++) {
tmp = virSCSIVHostDeviceListGet(list, i);
virSCSIVHostDeviceListSteal(mgr->activeSCSIVHostHostdevs, tmp);
}
virObjectUnlock(mgr->activeSCSIVHostHostdevs);
cleanup:
virObjectUnref(list);
return -1;
}
void
virHostdevReAttachUSBDevices(virHostdevManagerPtr mgr,
const char *drv_name,
@ -1615,6 +1715,69 @@ virHostdevReAttachSCSIDevices(virHostdevManagerPtr mgr,
virObjectUnlock(mgr->activeSCSIHostdevs);
}
void
virHostdevReAttachSCSIVHostDevices(virHostdevManagerPtr mgr,
const char *drv_name,
const char *dom_name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
size_t i;
virSCSIVHostDevicePtr host, tmp;
if (!nhostdevs)
return;
virObjectLock(mgr->activeSCSIVHostHostdevs);
for (i = 0; i < nhostdevs; i++) {
virDomainHostdevDefPtr hostdev = hostdevs[i];
virDomainHostdevSubsysSCSIVHostPtr hostsrc = &hostdev->source.subsys.u.scsi_host;
const char *usedby_drvname;
const char *usedby_domname;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST)
continue;
if (hostsrc->protocol != VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_VHOST)
continue; /* Not supported */
if (!(host = virSCSIVHostDeviceNew(hostsrc->wwpn))) {
VIR_WARN("Unable to reattach SCSI_host device %s on domain %s",
hostsrc->wwpn, dom_name);
virObjectUnlock(mgr->activeSCSIVHostHostdevs);
return;
}
/* Only delete the devices which are marked as being used by @name,
* because qemuProcessStart could fail half way through. */
if (!(tmp = virSCSIVHostDeviceListFind(mgr->activeSCSIVHostHostdevs,
host))) {
VIR_WARN("Unable to find device %s "
"in list of active SCSI_host devices",
hostsrc->wwpn);
virSCSIVHostDeviceFree(host);
virObjectUnlock(mgr->activeSCSIVHostHostdevs);
return;
}
virSCSIVHostDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
if (STREQ_NULLABLE(drv_name, usedby_drvname) &&
STREQ_NULLABLE(dom_name, usedby_domname)) {
VIR_DEBUG("Removing %s dom=%s from activeSCSIVHostHostdevs",
hostsrc->wwpn, dom_name);
virSCSIVHostDeviceListDel(mgr->activeSCSIVHostHostdevs, tmp);
}
virSCSIVHostDeviceFree(host);
}
virObjectUnlock(mgr->activeSCSIVHostHostdevs);
}
int
virHostdevPCINodeDeviceDetach(virHostdevManagerPtr mgr,
virPCIDevicePtr pci)

View File

@ -30,6 +30,7 @@
# include "virpci.h"
# include "virusb.h"
# include "virscsi.h"
# include "virscsivhost.h"
# include "domain_conf.h"
typedef enum {
@ -53,6 +54,7 @@ struct _virHostdevManager {
virPCIDeviceListPtr inactivePCIHostdevs;
virUSBDeviceListPtr activeUSBHostdevs;
virSCSIDeviceListPtr activeSCSIHostdevs;
virSCSIVHostDeviceListPtr activeSCSIVHostHostdevs;
};
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
virHostdevPrepareSCSIVHostDevices(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
virHostdevReAttachSCSIVHostDevices(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,

288
src/util/virscsivhost.c Normal file
View File

@ -0,0 +1,288 @@
/*
* virscsivhost.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@linux.vnet.ibm.com>
*/
#include <config.h>
#include <fcntl.h>
#include "virscsivhost.h"
#include "virlog.h"
#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virstring.h"
/* For virReportOOMError() and virReportSystemError() */
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("util.scsihost");
#define SYSFS_VHOST_SCSI_DEVICES "/sys/kernel/config/target/vhost/"
#define VHOST_SCSI_DEVICE "/dev/vhost-scsi"
struct _virSCSIVHostDevice {
char *name; /* naa.<wwn> */
char *path;
char *used_by_drvname;
char *used_by_domname;
};
struct _virSCSIVHostDeviceList {
virObjectLockable parent;
size_t count;
virSCSIVHostDevicePtr *devs;
};
static virClassPtr virSCSIVHostDeviceListClass;
static void
virSCSIVHostDeviceListDispose(void *obj)
{
virSCSIVHostDeviceListPtr list = obj;
size_t i;
for (i = 0; i < list->count; i++)
virSCSIVHostDeviceFree(list->devs[i]);
VIR_FREE(list->devs);
}
static int
virSCSIVHostOnceInit(void)
{
if (!(virSCSIVHostDeviceListClass = virClassNew(virClassForObjectLockable(),
"virSCSIVHostDeviceList",
sizeof(virSCSIVHostDeviceList),
virSCSIVHostDeviceListDispose)))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virSCSIVHost)
int
virSCSIVHostOpenVhostSCSI(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;
}
void
virSCSIVHostDeviceListDel(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev)
{
virSCSIVHostDevicePtr tmp = virSCSIVHostDeviceListSteal(list, dev);
virSCSIVHostDeviceFree(tmp);
}
static int
virSCSIVHostDeviceListFindIndex(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev)
{
size_t i;
for (i = 0; i < list->count; i++) {
virSCSIVHostDevicePtr other = list->devs[i];
if (STREQ_NULLABLE(other->name, dev->name))
return i;
}
return -1;
}
virSCSIVHostDevicePtr
virSCSIVHostDeviceListGet(virSCSIVHostDeviceListPtr list, int idx)
{
if (idx >= list->count || idx < 0)
return NULL;
return list->devs[idx];
}
size_t
virSCSIVHostDeviceListCount(virSCSIVHostDeviceListPtr list)
{
return list->count;
}
virSCSIVHostDevicePtr
virSCSIVHostDeviceListSteal(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev)
{
virSCSIVHostDevicePtr 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;
}
virSCSIVHostDevicePtr
virSCSIVHostDeviceListFind(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev)
{
int idx;
if ((idx = virSCSIVHostDeviceListFindIndex(list, dev)) >= 0)
return list->devs[idx];
else
return NULL;
}
int
virSCSIVHostDeviceListAdd(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev)
{
if (virSCSIVHostDeviceListFind(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);
}
virSCSIVHostDeviceListPtr
virSCSIVHostDeviceListNew(void)
{
if (virSCSIVHostInitialize() < 0)
return NULL;
return virObjectLockableNew(virSCSIVHostDeviceListClass);
}
int
virSCSIVHostDeviceSetUsedBy(virSCSIVHostDevicePtr dev,
const char *drvname,
const char *domname)
{
VIR_FREE(dev->used_by_drvname);
VIR_FREE(dev->used_by_domname);
if (VIR_STRDUP(dev->used_by_drvname, drvname) < 0)
return -1;
if (VIR_STRDUP(dev->used_by_domname, domname) < 0)
return -1;
return 0;
}
void
virSCSIVHostDeviceGetUsedBy(virSCSIVHostDevicePtr dev,
const char **drv_name,
const char **dom_name)
{
*drv_name = dev->used_by_drvname;
*dom_name = dev->used_by_domname;
}
int
virSCSIVHostDeviceFileIterate(virSCSIVHostDevicePtr dev,
virSCSIVHostDeviceFileActor actor,
void *opaque)
{
return (actor)(dev, dev->path, opaque);
}
const char *
virSCSIVHostDeviceGetName(virSCSIVHostDevicePtr dev)
{
return dev->name;
}
virSCSIVHostDevicePtr
virSCSIVHostDeviceNew(const char *name)
{
virSCSIVHostDevicePtr 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 error;
VIR_DEBUG("%s: initialized", dev->name);
cleanup:
return dev;
error:
virSCSIVHostDeviceFree(dev);
dev = NULL;
goto cleanup;
}
void
virSCSIVHostDeviceFree(virSCSIVHostDevicePtr dev)
{
if (!dev)
return;
VIR_DEBUG("%s: freeing", dev->name);
VIR_FREE(dev->name);
VIR_FREE(dev->path);
VIR_FREE(dev->used_by_drvname);
VIR_FREE(dev->used_by_domname);
VIR_FREE(dev);
}

65
src/util/virscsivhost.h Normal file
View File

@ -0,0 +1,65 @@
/*
* virscsivhost.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@linux.vnet.ibm.com>
*/
#ifndef __VIR_SCSIHOST_H__
# define __VIR_SCSIHOST_H__
# include "internal.h"
# include "virobject.h"
# include "virutil.h"
typedef struct _virSCSIVHostDevice virSCSIVHostDevice;
typedef virSCSIVHostDevice *virSCSIVHostDevicePtr;
typedef struct _virSCSIVHostDeviceList virSCSIVHostDeviceList;
typedef virSCSIVHostDeviceList *virSCSIVHostDeviceListPtr;
typedef int (*virSCSIVHostDeviceFileActor)(virSCSIVHostDevicePtr dev,
const char *name, void *opaque);
int virSCSIVHostDeviceFileIterate(virSCSIVHostDevicePtr dev,
virSCSIVHostDeviceFileActor actor,
void *opaque);
const char *virSCSIVHostDeviceGetName(virSCSIVHostDevicePtr dev);
virSCSIVHostDevicePtr virSCSIVHostDeviceListGet(virSCSIVHostDeviceListPtr list,
int idx);
size_t virSCSIVHostDeviceListCount(virSCSIVHostDeviceListPtr list);
virSCSIVHostDevicePtr virSCSIVHostDeviceListSteal(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev);
virSCSIVHostDevicePtr virSCSIVHostDeviceListFind(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev);
int virSCSIVHostDeviceListAdd(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev);
void virSCSIVHostDeviceListDel(virSCSIVHostDeviceListPtr list,
virSCSIVHostDevicePtr dev);
virSCSIVHostDeviceListPtr virSCSIVHostDeviceListNew(void);
virSCSIVHostDevicePtr virSCSIVHostDeviceNew(const char *name);
int virSCSIVHostDeviceSetUsedBy(virSCSIVHostDevicePtr dev,
const char *drvname,
const char *domname);
void virSCSIVHostDeviceGetUsedBy(virSCSIVHostDevicePtr dev,
const char **drv_name,
const char **dom_name);
void virSCSIVHostDeviceFree(virSCSIVHostDevicePtr dev);
int virSCSIVHostOpenVhostSCSI(int *vhostfd);
#endif /* __VIR_SCSIHOST_H__ */

View File

@ -31,6 +31,7 @@
#include "virnuma.h"
#include "virrandom.h"
#include "virscsi.h"
#include "virscsivhost.h"
#include "virstring.h"
#include "virtpm.h"
#include "virutil.h"
@ -106,6 +107,14 @@ virSCSIDeviceGetSgName(const char *sysfs_prefix ATTRIBUTE_UNUSED,
return ret;
}
int
virSCSIVHostOpenVhostSCSI(int *vhostfd)
{
*vhostfd = STDERR_FILENO + 1;
return 0;
}
int
virNetDevTapCreate(char **ifname,
const char *tunpath ATTRIBUTE_UNUSED,