libvirt/src/conf/virnodedeviceobj.c
Jonathon Jongsma 00b649d0cf nodedev: add helper functions to remove node devices
When a mediated device is stopped or undefined by an application outside
of libvirt, we need to remove it from our list of node devices within
libvirt. This patch introduces virNodeDeviceObjListRemoveLocked() and
virNodeDeviceObjListForEachRemove() (which are analogous to other types
of object lists in libvirt) to facilitate that. They will be used in
coming commits.

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Reviewed-by: Erik Skultety <eskultet@redhat.com>
2021-04-07 15:08:59 -05:00

1072 lines
28 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 */
bool active;
bool persistent;
};
struct _virNodeDeviceObjList {
virObjectRWLockable parent;
/* name string -> virNodeDeviceObj mapping
* for O(1), lockless lookup-by-name */
GHashTable *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 char *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 char *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 char *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 char *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 char *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 char *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 = virHashNew(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)
{
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(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_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)
{
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 char *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_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(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_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;
typedef virNodeDeviceObjListExportData *virNodeDeviceObjListExportDataPtr;
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)
{
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)
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(virNodeDeviceObjPtr 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;
}
struct virNodeDeviceObjListRemoveHelperData
{
virNodeDeviceObjListRemoveIterator callback;
void *opaque;
};
static int virNodeDeviceObjListRemoveHelper(void *key G_GNUC_UNUSED,
void *value,
void *opaque)
{
struct virNodeDeviceObjListRemoveHelperData *data = opaque;
return data->callback(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,
virNodeDeviceObjListRemoveIterator callback,
void *opaque)
{
struct virNodeDeviceObjListRemoveHelperData data = {
.callback = callback,
.opaque = opaque
};
virObjectRWLockWrite(devs);
g_hash_table_foreach_remove(devs->objs,
virNodeDeviceObjListRemoveHelper,
&data);
virObjectRWUnlock(devs);
}