libvirt/src/node_device/node_device_linux_sysfs.c
Laine Stump f391889f4e nodedev: report maxCount for virtual_functions capability
A PCI device may have the capability to setup virtual functions (VFs)
but have them currently all disabled. Prior to this patch, if that was
the case the the node device XML for the device wouldn't report any
virtual_functions capability.

With this patch, if a file called "sriov_totalvfs" is found in the
device's sysfs directory, its contents will be interpreted as a
decimal number, and that value will be reported as "maxCount" in a
capability element of the device's XML, e.g.:

   <capability type='virtual_functions' maxCount='7'/>

This will be reported regardless of whether or not any VFs are
currently enabled for the device.

NB: sriov_numvfs (the number of VFs currently active) is also
available in sysfs, but that value is implied by the number of items
in the list that is inside the capability element, so there is no
reason to explicitly provide it as an attribute.

sriov_totalvfs and sriov_numvfs are available in kernels at least as far
back as the 2.6.32 that is in RHEL6.7, but in the case that they
simply aren't there, libvirt will behave as it did prior to this patch
- no maxCount will be displayed, and the virtual_functions capability
will be absent from the device's XML when 0 VFs are enabled.
2015-11-24 12:31:04 -05:00

242 lines
7.5 KiB
C

/*
* node_device_linux_sysfs.c: Linux specific code to gather device data
* that is available from sysfs (but not from UDEV or HAL).
*
* Copyright (C) 2009-2015 Red Hat, Inc.
*
* 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 <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "node_device_driver.h"
#include "node_device_hal.h"
#include "node_device_linux_sysfs.h"
#include "virerror.h"
#include "viralloc.h"
#include "virlog.h"
#include "virfile.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_NODEDEV
#ifdef __linux__
VIR_LOG_INIT("node_device.node_device_linux_sysfs");
int
nodeDeviceSysfsGetSCSIHostCaps(virNodeDevCapDataPtr d)
{
char *max_vports = NULL;
char *vports = NULL;
int ret = -1;
if (virReadSCSIUniqueId(NULL, d->scsi_host.host,
&d->scsi_host.unique_id) < 0) {
VIR_DEBUG("Failed to read unique_id for host%d", d->scsi_host.host);
d->scsi_host.unique_id = -1;
}
VIR_DEBUG("Checking if host%d is an FC HBA", d->scsi_host.host);
if (virIsCapableFCHost(NULL, d->scsi_host.host)) {
d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
if (virReadFCHost(NULL,
d->scsi_host.host,
"port_name",
&d->scsi_host.wwpn) < 0) {
VIR_WARN("Failed to read WWPN for host%d", d->scsi_host.host);
goto cleanup;
}
if (virReadFCHost(NULL,
d->scsi_host.host,
"node_name",
&d->scsi_host.wwnn) < 0) {
VIR_WARN("Failed to read WWNN for host%d", d->scsi_host.host);
goto cleanup;
}
if (virReadFCHost(NULL,
d->scsi_host.host,
"fabric_name",
&d->scsi_host.fabric_wwn) < 0) {
VIR_WARN("Failed to read fabric WWN for host%d",
d->scsi_host.host);
goto cleanup;
}
}
if (virIsCapableVport(NULL, d->scsi_host.host)) {
d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
if (virReadFCHost(NULL,
d->scsi_host.host,
"max_npiv_vports",
&max_vports) < 0) {
VIR_WARN("Failed to read max_npiv_vports for host%d",
d->scsi_host.host);
goto cleanup;
}
if (virReadFCHost(NULL,
d->scsi_host.host,
"npiv_vports_inuse",
&vports) < 0) {
VIR_WARN("Failed to read npiv_vports_inuse for host%d",
d->scsi_host.host);
goto cleanup;
}
if (virStrToLong_i(max_vports, NULL, 10,
&d->scsi_host.max_vports) < 0) {
VIR_WARN("Failed to parse value of max_npiv_vports '%s'",
max_vports);
goto cleanup;
}
if (virStrToLong_i(vports, NULL, 10,
&d->scsi_host.vports) < 0) {
VIR_WARN("Failed to parse value of npiv_vports_inuse '%s'",
vports);
goto cleanup;
}
}
ret = 0;
cleanup:
if (ret < 0) {
/* Clear the two flags in case of producing confusing XML output */
d->scsi_host.flags &= ~(VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST |
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS);
VIR_FREE(d->scsi_host.wwnn);
VIR_FREE(d->scsi_host.wwpn);
VIR_FREE(d->scsi_host.fabric_wwn);
}
VIR_FREE(max_vports);
VIR_FREE(vports);
return ret;
}
static int
nodeDeviceSysfsGetPCISRIOVCaps(const char *sysfsPath,
virNodeDevCapDataPtr data)
{
size_t i;
int ret;
/* this could be a refresh, so clear out the old data */
for (i = 0; i < data->pci_dev.num_virtual_functions; i++)
VIR_FREE(data->pci_dev.virtual_functions[i]);
VIR_FREE(data->pci_dev.virtual_functions);
data->pci_dev.num_virtual_functions = 0;
data->pci_dev.max_virtual_functions = 0;
data->pci_dev.flags &= ~VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION;
data->pci_dev.flags &= ~VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION;
if (!virPCIGetPhysicalFunction(sysfsPath, &data->pci_dev.physical_function))
data->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION;
ret = virPCIGetVirtualFunctions(sysfsPath, &data->pci_dev.virtual_functions,
&data->pci_dev.num_virtual_functions,
&data->pci_dev.max_virtual_functions);
if (ret < 0)
return ret;
if (data->pci_dev.num_virtual_functions > 0 ||
data->pci_dev.max_virtual_functions > 0)
data->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION;
return ret;
}
static int
nodeDeviceSysfsGetPCIIOMMUGroupCaps(virNodeDevCapDataPtr data)
{
size_t i;
int tmpGroup, ret = -1;
virPCIDeviceAddress addr;
/* this could be a refresh, so clear out the old data */
for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++)
VIR_FREE(data->pci_dev.iommuGroupDevices[i]);
VIR_FREE(data->pci_dev.iommuGroupDevices);
data->pci_dev.nIommuGroupDevices = 0;
data->pci_dev.iommuGroupNumber = 0;
addr.domain = data->pci_dev.domain;
addr.bus = data->pci_dev.bus;
addr.slot = data->pci_dev.slot;
addr.function = data->pci_dev.function;
tmpGroup = virPCIDeviceAddressGetIOMMUGroupNum(&addr);
if (tmpGroup == -1) {
/* error was already reported */
goto cleanup;
}
if (tmpGroup == -2) {
/* -2 return means there is no iommu_group data */
ret = 0;
goto cleanup;
}
if (tmpGroup >= 0) {
if (virPCIDeviceAddressGetIOMMUGroupAddresses(&addr, &data->pci_dev.iommuGroupDevices,
&data->pci_dev.nIommuGroupDevices) < 0)
goto cleanup;
data->pci_dev.iommuGroupNumber = tmpGroup;
}
ret = 0;
cleanup:
return ret;
}
/* nodeDeviceSysfsGetPCIRelatedCaps() get info that is stored in sysfs
* about devices related to this device, i.e. things that can change
* without this device itself changing. These must be refreshed
* anytime full XML of the device is requested, because they can
* change with no corresponding notification from the kernel/udev.
*/
int
nodeDeviceSysfsGetPCIRelatedDevCaps(const char *sysfsPath,
virNodeDevCapDataPtr data)
{
if (nodeDeviceSysfsGetPCISRIOVCaps(sysfsPath, data) < 0)
return -1;
if (nodeDeviceSysfsGetPCIIOMMUGroupCaps(data) < 0)
return -1;
return 0;
}
#else
int
nodeDeviceSysfsGetSCSIHostCaps(virNodeDevCapDataPtr d ATTRIBUTE_UNUSED)
{
return -1;
}
#endif /* __linux__ */