mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-01 10:51:12 +00:00
9691440ecb
With recent additions to the node device xml schema, an xml schema can now describe a mdev device sufficiently for libvirt to create and start the device using the mdevctl utility. Note that some of the the configuration for a mediated device must be passed to mdevctl as a JSON-formatted file. In order to avoid creating and cleaning up temporary files, the JSON is instead fed to stdin and we pass the filename /dev/stdin to mdevctl. While this may not be portable, neither are mediated devices, so I don't believe it should cause any problems. Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
962 lines
25 KiB
C
962 lines
25 KiB
C
/*
|
|
* virnodedeviceobj.c: node device object handling
|
|
* (derived from node_device_conf.c)
|
|
*
|
|
* 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 "datatypes.h"
|
|
#include "node_device_conf.h"
|
|
|
|
#include "viralloc.h"
|
|
#include "virnodedeviceobj.h"
|
|
#include "virerror.h"
|
|
#include "virhash.h"
|
|
#include "virlog.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
VIR_LOG_INIT("conf.virnodedeviceobj");
|
|
|
|
struct _virNodeDeviceObj {
|
|
virObjectLockable parent;
|
|
|
|
virNodeDeviceDefPtr def; /* device definition */
|
|
bool skipUpdateCaps; /* whether to skip checking host caps,
|
|
used by testdriver */
|
|
};
|
|
|
|
struct _virNodeDeviceObjList {
|
|
virObjectRWLockable parent;
|
|
|
|
/* name string -> virNodeDeviceObj mapping
|
|
* for O(1), lockless lookup-by-name */
|
|
virHashTable *objs;
|
|
|
|
};
|
|
|
|
|
|
static virClassPtr virNodeDeviceObjClass;
|
|
static virClassPtr virNodeDeviceObjListClass;
|
|
static void virNodeDeviceObjDispose(void *opaque);
|
|
static void virNodeDeviceObjListDispose(void *opaque);
|
|
static bool virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, int type);
|
|
|
|
static int
|
|
virNodeDeviceObjOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virNodeDeviceObj, virClassForObjectLockable()))
|
|
return -1;
|
|
|
|
if (!VIR_CLASS_NEW(virNodeDeviceObjList, virClassForObjectRWLockable()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virNodeDeviceObj);
|
|
|
|
|
|
static void
|
|
virNodeDeviceObjDispose(void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = opaque;
|
|
|
|
virNodeDeviceDefFree(obj->def);
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjNew(void)
|
|
{
|
|
virNodeDeviceObjPtr obj;
|
|
|
|
if (virNodeDeviceObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(obj = virObjectLockableNew(virNodeDeviceObjClass)))
|
|
return NULL;
|
|
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
void
|
|
virNodeDeviceObjEndAPI(virNodeDeviceObjPtr *obj)
|
|
{
|
|
if (!*obj)
|
|
return;
|
|
|
|
virObjectUnlock(*obj);
|
|
virObjectUnref(*obj);
|
|
*obj = NULL;
|
|
}
|
|
|
|
|
|
virNodeDeviceDefPtr
|
|
virNodeDeviceObjGetDef(virNodeDeviceObjPtr obj)
|
|
{
|
|
return obj->def;
|
|
}
|
|
|
|
|
|
static bool
|
|
virNodeDeviceObjHasCapStr(const virNodeDeviceObj *obj,
|
|
const char *cap)
|
|
{
|
|
int type;
|
|
|
|
if ((type = virNodeDevCapTypeFromString(cap)) < 0)
|
|
return false;
|
|
|
|
return virNodeDeviceObjHasCap(obj, type);
|
|
}
|
|
|
|
|
|
/* virNodeDeviceFindFCCapDef:
|
|
* @obj: Pointer to current device
|
|
*
|
|
* Search the device object 'caps' array for fc_host capability.
|
|
*
|
|
* Returns:
|
|
* Pointer to the caps or NULL if not found
|
|
*/
|
|
static virNodeDevCapsDefPtr
|
|
virNodeDeviceFindFCCapDef(const virNodeDeviceObj *obj)
|
|
{
|
|
virNodeDevCapsDefPtr caps = obj->def->caps;
|
|
|
|
while (caps) {
|
|
if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST &&
|
|
(caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST))
|
|
break;
|
|
|
|
caps = caps->next;
|
|
}
|
|
return caps;
|
|
}
|
|
|
|
|
|
/* virNodeDeviceFindVPORTCapDef:
|
|
* @obj: Pointer to current device
|
|
*
|
|
* Search the device object 'caps' array for vport_ops capability.
|
|
*
|
|
* Returns:
|
|
* Pointer to the caps or NULL if not found
|
|
*/
|
|
static virNodeDevCapsDefPtr
|
|
virNodeDeviceFindVPORTCapDef(const virNodeDeviceObj *obj)
|
|
{
|
|
virNodeDevCapsDefPtr caps = obj->def->caps;
|
|
|
|
while (caps) {
|
|
if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST &&
|
|
(caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
|
|
break;
|
|
|
|
caps = caps->next;
|
|
}
|
|
return caps;
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjListSearch(virNodeDeviceObjListPtr devs,
|
|
virHashSearcher callback,
|
|
const void *data)
|
|
{
|
|
virNodeDeviceObjPtr obj;
|
|
|
|
virObjectRWLockRead(devs);
|
|
obj = virHashSearch(devs->objs, callback, data, NULL);
|
|
virObjectRef(obj);
|
|
virObjectRWUnlock(devs);
|
|
|
|
if (obj)
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListFindBySysfsPathCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
const char *sysfs_path = opaque;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
if (obj->def->sysfs_path &&
|
|
STREQ_NULLABLE(obj->def->sysfs_path, sysfs_path))
|
|
want = 1;
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindBySysfsPath(virNodeDeviceObjListPtr devs,
|
|
const char *sysfs_path)
|
|
{
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindBySysfsPathCallback,
|
|
sysfs_path);
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindByNameLocked(virNodeDeviceObjListPtr devs,
|
|
const char *name)
|
|
{
|
|
return virObjectRef(virHashLookup(devs->objs, name));
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindByName(virNodeDeviceObjListPtr devs,
|
|
const char *name)
|
|
{
|
|
virNodeDeviceObjPtr obj;
|
|
|
|
virObjectRWLockRead(devs);
|
|
obj = virNodeDeviceObjListFindByNameLocked(devs, name);
|
|
virObjectRWUnlock(devs);
|
|
if (obj)
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
struct virNodeDeviceObjListFindByWWNsData {
|
|
const char *parent_wwnn;
|
|
const char *parent_wwpn;
|
|
};
|
|
|
|
static int
|
|
virNodeDeviceObjListFindByWWNsCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
struct virNodeDeviceObjListFindByWWNsData *data =
|
|
(struct virNodeDeviceObjListFindByWWNsData *) opaque;
|
|
virNodeDevCapsDefPtr cap;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
if ((cap = virNodeDeviceFindFCCapDef(obj)) &&
|
|
STREQ_NULLABLE(cap->data.scsi_host.wwnn, data->parent_wwnn) &&
|
|
STREQ_NULLABLE(cap->data.scsi_host.wwpn, data->parent_wwpn) &&
|
|
virNodeDeviceFindVPORTCapDef(obj))
|
|
want = 1;
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindByWWNs(virNodeDeviceObjListPtr devs,
|
|
const char *parent_wwnn,
|
|
const char *parent_wwpn)
|
|
{
|
|
struct virNodeDeviceObjListFindByWWNsData data = {
|
|
.parent_wwnn = parent_wwnn, .parent_wwpn = parent_wwpn };
|
|
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindByWWNsCallback,
|
|
&data);
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListFindByFabricWWNCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
const char *matchstr = opaque;
|
|
virNodeDevCapsDefPtr cap;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
if ((cap = virNodeDeviceFindFCCapDef(obj)) &&
|
|
STREQ_NULLABLE(cap->data.scsi_host.fabric_wwn, matchstr) &&
|
|
virNodeDeviceFindVPORTCapDef(obj))
|
|
want = 1;
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindByFabricWWN(virNodeDeviceObjListPtr devs,
|
|
const char *parent_fabric_wwn)
|
|
{
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindByFabricWWNCallback,
|
|
parent_fabric_wwn);
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListFindByCapCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
const char *matchstr = opaque;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
if (virNodeDeviceObjHasCapStr(obj, matchstr))
|
|
want = 1;
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
static virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindByCap(virNodeDeviceObjListPtr devs,
|
|
const char *cap)
|
|
{
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindByCapCallback,
|
|
cap);
|
|
}
|
|
|
|
|
|
struct virNodeDeviceObjListFindSCSIHostByWWNsData {
|
|
const char *wwnn;
|
|
const char *wwpn;
|
|
};
|
|
|
|
static int
|
|
virNodeDeviceObjListFindSCSIHostByWWNsCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
struct virNodeDeviceObjListFindSCSIHostByWWNsData *data =
|
|
(struct virNodeDeviceObjListFindSCSIHostByWWNsData *) opaque;
|
|
virNodeDevCapsDefPtr cap;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
cap = obj->def->caps;
|
|
|
|
while (cap) {
|
|
if (cap->data.type == VIR_NODE_DEV_CAP_SCSI_HOST) {
|
|
virNodeDeviceGetSCSIHostCaps(&cap->data.scsi_host);
|
|
if (cap->data.scsi_host.flags &
|
|
VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
|
|
if (STREQ(cap->data.scsi_host.wwnn, data->wwnn) &&
|
|
STREQ(cap->data.scsi_host.wwpn, data->wwpn)) {
|
|
want = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cap = cap->next;
|
|
}
|
|
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceObjListPtr devs,
|
|
const char *wwnn,
|
|
const char *wwpn)
|
|
{
|
|
struct virNodeDeviceObjListFindSCSIHostByWWNsData data = {
|
|
.wwnn = wwnn, .wwpn = wwpn };
|
|
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindSCSIHostByWWNsCallback,
|
|
&data);
|
|
}
|
|
|
|
static int
|
|
virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
const void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
|
|
const char *uuid = (const char *) opaque;
|
|
virNodeDevCapsDefPtr cap;
|
|
int want = 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
for (cap = obj->def->caps; cap != NULL; cap = cap->next) {
|
|
if (cap->data.type == VIR_NODE_DEV_CAP_MDEV) {
|
|
if (STREQ(cap->data.mdev.uuid, uuid)) {
|
|
want = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
virObjectUnlock(obj);
|
|
return want;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjListPtr devs,
|
|
const char *uuid)
|
|
{
|
|
return virNodeDeviceObjListSearch(devs,
|
|
virNodeDeviceObjListFindMediatedDeviceByUUIDCallback,
|
|
uuid);
|
|
}
|
|
|
|
static void
|
|
virNodeDeviceObjListDispose(void *obj)
|
|
{
|
|
virNodeDeviceObjListPtr devs = obj;
|
|
|
|
virHashFree(devs->objs);
|
|
}
|
|
|
|
|
|
virNodeDeviceObjListPtr
|
|
virNodeDeviceObjListNew(void)
|
|
{
|
|
virNodeDeviceObjListPtr devs;
|
|
|
|
if (virNodeDeviceObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(devs = virObjectRWLockableNew(virNodeDeviceObjListClass)))
|
|
return NULL;
|
|
|
|
if (!(devs->objs = virHashCreate(50, virObjectFreeHashData))) {
|
|
virObjectUnref(devs);
|
|
return NULL;
|
|
}
|
|
|
|
return devs;
|
|
}
|
|
|
|
|
|
void
|
|
virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
|
|
{
|
|
virObjectUnref(devs);
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceObjListAssignDef(virNodeDeviceObjListPtr devs,
|
|
virNodeDeviceDefPtr def)
|
|
{
|
|
virNodeDeviceObjPtr obj;
|
|
|
|
virObjectRWLockWrite(devs);
|
|
|
|
if ((obj = virNodeDeviceObjListFindByNameLocked(devs, def->name))) {
|
|
virObjectLock(obj);
|
|
virNodeDeviceDefFree(obj->def);
|
|
obj->def = def;
|
|
} else {
|
|
if (!(obj = virNodeDeviceObjNew()))
|
|
goto cleanup;
|
|
|
|
if (virHashAddEntry(devs->objs, def->name, obj) < 0) {
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
goto cleanup;
|
|
}
|
|
|
|
obj->def = def;
|
|
virObjectRef(obj);
|
|
}
|
|
|
|
cleanup:
|
|
virObjectRWUnlock(devs);
|
|
return obj;
|
|
}
|
|
|
|
|
|
void
|
|
virNodeDeviceObjListRemove(virNodeDeviceObjListPtr devs,
|
|
virNodeDeviceObjPtr obj)
|
|
{
|
|
virNodeDeviceDefPtr def;
|
|
|
|
if (!obj)
|
|
return;
|
|
def = obj->def;
|
|
|
|
virObjectRef(obj);
|
|
virObjectUnlock(obj);
|
|
virObjectRWLockWrite(devs);
|
|
virObjectLock(obj);
|
|
virHashRemoveEntry(devs->objs, def->name);
|
|
virObjectUnlock(obj);
|
|
virObjectUnref(obj);
|
|
virObjectRWUnlock(devs);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the NPIV dev's parent device name
|
|
*/
|
|
/* virNodeDeviceFindFCParentHost:
|
|
* @obj: Pointer to node device object
|
|
*
|
|
* Search the capabilities for the device to find the FC capabilities
|
|
* in order to set the parent_host value.
|
|
*
|
|
* Returns:
|
|
* parent_host value on success (>= 0), -1 otherwise.
|
|
*/
|
|
static int
|
|
virNodeDeviceFindFCParentHost(virNodeDeviceObjPtr obj)
|
|
{
|
|
virNodeDevCapsDefPtr cap = virNodeDeviceFindVPORTCapDef(obj);
|
|
|
|
if (!cap) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Parent device %s is not capable "
|
|
"of vport operations"),
|
|
obj->def->name);
|
|
return -1;
|
|
}
|
|
|
|
return cap->data.scsi_host.host;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListGetParentHostByParent(virNodeDeviceObjListPtr devs,
|
|
const char *dev_name,
|
|
const char *parent_name)
|
|
{
|
|
virNodeDeviceObjPtr obj = NULL;
|
|
int ret;
|
|
|
|
if (!(obj = virNodeDeviceObjListFindByName(devs, parent_name))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find parent device for '%s'"),
|
|
dev_name);
|
|
return -1;
|
|
}
|
|
|
|
ret = virNodeDeviceFindFCParentHost(obj);
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListGetParentHostByWWNs(virNodeDeviceObjListPtr devs,
|
|
const char *dev_name,
|
|
const char *parent_wwnn,
|
|
const char *parent_wwpn)
|
|
{
|
|
virNodeDeviceObjPtr obj = NULL;
|
|
int ret;
|
|
|
|
if (!(obj = virNodeDeviceObjListFindByWWNs(devs, parent_wwnn,
|
|
parent_wwpn))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find parent device for '%s'"),
|
|
dev_name);
|
|
return -1;
|
|
}
|
|
|
|
ret = virNodeDeviceFindFCParentHost(obj);
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs,
|
|
const char *dev_name,
|
|
const char *parent_fabric_wwn)
|
|
{
|
|
virNodeDeviceObjPtr obj = NULL;
|
|
int ret;
|
|
|
|
if (!(obj = virNodeDeviceObjListFindByFabricWWN(devs, parent_fabric_wwn))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find parent device for '%s'"),
|
|
dev_name);
|
|
return -1;
|
|
}
|
|
|
|
ret = virNodeDeviceFindFCParentHost(obj);
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDeviceObjListFindVportParentHost(virNodeDeviceObjListPtr devs)
|
|
{
|
|
virNodeDeviceObjPtr obj = NULL;
|
|
const char *cap = virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS);
|
|
int ret;
|
|
|
|
if (!(obj = virNodeDeviceObjListFindByCap(devs, cap))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Could not find any vport capable device"));
|
|
return -1;
|
|
}
|
|
|
|
ret = virNodeDeviceFindFCParentHost(obj);
|
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
virNodeDeviceObjListGetParentHost(virNodeDeviceObjListPtr devs,
|
|
virNodeDeviceDefPtr def)
|
|
{
|
|
int parent_host = -1;
|
|
|
|
if (def->parent) {
|
|
parent_host = virNodeDeviceObjListGetParentHostByParent(devs, def->name,
|
|
def->parent);
|
|
} else if (def->parent_wwnn && def->parent_wwpn) {
|
|
parent_host = virNodeDeviceObjListGetParentHostByWWNs(devs, def->name,
|
|
def->parent_wwnn,
|
|
def->parent_wwpn);
|
|
} else if (def->parent_fabric_wwn) {
|
|
parent_host =
|
|
virNodeDeviceObjListGetParentHostByFabricWWN(devs, def->name,
|
|
def->parent_fabric_wwn);
|
|
} else {
|
|
/* Try to find a vport capable scsi_host when no parent supplied */
|
|
parent_host = virNodeDeviceObjListFindVportParentHost(devs);
|
|
}
|
|
|
|
return parent_host;
|
|
}
|
|
|
|
|
|
static bool
|
|
virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
|
|
int type)
|
|
{
|
|
virNodeDevCapsDefPtr cap = NULL;
|
|
|
|
for (cap = obj->def->caps; cap; cap = cap->next) {
|
|
if (type == cap->data.type)
|
|
return true;
|
|
|
|
switch (cap->data.type) {
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
|
|
(cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV))
|
|
return true;
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
if (type == VIR_NODE_DEV_CAP_FC_HOST &&
|
|
(cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST))
|
|
return true;
|
|
|
|
if (type == VIR_NODE_DEV_CAP_VPORTS &&
|
|
(cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
|
|
return true;
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
case VIR_NODE_DEV_CAP_FC_HOST:
|
|
case VIR_NODE_DEV_CAP_VPORTS:
|
|
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
|
|
case VIR_NODE_DEV_CAP_DRM:
|
|
case VIR_NODE_DEV_CAP_MDEV_TYPES:
|
|
case VIR_NODE_DEV_CAP_MDEV:
|
|
case VIR_NODE_DEV_CAP_CCW_DEV:
|
|
case VIR_NODE_DEV_CAP_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
struct virNodeDeviceCountData {
|
|
virConnectPtr conn;
|
|
virNodeDeviceObjListFilter filter;
|
|
const char *matchstr;
|
|
int count;
|
|
};
|
|
|
|
static int
|
|
virNodeDeviceObjListNumOfDevicesCallback(void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = payload;
|
|
virNodeDeviceDefPtr def;
|
|
struct virNodeDeviceCountData *data = opaque;
|
|
virNodeDeviceObjListFilter filter = data->filter;
|
|
|
|
virObjectLock(obj);
|
|
def = obj->def;
|
|
if ((!filter || filter(data->conn, def)) &&
|
|
(!data->matchstr || virNodeDeviceObjHasCapStr(obj, data->matchstr)))
|
|
data->count++;
|
|
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virNodeDeviceObjListNumOfDevices(virNodeDeviceObjListPtr devs,
|
|
virConnectPtr conn,
|
|
const char *cap,
|
|
virNodeDeviceObjListFilter filter)
|
|
{
|
|
struct virNodeDeviceCountData data = {
|
|
.conn = conn, .filter = filter, .matchstr = cap, .count = 0 };
|
|
|
|
virObjectRWLockRead(devs);
|
|
virHashForEach(devs->objs, virNodeDeviceObjListNumOfDevicesCallback, &data);
|
|
virObjectRWUnlock(devs);
|
|
|
|
return data.count;
|
|
}
|
|
|
|
|
|
struct virNodeDeviceGetNamesData {
|
|
virConnectPtr conn;
|
|
virNodeDeviceObjListFilter filter;
|
|
const char *matchstr;
|
|
int nnames;
|
|
char **names;
|
|
int maxnames;
|
|
bool error;
|
|
};
|
|
|
|
static int
|
|
virNodeDeviceObjListGetNamesCallback(void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = payload;
|
|
virNodeDeviceDefPtr def;
|
|
struct virNodeDeviceGetNamesData *data = opaque;
|
|
virNodeDeviceObjListFilter filter = data->filter;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
if (data->nnames >= data->maxnames)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
def = obj->def;
|
|
|
|
if ((!filter || filter(data->conn, def)) &&
|
|
(!data->matchstr || virNodeDeviceObjHasCapStr(obj, data->matchstr))) {
|
|
data->names[data->nnames] = g_strdup(def->name);
|
|
data->nnames++;
|
|
}
|
|
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virNodeDeviceObjListGetNames(virNodeDeviceObjListPtr devs,
|
|
virConnectPtr conn,
|
|
virNodeDeviceObjListFilter filter,
|
|
const char *cap,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
struct virNodeDeviceGetNamesData data = {
|
|
.conn = conn, .filter = filter, .matchstr = cap, .names = names,
|
|
.nnames = 0, .maxnames = maxnames, .error = false };
|
|
|
|
virObjectRWLockRead(devs);
|
|
virHashForEach(devs->objs, virNodeDeviceObjListGetNamesCallback, &data);
|
|
virObjectRWUnlock(devs);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.nnames;
|
|
|
|
error:
|
|
while (--data.nnames)
|
|
VIR_FREE(data.names[data.nnames]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
#define MATCH(FLAG) ((flags & (VIR_CONNECT_LIST_NODE_DEVICES_CAP_ ## FLAG)) && \
|
|
virNodeDeviceObjHasCap(obj, VIR_NODE_DEV_CAP_ ## FLAG))
|
|
static bool
|
|
virNodeDeviceObjMatch(virNodeDeviceObjPtr obj,
|
|
unsigned int flags)
|
|
{
|
|
/* Refresh the capabilities first, e.g. due to a driver change */
|
|
if (!obj->skipUpdateCaps &&
|
|
virNodeDeviceUpdateCaps(obj->def) < 0)
|
|
return false;
|
|
|
|
/* filter by cap type */
|
|
if (flags & VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP) {
|
|
if (!(MATCH(SYSTEM) ||
|
|
MATCH(PCI_DEV) ||
|
|
MATCH(USB_DEV) ||
|
|
MATCH(USB_INTERFACE) ||
|
|
MATCH(NET) ||
|
|
MATCH(SCSI_HOST) ||
|
|
MATCH(SCSI_TARGET) ||
|
|
MATCH(SCSI) ||
|
|
MATCH(STORAGE) ||
|
|
MATCH(FC_HOST) ||
|
|
MATCH(VPORTS) ||
|
|
MATCH(SCSI_GENERIC) ||
|
|
MATCH(DRM) ||
|
|
MATCH(MDEV_TYPES) ||
|
|
MATCH(MDEV) ||
|
|
MATCH(CCW_DEV)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#undef MATCH
|
|
|
|
|
|
typedef struct _virNodeDeviceObjListExportData virNodeDeviceObjListExportData;
|
|
typedef virNodeDeviceObjListExportData *virNodeDeviceObjListExportDataPtr;
|
|
struct _virNodeDeviceObjListExportData {
|
|
virConnectPtr conn;
|
|
virNodeDeviceObjListFilter filter;
|
|
unsigned int flags;
|
|
virNodeDevicePtr *devices;
|
|
int ndevices;
|
|
bool error;
|
|
};
|
|
|
|
static int
|
|
virNodeDeviceObjListExportCallback(void *payload,
|
|
const void *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNodeDeviceObjPtr obj = payload;
|
|
virNodeDeviceDefPtr def;
|
|
virNodeDeviceObjListExportDataPtr data = opaque;
|
|
virNodeDevicePtr device = NULL;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
def = obj->def;
|
|
|
|
if ((!data->filter || data->filter(data->conn, def)) &&
|
|
virNodeDeviceObjMatch(obj, data->flags)) {
|
|
if (data->devices) {
|
|
if (!(device = virGetNodeDevice(data->conn, def->name))) {
|
|
virObjectUnref(device);
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
device->parentName = g_strdup(def->parent);
|
|
data->devices[data->ndevices] = device;
|
|
}
|
|
data->ndevices++;
|
|
}
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virNodeDeviceObjListExport(virConnectPtr conn,
|
|
virNodeDeviceObjListPtr devs,
|
|
virNodeDevicePtr **devices,
|
|
virNodeDeviceObjListFilter filter,
|
|
unsigned int flags)
|
|
{
|
|
virNodeDeviceObjListExportData data = {
|
|
.conn = conn, .filter = filter, .flags = flags,
|
|
.devices = NULL, .ndevices = 0, .error = false };
|
|
|
|
virObjectRWLockRead(devs);
|
|
if (devices &&
|
|
VIR_ALLOC_N(data.devices, virHashSize(devs->objs) + 1) < 0) {
|
|
virObjectRWUnlock(devs);
|
|
return -1;
|
|
}
|
|
|
|
virHashForEach(devs->objs, virNodeDeviceObjListExportCallback, &data);
|
|
virObjectRWUnlock(devs);
|
|
|
|
if (data.error)
|
|
goto cleanup;
|
|
|
|
if (data.devices) {
|
|
ignore_value(VIR_REALLOC_N(data.devices, data.ndevices + 1));
|
|
*devices = data.devices;
|
|
}
|
|
|
|
return data.ndevices;
|
|
|
|
cleanup:
|
|
virObjectListFree(data.devices);
|
|
return -1;
|
|
}
|
|
|
|
|
|
void
|
|
virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObjPtr obj,
|
|
bool skipUpdateCaps)
|
|
{
|
|
obj->skipUpdateCaps = skipUpdateCaps;
|
|
}
|