/*
* 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
* .
*/
#include
#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;
virNodeDeviceDef *def; /* device definition */
bool skipUpdateCaps; /* whether to skip checking host caps,
used by testdriver */
bool active;
bool persistent;
};
struct _virNodeDeviceObjList {
virObjectRWLockable parent;
/* name string -> virNodeDeviceObj mapping
* for O(1), lockless lookup-by-name */
GHashTable *objs;
};
static virClass *virNodeDeviceObjClass;
static virClass *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)
{
virNodeDeviceObj *obj = opaque;
virNodeDeviceDefFree(obj->def);
}
static virNodeDeviceObj *
virNodeDeviceObjNew(void)
{
virNodeDeviceObj *obj;
if (virNodeDeviceObjInitialize() < 0)
return NULL;
if (!(obj = virObjectLockableNew(virNodeDeviceObjClass)))
return NULL;
virObjectLock(obj);
return obj;
}
void
virNodeDeviceObjEndAPI(virNodeDeviceObj **obj)
{
if (!*obj)
return;
virObjectUnlock(*obj);
virObjectUnref(*obj);
*obj = NULL;
}
virNodeDeviceDef *
virNodeDeviceObjGetDef(virNodeDeviceObj *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 virNodeDevCapsDef *
virNodeDeviceFindFCCapDef(const virNodeDeviceObj *obj)
{
virNodeDevCapsDef *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 virNodeDevCapsDef *
virNodeDeviceFindVPORTCapDef(const virNodeDeviceObj *obj)
{
virNodeDevCapsDef *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 virNodeDeviceObj *
virNodeDeviceObjListSearch(virNodeDeviceObjList *devs,
virHashSearcher callback,
const void *data)
{
virNodeDeviceObj *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 char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) 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;
}
virNodeDeviceObj *
virNodeDeviceObjListFindBySysfsPath(virNodeDeviceObjList *devs,
const char *sysfs_path)
{
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindBySysfsPathCallback,
sysfs_path);
}
static virNodeDeviceObj *
virNodeDeviceObjListFindByNameLocked(virNodeDeviceObjList *devs,
const char *name)
{
return virObjectRef(virHashLookup(devs->objs, name));
}
virNodeDeviceObj *
virNodeDeviceObjListFindByName(virNodeDeviceObjList *devs,
const char *name)
{
virNodeDeviceObj *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 char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
struct virNodeDeviceObjListFindByWWNsData *data =
(struct virNodeDeviceObjListFindByWWNsData *) opaque;
virNodeDevCapsDef *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 virNodeDeviceObj *
virNodeDeviceObjListFindByWWNs(virNodeDeviceObjList *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 char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
const char *matchstr = opaque;
virNodeDevCapsDef *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 virNodeDeviceObj *
virNodeDeviceObjListFindByFabricWWN(virNodeDeviceObjList *devs,
const char *parent_fabric_wwn)
{
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindByFabricWWNCallback,
parent_fabric_wwn);
}
static int
virNodeDeviceObjListFindByCapCallback(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
const char *matchstr = opaque;
int want = 0;
virObjectLock(obj);
if (virNodeDeviceObjHasCapStr(obj, matchstr))
want = 1;
virObjectUnlock(obj);
return want;
}
static virNodeDeviceObj *
virNodeDeviceObjListFindByCap(virNodeDeviceObjList *devs,
const char *cap)
{
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindByCapCallback,
cap);
}
struct virNodeDeviceObjListFindSCSIHostByWWNsData {
const char *wwnn;
const char *wwpn;
};
static int
virNodeDeviceObjListFindSCSIHostByWWNsCallback(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
struct virNodeDeviceObjListFindSCSIHostByWWNsData *data =
(struct virNodeDeviceObjListFindSCSIHostByWWNsData *) opaque;
virNodeDevCapsDef *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;
}
virNodeDeviceObj *
virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceObjList *devs,
const char *wwnn,
const char *wwpn)
{
struct virNodeDeviceObjListFindSCSIHostByWWNsData data = {
.wwnn = wwnn, .wwpn = wwpn };
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindSCSIHostByWWNsCallback,
&data);
}
typedef struct _FindMediatedDeviceData FindMediatedDeviceData;
struct _FindMediatedDeviceData {
const char *uuid;
const char *parent_addr;
};
static int
virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
const FindMediatedDeviceData* data = opaque;
virNodeDevCapsDef *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, data->uuid) &&
STREQ(cap->data.mdev.parent_addr, data->parent_addr)) {
want = 1;
break;
}
}
}
virObjectUnlock(obj);
return want;
}
virNodeDeviceObj *
virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjList *devs,
const char *uuid,
const char *parent_addr)
{
const FindMediatedDeviceData data = {uuid, parent_addr};
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindMediatedDeviceByUUIDCallback,
&data);
}
static void
virNodeDeviceObjListDispose(void *obj)
{
virNodeDeviceObjList *devs = obj;
virHashFree(devs->objs);
}
virNodeDeviceObjList *
virNodeDeviceObjListNew(void)
{
virNodeDeviceObjList *devs;
if (virNodeDeviceObjInitialize() < 0)
return NULL;
if (!(devs = virObjectRWLockableNew(virNodeDeviceObjListClass)))
return NULL;
devs->objs = virHashNew(virObjectFreeHashData);
return devs;
}
void
virNodeDeviceObjListFree(virNodeDeviceObjList *devs)
{
virObjectUnref(devs);
}
virNodeDeviceObj *
virNodeDeviceObjListAssignDef(virNodeDeviceObjList *devs,
virNodeDeviceDef *def)
{
virNodeDeviceObj *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(virNodeDeviceObjList *devs,
virNodeDeviceObj *obj)
{
if (!obj)
return;
virObjectRef(obj);
virObjectUnlock(obj);
virObjectRWLockWrite(devs);
virObjectLock(obj);
virNodeDeviceObjListRemoveLocked(devs, obj);
virObjectUnlock(obj);
virObjectUnref(obj);
virObjectRWUnlock(devs);
}
/* The caller must hold lock on 'devs' */
void
virNodeDeviceObjListRemoveLocked(virNodeDeviceObjList *devs,
virNodeDeviceObj *dev)
{
virHashRemoveEntry(devs->objs, dev->def->name);
}
/*
* 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(virNodeDeviceObj *obj)
{
virNodeDevCapsDef *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(virNodeDeviceObjList *devs,
const char *dev_name,
const char *parent_name)
{
virNodeDeviceObj *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(virNodeDeviceObjList *devs,
const char *dev_name,
const char *parent_wwnn,
const char *parent_wwpn)
{
virNodeDeviceObj *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(virNodeDeviceObjList *devs,
const char *dev_name,
const char *parent_fabric_wwn)
{
virNodeDeviceObj *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(virNodeDeviceObjList *devs)
{
virNodeDeviceObj *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(virNodeDeviceObjList *devs,
virNodeDeviceDef *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)
{
virNodeDevCapsDef *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_CSS_DEV:
if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
(cap->data.ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CSS_MDEV))
return true;
break;
case VIR_NODE_DEV_CAP_AP_MATRIX:
if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
(cap->data.ap_matrix.flags & VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV))
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_VDPA:
case VIR_NODE_DEV_CAP_AP_CARD:
case VIR_NODE_DEV_CAP_AP_QUEUE:
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 char *name G_GNUC_UNUSED,
void *opaque)
{
virNodeDeviceObj *obj = payload;
virNodeDeviceDef *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(virNodeDeviceObjList *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 char *name G_GNUC_UNUSED,
void *opaque)
{
virNodeDeviceObj *obj = payload;
virNodeDeviceDef *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(virNodeDeviceObjList *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_CAP(FLAG) ((flags & (VIR_CONNECT_LIST_NODE_DEVICES_CAP_ ## FLAG)) && \
virNodeDeviceObjHasCap(obj, VIR_NODE_DEV_CAP_ ## FLAG))
#define MATCH(FLAG) (flags & (FLAG))
static bool
virNodeDeviceObjMatch(virNodeDeviceObj *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_CAP(SYSTEM) ||
MATCH_CAP(PCI_DEV) ||
MATCH_CAP(USB_DEV) ||
MATCH_CAP(USB_INTERFACE) ||
MATCH_CAP(NET) ||
MATCH_CAP(SCSI_HOST) ||
MATCH_CAP(SCSI_TARGET) ||
MATCH_CAP(SCSI) ||
MATCH_CAP(STORAGE) ||
MATCH_CAP(FC_HOST) ||
MATCH_CAP(VPORTS) ||
MATCH_CAP(SCSI_GENERIC) ||
MATCH_CAP(DRM) ||
MATCH_CAP(MDEV_TYPES) ||
MATCH_CAP(MDEV) ||
MATCH_CAP(CCW_DEV) ||
MATCH_CAP(CSS_DEV) ||
MATCH_CAP(VDPA) ||
MATCH_CAP(AP_CARD) ||
MATCH_CAP(AP_QUEUE) ||
MATCH_CAP(AP_MATRIX)))
return false;
}
if (flags & (VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE)) {
if (!((MATCH(VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE) &&
virNodeDeviceObjIsActive(obj)) ||
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_INACTIVE) &&
!virNodeDeviceObjIsActive(obj))))
return false;
}
return true;
}
#undef MATCH
#undef MATCH_CAP
typedef struct _virNodeDeviceObjListExportData virNodeDeviceObjListExportData;
struct _virNodeDeviceObjListExportData {
virConnectPtr conn;
virNodeDeviceObjListFilter filter;
unsigned int flags;
virNodeDevicePtr *devices;
int ndevices;
bool error;
};
static int
virNodeDeviceObjListExportCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virNodeDeviceObj *obj = payload;
virNodeDeviceDef *def;
virNodeDeviceObjListExportData *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,
virNodeDeviceObjList *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)
data.devices = g_new0(virNodeDevicePtr, virHashSize(devs->objs) + 1);
virHashForEach(devs->objs, virNodeDeviceObjListExportCallback, &data);
virObjectRWUnlock(devs);
if (data.error)
goto cleanup;
if (data.devices) {
VIR_REALLOC_N(data.devices, data.ndevices + 1);
*devices = data.devices;
}
return data.ndevices;
cleanup:
virObjectListFree(data.devices);
return -1;
}
void
virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObj *obj,
bool skipUpdateCaps)
{
obj->skipUpdateCaps = skipUpdateCaps;
}
bool
virNodeDeviceObjIsActive(virNodeDeviceObj *obj)
{
return obj->active;
}
void
virNodeDeviceObjSetActive(virNodeDeviceObj *obj,
bool active)
{
obj->active = active;
}
bool
virNodeDeviceObjIsPersistent(virNodeDeviceObj *obj)
{
return obj->persistent;
}
void
virNodeDeviceObjSetPersistent(virNodeDeviceObj *obj,
bool persistent)
{
obj->persistent = persistent;
}
typedef struct _PredicateHelperData PredicateHelperData;
struct _PredicateHelperData {
virNodeDeviceObjListPredicate predicate;
void *opaque;
};
static int virNodeDeviceObjListRemoveHelper(void *key G_GNUC_UNUSED,
void *value,
void *opaque)
{
PredicateHelperData *data = opaque;
return data->predicate(value, data->opaque);
}
/**
* virNodeDeviceObjListForEachRemove
* @devs: Pointer to object list
* @callback: function to call for each device object
* @opaque: Opaque data to use as argument to helper
*
* For each object in @devs, call the @callback helper using @opaque as
* an argument. If @callback returns true, that item will be removed from the
* object list.
*/
void
virNodeDeviceObjListForEachRemove(virNodeDeviceObjList *devs,
virNodeDeviceObjListPredicate callback,
void *opaque)
{
PredicateHelperData data = {
.predicate = callback,
.opaque = opaque
};
virObjectRWLockWrite(devs);
g_hash_table_foreach_remove(devs->objs,
virNodeDeviceObjListRemoveHelper,
&data);
virObjectRWUnlock(devs);
}
static int virNodeDeviceObjListFindHelper(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
PredicateHelperData *data = (PredicateHelperData *) opaque;
virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
return data->predicate(obj, data->opaque);
}
/**
* virNodeDeviceObjListFind
* @devs: Pointer to object list
* @predicate: function to test the device for a certain property
* @opaque: Opaque data to use as argument to helper
*
* For each object in @devs, call the @predicate helper using @opaque as
* an argument until it returns TRUE. The list may not be modified while
* iterating.
*/
virNodeDeviceObj *
virNodeDeviceObjListFind(virNodeDeviceObjList *devs,
virNodeDeviceObjListPredicate predicate,
void *opaque)
{
PredicateHelperData data = {
.predicate = predicate,
.opaque = opaque
};
return virNodeDeviceObjListSearch(devs,
virNodeDeviceObjListFindHelper,
&data);
}