/*
* 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 "virlog.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_NODEDEV
VIR_LOG_INIT("conf.virnodedeviceobj");
static int
virNodeDeviceObjHasCap(const virNodeDeviceObj *dev,
const char *cap)
{
virNodeDevCapsDefPtr caps = dev->def->caps;
const char *fc_host_cap =
virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_FC_HOST);
const char *vports_cap =
virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS);
while (caps) {
if (STREQ(cap, virNodeDevCapTypeToString(caps->data.type)))
return 1;
else if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST)
if ((STREQ(cap, fc_host_cap) &&
(caps->data.scsi_host.flags &
VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)) ||
(STREQ(cap, vports_cap) &&
(caps->data.scsi_host.flags &
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)))
return 1;
caps = caps->next;
}
return 0;
}
/* virNodeDeviceFindFCCapDef:
* @dev: 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 *dev)
{
virNodeDevCapsDefPtr caps = dev->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:
* @dev: 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 *dev)
{
virNodeDevCapsDefPtr caps = dev->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;
}
virNodeDeviceObjPtr
virNodeDeviceObjFindBySysfsPath(virNodeDeviceObjListPtr devs,
const char *sysfs_path)
{
size_t i;
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjLock(devs->objs[i]);
if ((devs->objs[i]->def->sysfs_path != NULL) &&
(STREQ(devs->objs[i]->def->sysfs_path, sysfs_path))) {
return devs->objs[i];
}
virNodeDeviceObjUnlock(devs->objs[i]);
}
return NULL;
}
virNodeDeviceObjPtr
virNodeDeviceObjFindByName(virNodeDeviceObjListPtr devs,
const char *name)
{
size_t i;
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjLock(devs->objs[i]);
if (STREQ(devs->objs[i]->def->name, name))
return devs->objs[i];
virNodeDeviceObjUnlock(devs->objs[i]);
}
return NULL;
}
static virNodeDeviceObjPtr
virNodeDeviceFindByWWNs(virNodeDeviceObjListPtr devs,
const char *parent_wwnn,
const char *parent_wwpn)
{
size_t i;
for (i = 0; i < devs->count; i++) {
virNodeDevCapsDefPtr cap;
virNodeDeviceObjLock(devs->objs[i]);
if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) &&
STREQ_NULLABLE(cap->data.scsi_host.wwnn, parent_wwnn) &&
STREQ_NULLABLE(cap->data.scsi_host.wwpn, parent_wwpn))
return devs->objs[i];
virNodeDeviceObjUnlock(devs->objs[i]);
}
return NULL;
}
static virNodeDeviceObjPtr
virNodeDeviceFindByFabricWWN(virNodeDeviceObjListPtr devs,
const char *parent_fabric_wwn)
{
size_t i;
for (i = 0; i < devs->count; i++) {
virNodeDevCapsDefPtr cap;
virNodeDeviceObjLock(devs->objs[i]);
if ((cap = virNodeDeviceFindFCCapDef(devs->objs[i])) &&
STREQ_NULLABLE(cap->data.scsi_host.fabric_wwn, parent_fabric_wwn))
return devs->objs[i];
virNodeDeviceObjUnlock(devs->objs[i]);
}
return NULL;
}
static virNodeDeviceObjPtr
virNodeDeviceFindByCap(virNodeDeviceObjListPtr devs,
const char *cap)
{
size_t i;
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjLock(devs->objs[i]);
if (virNodeDeviceObjHasCap(devs->objs[i], cap))
return devs->objs[i];
virNodeDeviceObjUnlock(devs->objs[i]);
}
return NULL;
}
void
virNodeDeviceObjFree(virNodeDeviceObjPtr dev)
{
if (!dev)
return;
virNodeDeviceDefFree(dev->def);
if (dev->privateFree)
(*dev->privateFree)(dev->privateData);
virMutexDestroy(&dev->lock);
VIR_FREE(dev);
}
void
virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
{
size_t i;
for (i = 0; i < devs->count; i++)
virNodeDeviceObjFree(devs->objs[i]);
VIR_FREE(devs->objs);
devs->count = 0;
}
virNodeDeviceObjPtr
virNodeDeviceObjAssignDef(virNodeDeviceObjListPtr devs,
virNodeDeviceDefPtr def)
{
virNodeDeviceObjPtr device;
if ((device = virNodeDeviceObjFindByName(devs, def->name))) {
virNodeDeviceDefFree(device->def);
device->def = def;
return device;
}
if (VIR_ALLOC(device) < 0)
return NULL;
if (virMutexInit(&device->lock) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot initialize mutex"));
VIR_FREE(device);
return NULL;
}
virNodeDeviceObjLock(device);
if (VIR_APPEND_ELEMENT_COPY(devs->objs, devs->count, device) < 0) {
virNodeDeviceObjUnlock(device);
virNodeDeviceObjFree(device);
return NULL;
}
device->def = def;
return device;
}
void
virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
virNodeDeviceObjPtr *dev)
{
size_t i;
virNodeDeviceObjUnlock(*dev);
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjLock(*dev);
if (devs->objs[i] == *dev) {
virNodeDeviceObjUnlock(*dev);
virNodeDeviceObjFree(devs->objs[i]);
*dev = NULL;
VIR_DELETE_ELEMENT(devs->objs, i, devs->count);
break;
}
virNodeDeviceObjUnlock(*dev);
}
}
/*
* Return the NPIV dev's parent device name
*/
/* virNodeDeviceFindFCParentHost:
* @parent: 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 parent)
{
virNodeDevCapsDefPtr cap = virNodeDeviceFindVPORTCapDef(parent);
if (!cap) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Parent device %s is not capable "
"of vport operations"),
parent->def->name);
return -1;
}
return cap->data.scsi_host.host;
}
static int
virNodeDeviceGetParentHostByParent(virNodeDeviceObjListPtr devs,
const char *dev_name,
const char *parent_name)
{
virNodeDeviceObjPtr parent = NULL;
int ret;
if (!(parent = virNodeDeviceObjFindByName(devs, parent_name))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find parent device for '%s'"),
dev_name);
return -1;
}
ret = virNodeDeviceFindFCParentHost(parent);
virNodeDeviceObjUnlock(parent);
return ret;
}
static int
virNodeDeviceGetParentHostByWWNs(virNodeDeviceObjListPtr devs,
const char *dev_name,
const char *parent_wwnn,
const char *parent_wwpn)
{
virNodeDeviceObjPtr parent = NULL;
int ret;
if (!(parent = virNodeDeviceFindByWWNs(devs, parent_wwnn, parent_wwpn))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find parent device for '%s'"),
dev_name);
return -1;
}
ret = virNodeDeviceFindFCParentHost(parent);
virNodeDeviceObjUnlock(parent);
return ret;
}
static int
virNodeDeviceGetParentHostByFabricWWN(virNodeDeviceObjListPtr devs,
const char *dev_name,
const char *parent_fabric_wwn)
{
virNodeDeviceObjPtr parent = NULL;
int ret;
if (!(parent = virNodeDeviceFindByFabricWWN(devs, parent_fabric_wwn))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find parent device for '%s'"),
dev_name);
return -1;
}
ret = virNodeDeviceFindFCParentHost(parent);
virNodeDeviceObjUnlock(parent);
return ret;
}
static int
virNodeDeviceFindVportParentHost(virNodeDeviceObjListPtr devs)
{
virNodeDeviceObjPtr parent = NULL;
const char *cap = virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS);
int ret;
if (!(parent = virNodeDeviceFindByCap(devs, cap))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not find any vport capable device"));
return -1;
}
ret = virNodeDeviceFindFCParentHost(parent);
virNodeDeviceObjUnlock(parent);
return ret;
}
int
virNodeDeviceObjGetParentHost(virNodeDeviceObjListPtr devs,
virNodeDeviceDefPtr def,
int create)
{
int parent_host = -1;
if (def->parent) {
parent_host = virNodeDeviceGetParentHostByParent(devs, def->name,
def->parent);
} else if (def->parent_wwnn && def->parent_wwpn) {
parent_host = virNodeDeviceGetParentHostByWWNs(devs, def->name,
def->parent_wwnn,
def->parent_wwpn);
} else if (def->parent_fabric_wwn) {
parent_host =
virNodeDeviceGetParentHostByFabricWWN(devs, def->name,
def->parent_fabric_wwn);
} else if (create == CREATE_DEVICE) {
/* Try to find a vport capable scsi_host when no parent supplied */
parent_host = virNodeDeviceFindVportParentHost(devs);
}
return parent_host;
}
void
virNodeDeviceObjLock(virNodeDeviceObjPtr obj)
{
virMutexLock(&obj->lock);
}
void
virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj)
{
virMutexUnlock(&obj->lock);
}
static bool
virNodeDeviceCapMatch(virNodeDeviceObjPtr devobj,
int type)
{
virNodeDevCapsDefPtr cap = NULL;
for (cap = devobj->def->caps; cap; cap = cap->next) {
if (type == cap->data.type)
return true;
if (cap->data.type == 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;
}
}
return false;
}
int
virNodeDeviceObjNumOfDevices(virNodeDeviceObjListPtr devs,
virConnectPtr conn,
const char *cap,
virNodeDeviceObjListFilter aclfilter)
{
size_t i;
int ndevs = 0;
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjPtr obj = devs->objs[i];
virNodeDeviceObjLock(obj);
if (aclfilter && aclfilter(conn, obj->def) &&
(!cap || virNodeDeviceObjHasCap(obj, cap)))
++ndevs;
virNodeDeviceObjUnlock(obj);
}
return ndevs;
}
int
virNodeDeviceObjGetNames(virNodeDeviceObjListPtr devs,
virConnectPtr conn,
virNodeDeviceObjListFilter aclfilter,
const char *cap,
char **const names,
int maxnames)
{
int nnames = 0;
size_t i;
for (i = 0; i < devs->count && nnames < maxnames; i++) {
virNodeDeviceObjPtr obj = devs->objs[i];
virNodeDeviceObjLock(obj);
if (aclfilter && aclfilter(conn, obj->def) &&
(!cap || virNodeDeviceObjHasCap(obj, cap))) {
if (VIR_STRDUP(names[nnames], obj->def->name) < 0) {
virNodeDeviceObjUnlock(obj);
goto failure;
}
nnames++;
}
virNodeDeviceObjUnlock(obj);
}
return nnames;
failure:
while (--nnames >= 0)
VIR_FREE(names[nnames]);
return -1;
}
#define MATCH(FLAG) ((flags & (VIR_CONNECT_LIST_NODE_DEVICES_CAP_ ## FLAG)) && \
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_ ## FLAG))
static bool
virNodeDeviceMatch(virNodeDeviceObjPtr devobj,
unsigned int flags)
{
/* 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)))
return false;
}
return true;
}
#undef MATCH
int
virNodeDeviceObjListExport(virConnectPtr conn,
virNodeDeviceObjListPtr devobjs,
virNodeDevicePtr **devices,
virNodeDeviceObjListFilter filter,
unsigned int flags)
{
virNodeDevicePtr *tmp_devices = NULL;
virNodeDevicePtr device = NULL;
int ndevices = 0;
int ret = -1;
size_t i;
if (devices && VIR_ALLOC_N(tmp_devices, devobjs->count + 1) < 0)
goto cleanup;
for (i = 0; i < devobjs->count; i++) {
virNodeDeviceObjPtr devobj = devobjs->objs[i];
virNodeDeviceObjLock(devobj);
if ((!filter || filter(conn, devobj->def)) &&
virNodeDeviceMatch(devobj, flags)) {
if (devices) {
if (!(device = virGetNodeDevice(conn, devobj->def->name)) ||
VIR_STRDUP(device->parent, devobj->def->parent) < 0) {
virObjectUnref(device);
virNodeDeviceObjUnlock(devobj);
goto cleanup;
}
tmp_devices[ndevices] = device;
}
ndevices++;
}
virNodeDeviceObjUnlock(devobj);
}
if (tmp_devices) {
/* trim the array to the final size */
ignore_value(VIR_REALLOC_N(tmp_devices, ndevices + 1));
*devices = tmp_devices;
tmp_devices = NULL;
}
ret = ndevices;
cleanup:
if (tmp_devices) {
for (i = 0; i < ndevices; i++)
virObjectUnref(tmp_devices[i]);
}
VIR_FREE(tmp_devices);
return ret;
}