/* * 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; }