2008-02-27 04:35:08 +00:00
|
|
|
/*
|
|
|
|
* capabilities.c: hypervisor capabilities
|
|
|
|
*
|
2015-09-17 14:16:55 +05:30
|
|
|
* Copyright (C) 2006-2015 Red Hat, Inc.
|
2008-02-27 04:35:08 +00:00
|
|
|
* Copyright (C) 2006-2008 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 18:06:23 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2008-02-27 04:35:08 +00:00
|
|
|
*/
|
|
|
|
|
2008-02-27 14:40:19 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
2017-03-07 10:40:15 +01:00
|
|
|
#include <unistd.h>
|
2011-11-22 09:45:47 +00:00
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
#include "capabilities.h"
|
2009-12-18 14:44:55 +01:00
|
|
|
#include "cpu_conf.h"
|
2017-03-07 10:40:15 +01:00
|
|
|
#include "domain_conf.h"
|
2019-01-10 07:19:35 -05:00
|
|
|
#include "storage_conf.h"
|
2017-03-07 10:40:15 +01:00
|
|
|
#include "viralloc.h"
|
|
|
|
#include "virarch.h"
|
|
|
|
#include "virbuffer.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2017-03-07 10:40:15 +01:00
|
|
|
#include "virfile.h"
|
|
|
|
#include "virhostcpu.h"
|
|
|
|
#include "virhostmem.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
#include "virnuma.h"
|
2013-05-03 14:40:46 +02:00
|
|
|
#include "virstring.h"
|
2017-03-07 10:40:15 +01:00
|
|
|
#include "virtypedparam.h"
|
|
|
|
#include "viruuid.h"
|
2019-04-01 12:14:26 +02:00
|
|
|
#include "virenum.h"
|
2020-02-16 22:59:28 +01:00
|
|
|
#include "virutil.h"
|
2011-11-22 11:31:22 +08:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_CAPABILITIES
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
#define SYSFS_SYSTEM_PATH "/sys/devices/system"
|
|
|
|
|
2019-01-20 11:32:42 -05:00
|
|
|
VIR_LOG_INIT("conf.capabilities");
|
2017-03-07 10:40:15 +01:00
|
|
|
|
2019-01-20 11:04:56 -05:00
|
|
|
VIR_ENUM_DECL(virCapsHostPMTarget);
|
2019-03-16 14:20:32 -04:00
|
|
|
VIR_ENUM_IMPL(virCapsHostPMTarget,
|
|
|
|
VIR_NODE_SUSPEND_TARGET_LAST,
|
2019-01-20 11:30:15 -05:00
|
|
|
"suspend_mem", "suspend_disk", "suspend_hybrid",
|
|
|
|
);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virClass *virCapsClass;
|
2018-04-13 14:56:40 +02:00
|
|
|
static void virCapsDispose(void *obj);
|
2013-02-01 12:26:18 +00:00
|
|
|
|
|
|
|
static int virCapabilitiesOnceInit(void)
|
|
|
|
{
|
2018-04-17 17:42:33 +02:00
|
|
|
if (!VIR_CLASS_NEW(virCaps, virClassForObject()))
|
2013-02-01 12:26:18 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-20 12:23:29 -05:00
|
|
|
VIR_ONCE_GLOBAL_INIT(virCapabilities);
|
2013-02-01 12:26:18 +00:00
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
/**
|
|
|
|
* virCapabilitiesNew:
|
2012-12-10 22:28:09 +00:00
|
|
|
* @hostarch: host machine architecture
|
2014-07-14 06:56:13 -06:00
|
|
|
* @offlineMigrate: true if offline migration is available
|
|
|
|
* @liveMigrate: true if live migration is available
|
2008-02-27 14:40:19 +00:00
|
|
|
*
|
2008-02-27 04:35:08 +00:00
|
|
|
* Allocate a new capabilities object
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virCaps *
|
2012-12-10 22:28:09 +00:00
|
|
|
virCapabilitiesNew(virArch hostarch,
|
2014-07-14 06:56:13 -06:00
|
|
|
bool offlineMigrate,
|
|
|
|
bool liveMigrate)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCaps *caps;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2013-02-01 12:26:18 +00:00
|
|
|
if (virCapabilitiesInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(caps = virObjectNew(virCapsClass)))
|
2008-05-29 15:28:28 +00:00
|
|
|
return NULL;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2012-12-10 22:28:09 +00:00
|
|
|
caps->host.arch = hostarch;
|
2008-02-27 04:35:08 +00:00
|
|
|
caps->host.offlineMigrate = offlineMigrate;
|
|
|
|
caps->host.liveMigrate = liveMigrate;
|
|
|
|
|
|
|
|
return caps;
|
|
|
|
}
|
|
|
|
|
2013-01-22 18:42:08 +01:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesClearHostNUMACellCPUTopology(virCapsHostNUMACellCPU *cpus,
|
2013-01-22 18:42:08 +01:00
|
|
|
size_t ncpus)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!cpus)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ncpus; i++) {
|
|
|
|
virBitmapFree(cpus[i].siblings);
|
|
|
|
cpus[i].siblings = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeHostNUMACell(virCapsHostNUMACell *cell)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2008-05-29 15:28:28 +00:00
|
|
|
if (cell == NULL)
|
|
|
|
return;
|
|
|
|
|
2013-01-22 18:42:08 +01:00
|
|
|
virCapabilitiesClearHostNUMACellCPUTopology(cell->cpus, cell->ncpus);
|
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(cell->cpus);
|
2021-05-03 12:06:42 +02:00
|
|
|
g_free(cell->distances);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(cell->pageinfo);
|
2021-04-29 18:58:43 +02:00
|
|
|
if (cell->caches)
|
|
|
|
g_array_unref(cell->caches);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(cell);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2009-07-23 18:31:34 +01:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeGuestMachine(virCapsGuestMachine *machine)
|
2009-07-23 18:31:34 +01:00
|
|
|
{
|
|
|
|
if (machine == NULL)
|
|
|
|
return;
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(machine->name);
|
|
|
|
g_free(machine->canonical);
|
|
|
|
g_free(machine);
|
2009-07-23 18:31:34 +01:00
|
|
|
}
|
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeGuestDomain(virCapsGuestDomain *dom)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-05-29 15:28:28 +00:00
|
|
|
if (dom == NULL)
|
|
|
|
return;
|
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(dom->info.emulator);
|
|
|
|
g_free(dom->info.loader);
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < dom->info.nmachines; i++)
|
2009-07-23 18:31:34 +01:00
|
|
|
virCapabilitiesFreeGuestMachine(dom->info.machines[i]);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(dom->info.machines);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(dom);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2017-03-15 13:01:13 +01:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeGuest(virCapsGuest *guest)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-05-29 15:28:28 +00:00
|
|
|
if (guest == NULL)
|
|
|
|
return;
|
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(guest->arch.defaultInfo.emulator);
|
|
|
|
g_free(guest->arch.defaultInfo.loader);
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < guest->arch.defaultInfo.nmachines; i++)
|
2009-07-23 18:31:34 +01:00
|
|
|
virCapabilitiesFreeGuestMachine(guest->arch.defaultInfo.machines[i]);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(guest->arch.defaultInfo.machines);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < guest->arch.ndomains; i++)
|
2008-02-27 04:35:08 +00:00
|
|
|
virCapabilitiesFreeGuestDomain(guest->arch.domains[i]);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(guest->arch.domains);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(guest);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeStoragePool(virCapsStoragePool *pool)
|
2019-01-10 07:19:35 -05:00
|
|
|
{
|
|
|
|
if (!pool)
|
|
|
|
return;
|
|
|
|
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(pool);
|
2019-01-10 07:19:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-29 10:41:56 +00:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAUnref(virCapsHostNUMA *caps)
|
2009-06-29 10:41:56 +00:00
|
|
|
{
|
2019-12-18 07:40:17 +01:00
|
|
|
if (!caps)
|
|
|
|
return;
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
if (g_atomic_int_dec_and_test(&caps->refs)) {
|
|
|
|
g_ptr_array_unref(caps->cells);
|
capabilities: Expose NUMA interconnects
Links between NUMA nodes can have different latencies and
bandwidths. This info is newly defined in ACPI 6.2 under
Heterogeneous Memory Attribute Table (HMAT) table. Linux kernel
learned how to report these values under sysfs and thus we can
expose them in our capabilities XML. The sysfs interface is
documented in kernel's Documentation/admin-guide/mm/numaperf.rst.
Long story short, two nodes can be in initiator-target
relationship. A node can be initiator if it has a CPU or a device
that's capable of initiating memory transfer. Therefore a node
that has just memory can only be target. An initiator-target link
can then have any combination of {bandwidth, latency} - {access,
read, write} attribute (6 in total). However, the standard says
access is applicable iff read and write values are the same.
Therefore, we really have just four combinations of attributes:
bandwidth-read, bandwidth-write, latency-read, latency-write.
This is the combination that kernel reports anyway.
Then, under /sys/system/devices/node/nodeX/acccessN/initiators we
find values for those 4 attributes and also symlinks named
"nodeN" which then represent initiators to nodeX. For instance:
/sys/system/node/node1/access1/initiators/node0 -> ../../node0
/sys/system/node/node1/access1/initiators/read_bandwidth
/sys/system/node/node1/access1/initiators/read_latency
/sys/system/node/node1/access1/initiators/write_bandwidth
/sys/system/node/node1/access1/initiators/write_latency
This means that node0 is initiator and node1 is target and values
of the interconnect can be read.
In theory, there can be separate links to memory side caches too
(e.g. one link from node X to node Y's main memory, another from
node X to node Y's L1 cache, another one to L2 cache and so on).
But sysfs does not express this relationship just yet.
The "accessN" means either "access0" or "access1". The difference
is that while the former expresses the best interconnect between
two nodes including CPUS and I/O devices (such as GPUs and NICs),
the latter includes only CPUs and thus is what we need.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1786309
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2021-05-17 13:36:34 +02:00
|
|
|
if (caps->interconnects)
|
|
|
|
g_array_unref(caps->interconnects);
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(caps);
|
2019-11-29 09:55:59 +00:00
|
|
|
}
|
|
|
|
}
|
2009-06-29 10:41:56 +00:00
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMARef(virCapsHostNUMA *caps)
|
2019-11-29 09:55:59 +00:00
|
|
|
{
|
|
|
|
g_atomic_int_inc(&caps->refs);
|
2009-06-29 10:41:56 +00:00
|
|
|
}
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostMemBWNodeFree(virCapsHostMemBWNode *ptr)
|
2018-07-30 11:12:41 +08:00
|
|
|
{
|
|
|
|
if (!ptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virBitmapFree(ptr->cpus);
|
2021-01-30 14:05:50 -05:00
|
|
|
g_free(ptr);
|
2018-07-30 11:12:41 +08:00
|
|
|
}
|
|
|
|
|
2013-10-18 14:13:21 +02:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesClearSecModel(virCapsHostSecModel *secmodel)
|
2013-10-18 14:13:21 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < secmodel->nlabels; i++) {
|
|
|
|
VIR_FREE(secmodel->labels[i].type);
|
|
|
|
VIR_FREE(secmodel->labels[i].label);
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(secmodel->labels);
|
|
|
|
VIR_FREE(secmodel->model);
|
|
|
|
VIR_FREE(secmodel->doi);
|
|
|
|
}
|
|
|
|
|
2013-02-01 12:26:18 +00:00
|
|
|
static void
|
2018-04-13 14:56:40 +02:00
|
|
|
virCapsDispose(void *object)
|
2013-02-01 12:26:18 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCaps *caps = object;
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
for (i = 0; i < caps->npools; i++)
|
|
|
|
virCapabilitiesFreeStoragePool(caps->pools[i]);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->pools);
|
2019-01-10 07:19:35 -05:00
|
|
|
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < caps->nguests; i++)
|
2008-02-27 04:35:08 +00:00
|
|
|
virCapabilitiesFreeGuest(caps->guests[i]);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->guests);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < caps->host.nfeatures; i++)
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.features[i]);
|
|
|
|
g_free(caps->host.features);
|
2009-06-29 10:41:56 +00:00
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
if (caps->host.numa)
|
|
|
|
virCapabilitiesHostNUMAUnref(caps->host.numa);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2013-05-21 15:21:17 +08:00
|
|
|
for (i = 0; i < caps->host.nmigrateTrans; i++)
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.migrateTrans[i]);
|
|
|
|
g_free(caps->host.migrateTrans);
|
2008-03-03 13:15:14 +00:00
|
|
|
|
2014-11-13 15:23:27 +01:00
|
|
|
for (i = 0; i < caps->host.nsecModels; i++)
|
2013-10-18 14:13:21 +02:00
|
|
|
virCapabilitiesClearSecModel(&caps->host.secModels[i]);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.secModels);
|
2012-08-15 19:10:35 -03:00
|
|
|
|
2018-09-20 18:10:48 +08:00
|
|
|
for (i = 0; i < caps->host.cache.nbanks; i++)
|
|
|
|
virCapsHostCacheBankFree(caps->host.cache.banks[i]);
|
2018-09-20 18:10:50 +08:00
|
|
|
virResctrlInfoMonFree(caps->host.cache.monitor);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.cache.banks);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
2018-09-20 18:10:49 +08:00
|
|
|
for (i = 0; i < caps->host.memBW.nnodes; i++)
|
|
|
|
virCapsHostMemBWNodeFree(caps->host.memBW.nodes[i]);
|
2018-09-20 18:10:50 +08:00
|
|
|
virResctrlInfoMonFree(caps->host.memBW.monitor);
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.memBW.nodes);
|
2018-07-30 11:12:41 +08:00
|
|
|
|
2021-02-03 15:15:35 -05:00
|
|
|
g_free(caps->host.netprefix);
|
|
|
|
g_free(caps->host.pagesSize);
|
2009-12-18 14:44:55 +01:00
|
|
|
virCPUDefFree(caps->host.cpu);
|
2017-11-20 13:43:30 +01:00
|
|
|
virObjectUnref(caps->host.resctrl);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCapabilitiesAddHostFeature:
|
|
|
|
* @caps: capabilities to extend
|
|
|
|
* @name: name of new feature
|
|
|
|
*
|
|
|
|
* Registers a new host CPU feature, eg 'pae', or 'vmx'
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddHostFeature(virCaps *caps,
|
2008-02-27 04:35:08 +00:00
|
|
|
const char *name)
|
|
|
|
{
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(caps->host.features, caps->host.nfeatures_max,
|
|
|
|
caps->host.nfeatures, 1);
|
2019-10-20 13:49:46 +02:00
|
|
|
caps->host.features[caps->host.nfeatures] = g_strdup(name);
|
2008-02-27 04:35:08 +00:00
|
|
|
caps->host.nfeatures++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCapabilitiesAddHostMigrateTransport:
|
|
|
|
* @caps: capabilities to extend
|
|
|
|
* @name: name of migration transport
|
|
|
|
*
|
|
|
|
* Registers a new domain migration transport URI
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddHostMigrateTransport(virCaps *caps,
|
2008-02-27 04:35:08 +00:00
|
|
|
const char *name)
|
|
|
|
{
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(caps->host.migrateTrans, caps->host.nmigrateTrans_max,
|
|
|
|
caps->host.nmigrateTrans, 1);
|
2019-10-20 13:49:46 +02:00
|
|
|
caps->host.migrateTrans[caps->host.nmigrateTrans] = g_strdup(name);
|
2008-02-27 04:35:08 +00:00
|
|
|
caps->host.nmigrateTrans++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-03 21:40:33 +00:00
|
|
|
/**
|
|
|
|
* virCapabilitiesSetNetPrefix:
|
|
|
|
* @caps: capabilities to extend
|
|
|
|
* @name: prefix for host generated network interfaces
|
|
|
|
*
|
|
|
|
* Registers the prefix that is used for generated network interfaces
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesSetNetPrefix(virCaps *caps,
|
2016-02-03 21:40:33 +00:00
|
|
|
const char *prefix)
|
|
|
|
{
|
2019-10-20 13:49:46 +02:00
|
|
|
caps->host.netprefix = g_strdup(prefix);
|
2016-02-03 21:40:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
|
|
|
|
/**
|
2019-11-29 09:55:59 +00:00
|
|
|
* virCapabilitiesHostNUMAAddCell:
|
2008-02-27 04:35:08 +00:00
|
|
|
* @caps: capabilities to extend
|
|
|
|
* @num: ID number of NUMA cell
|
2013-10-18 14:53:49 +02:00
|
|
|
* @mem: Total size of memory in the NUMA node (in KiB)
|
2014-06-03 15:18:27 +02:00
|
|
|
* @ncpus: number of CPUs in cell
|
2021-04-29 18:58:40 +02:00
|
|
|
* @cpus: array of CPU definition structures
|
2021-05-03 12:06:42 +02:00
|
|
|
* @ndistances: number of sibling NUMA nodes
|
|
|
|
* @distances: NUMA distances to other nodes
|
2014-06-06 18:12:51 +02:00
|
|
|
* @npageinfo: number of pages at node @num
|
|
|
|
* @pageinfo: info on each single memory page
|
2021-04-29 18:58:43 +02:00
|
|
|
* @caches: info on memory side caches
|
2008-02-27 04:35:08 +00:00
|
|
|
*
|
2021-04-29 18:58:40 +02:00
|
|
|
* Registers a new NUMA cell for a host, passing in a array of
|
|
|
|
* CPU IDs belonging to the cell, distances to other NUMA nodes
|
|
|
|
* and info on hugepages on the node.
|
|
|
|
*
|
|
|
|
* All pointers are stolen.
|
2008-02-27 04:35:08 +00:00
|
|
|
*/
|
2019-11-29 09:55:59 +00:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAAddCell(virCapsHostNUMA *caps,
|
2008-02-27 04:35:08 +00:00
|
|
|
int num,
|
2013-03-07 11:03:36 -05:00
|
|
|
unsigned long long mem,
|
2014-06-03 15:18:27 +02:00
|
|
|
int ncpus,
|
2021-04-29 18:58:40 +02:00
|
|
|
virCapsHostNUMACellCPU **cpus,
|
2021-05-03 12:06:42 +02:00
|
|
|
int ndistances,
|
2021-05-03 11:04:45 +02:00
|
|
|
virNumaDistance **distances,
|
2014-06-06 18:12:51 +02:00
|
|
|
int npageinfo,
|
2021-04-29 18:58:43 +02:00
|
|
|
virCapsHostNUMACellPageInfo **pageinfo,
|
|
|
|
GArray **caches)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACell *cell = g_new0(virCapsHostNUMACell, 1);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2008-07-07 09:52:26 +00:00
|
|
|
cell->num = num;
|
2013-03-07 11:03:36 -05:00
|
|
|
cell->mem = mem;
|
2021-04-29 18:58:40 +02:00
|
|
|
if (cpus) {
|
|
|
|
cell->ncpus = ncpus;
|
|
|
|
cell->cpus = g_steal_pointer(cpus);
|
|
|
|
}
|
2021-05-03 12:06:42 +02:00
|
|
|
if (distances) {
|
|
|
|
cell->ndistances = ndistances;
|
|
|
|
cell->distances = g_steal_pointer(distances);
|
2021-04-29 18:58:40 +02:00
|
|
|
}
|
|
|
|
if (pageinfo) {
|
|
|
|
cell->npageinfo = npageinfo;
|
|
|
|
cell->pageinfo = g_steal_pointer(pageinfo);
|
|
|
|
}
|
2021-04-29 18:58:43 +02:00
|
|
|
if (caches) {
|
|
|
|
cell->caches = g_steal_pointer(caches);
|
|
|
|
}
|
2008-07-07 09:52:26 +00:00
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
g_ptr_array_add(caps->cells, cell);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2009-07-23 18:31:34 +01:00
|
|
|
/**
|
|
|
|
* virCapabilitiesAllocMachines:
|
|
|
|
* @machines: machine variants for emulator ('pc', or 'isapc', etc)
|
|
|
|
* @nmachines: number of machine variants for emulator
|
|
|
|
*
|
2021-03-11 08:16:13 +01:00
|
|
|
* Allocate a table of virCapsGuestMachine *from the supplied table
|
2009-07-23 18:31:34 +01:00
|
|
|
* of machine names.
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine **
|
2009-07-23 18:31:34 +01:00
|
|
|
virCapabilitiesAllocMachines(const char *const *names, int nnames)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine **machines;
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-07-23 18:31:34 +01:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
machines = g_new0(virCapsGuestMachine *, nnames);
|
2009-07-23 18:31:34 +01:00
|
|
|
|
|
|
|
for (i = 0; i < nnames; i++) {
|
2020-10-07 21:12:01 +02:00
|
|
|
machines[i] = g_new0(virCapsGuestMachine, 1);
|
2019-10-20 13:49:46 +02:00
|
|
|
machines[i]->name = g_strdup(names[i]);
|
2009-07-23 18:31:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return machines;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCapabilitiesFreeMachines:
|
|
|
|
* @machines: table of vircapsGuestMachinePtr
|
|
|
|
*
|
2021-03-11 08:16:13 +01:00
|
|
|
* Free a table of virCapsGuestMachine *
|
2009-07-23 18:31:34 +01:00
|
|
|
*/
|
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFreeMachines(virCapsGuestMachine **machines,
|
2009-07-23 18:31:34 +01:00
|
|
|
int nmachines)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-07-23 18:31:34 +01:00
|
|
|
if (!machines)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < nmachines && machines[i]; i++) {
|
|
|
|
virCapabilitiesFreeGuestMachine(machines[i]);
|
|
|
|
machines[i] = NULL;
|
|
|
|
}
|
2021-02-09 12:20:57 -05:00
|
|
|
g_free(machines);
|
2009-07-23 18:31:34 +01:00
|
|
|
}
|
2008-02-27 04:35:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virCapabilitiesAddGuest:
|
|
|
|
* @caps: capabilities to extend
|
2015-04-17 18:09:16 -04:00
|
|
|
* @ostype: guest operating system type, of enum VIR_DOMAIN_OSTYPE
|
2012-12-10 22:28:09 +00:00
|
|
|
* @arch: guest CPU architecture
|
2008-02-27 04:35:08 +00:00
|
|
|
* @wordsize: number of bits in CPU word
|
|
|
|
* @emulator: path to default device emulator for arch/ostype
|
|
|
|
* @loader: path to default BIOS loader for arch/ostype
|
|
|
|
* @nmachines: number of machine variants for emulator
|
|
|
|
* @machines: machine variants for emulator ('pc', or 'isapc', etc)
|
|
|
|
*
|
|
|
|
* Registers a new guest operating system. This should be
|
|
|
|
* followed by registration of at least one domain for
|
|
|
|
* running the guest
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuest *
|
|
|
|
virCapabilitiesAddGuest(virCaps *caps,
|
2015-04-17 18:09:16 -04:00
|
|
|
int ostype,
|
2012-12-10 22:28:09 +00:00
|
|
|
virArch arch,
|
2008-02-27 04:35:08 +00:00
|
|
|
const char *emulator,
|
|
|
|
const char *loader,
|
|
|
|
int nmachines,
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine **machines)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuest *guest;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
guest = g_new0(virCapsGuest, 1);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2015-04-16 19:18:32 -04:00
|
|
|
guest->ostype = ostype;
|
2012-12-10 22:28:09 +00:00
|
|
|
guest->arch.id = arch;
|
|
|
|
guest->arch.wordsize = virArchGetWordSize(arch);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
guest->arch.defaultInfo.emulator = g_strdup(emulator);
|
|
|
|
guest->arch.defaultInfo.loader = g_strdup(loader);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(caps->guests, caps->nguests_max, caps->nguests, 1);
|
2010-08-17 15:41:51 -06:00
|
|
|
caps->guests[caps->nguests++] = guest;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2009-09-02 15:09:04 +01:00
|
|
|
if (nmachines) {
|
|
|
|
guest->arch.defaultInfo.nmachines = nmachines;
|
|
|
|
guest->arch.defaultInfo.machines = machines;
|
|
|
|
}
|
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
return guest;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCapabilitiesAddGuestDomain:
|
|
|
|
* @guest: guest to support
|
|
|
|
* @hvtype: hypervisor type ('xen', 'qemu', 'kvm')
|
|
|
|
* @emulator: specialized device emulator for domain
|
|
|
|
* @loader: specialized BIOS loader for domain
|
|
|
|
* @nmachines: number of machine variants for emulator
|
|
|
|
* @machines: specialized machine variants for emulator
|
|
|
|
*
|
|
|
|
* Registers a virtual domain capable of running a
|
|
|
|
* guest operating system
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestDomain *
|
|
|
|
virCapabilitiesAddGuestDomain(virCapsGuest *guest,
|
2015-04-17 18:38:10 -04:00
|
|
|
int hvtype,
|
2008-02-27 04:35:08 +00:00
|
|
|
const char *emulator,
|
|
|
|
const char *loader,
|
|
|
|
int nmachines,
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine **machines)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestDomain *dom;
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
dom = g_new0(virCapsGuestDomain, 1);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2015-04-17 18:38:10 -04:00
|
|
|
dom->type = hvtype;
|
2019-10-20 13:49:46 +02:00
|
|
|
dom->info.emulator = g_strdup(emulator);
|
|
|
|
dom->info.loader = g_strdup(loader);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(guest->arch.domains, guest->arch.ndomains_max,
|
|
|
|
guest->arch.ndomains, 1);
|
2008-02-27 04:35:08 +00:00
|
|
|
guest->arch.domains[guest->arch.ndomains] = dom;
|
|
|
|
guest->arch.ndomains++;
|
|
|
|
|
2009-09-02 15:09:04 +01:00
|
|
|
if (nmachines) {
|
|
|
|
dom->info.nmachines = nmachines;
|
|
|
|
dom->info.machines = machines;
|
|
|
|
}
|
2008-02-27 04:35:08 +00:00
|
|
|
|
|
|
|
return dom;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-23 17:04:20 +02:00
|
|
|
struct virCapsGuestFeatureInfo {
|
|
|
|
const char *name;
|
|
|
|
bool togglesRequired;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct virCapsGuestFeatureInfo virCapsGuestFeatureInfos[VIR_CAPS_GUEST_FEATURE_TYPE_LAST] = {
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_PAE] = { "pae", false },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_NONPAE] = { "nonpae", false },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_IA64_BE] = { "ia64_be", false },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_ACPI] = { "acpi", true },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_APIC] = { "apic", true },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_CPUSELECTION] = { "cpuselection", false },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_DEVICEBOOT] = { "deviceboot", false },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_DISKSNAPSHOT] = { "disksnapshot", true },
|
|
|
|
[VIR_CAPS_GUEST_FEATURE_TYPE_HAP] = { "hap", true },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-11-12 13:57:23 +01:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddGuestFeatureInternal(virCapsGuest *guest,
|
2019-11-12 13:57:23 +01:00
|
|
|
virCapsGuestFeatureType feature,
|
|
|
|
bool defaultOn,
|
|
|
|
bool toggle)
|
|
|
|
{
|
|
|
|
guest->features[feature].present = true;
|
|
|
|
|
|
|
|
if (virCapsGuestFeatureInfos[feature].togglesRequired) {
|
|
|
|
guest->features[feature].defaultOn = virTristateSwitchFromBool(defaultOn);
|
|
|
|
guest->features[feature].toggle = virTristateBoolFromBool(toggle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 04:35:08 +00:00
|
|
|
/**
|
|
|
|
* virCapabilitiesAddGuestFeature:
|
|
|
|
* @guest: guest to associate feature with
|
2019-11-12 13:57:23 +01:00
|
|
|
* @feature: feature to add
|
2008-02-27 04:35:08 +00:00
|
|
|
*
|
2014-07-14 06:56:13 -06:00
|
|
|
* Registers a feature for a guest domain.
|
2008-02-27 04:35:08 +00:00
|
|
|
*/
|
2019-11-12 13:57:23 +01:00
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddGuestFeature(virCapsGuest *guest,
|
2019-11-12 13:57:23 +01:00
|
|
|
virCapsGuestFeatureType feature)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2019-11-12 13:57:23 +01:00
|
|
|
virCapabilitiesAddGuestFeatureInternal(guest, feature, false, false);
|
|
|
|
}
|
2008-02-27 04:35:08 +00:00
|
|
|
|
|
|
|
|
2019-11-12 13:57:23 +01:00
|
|
|
/**
|
|
|
|
* virCapabilitiesAddGuestFeatureWithToggle:
|
|
|
|
* @guest: guest to associate feature with
|
|
|
|
* @feature: feature to add
|
|
|
|
* @defaultOn: true if it defaults to on
|
|
|
|
* @toggle: true if its state can be toggled
|
|
|
|
*
|
|
|
|
* Registers a feature with toggles for a guest domain.
|
|
|
|
*/
|
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddGuestFeatureWithToggle(virCapsGuest *guest,
|
2019-11-12 13:57:23 +01:00
|
|
|
virCapsGuestFeatureType feature,
|
|
|
|
bool defaultOn,
|
|
|
|
bool toggle)
|
|
|
|
{
|
|
|
|
virCapabilitiesAddGuestFeatureInternal(guest, feature, defaultOn, toggle);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 13:57:23 +01:00
|
|
|
|
2013-10-18 14:13:21 +02:00
|
|
|
/**
|
|
|
|
* virCapabilitiesHostSecModelAddBaseLabel
|
|
|
|
* @secmodel: Security model to add a base label for
|
|
|
|
* @type: virtualization type
|
|
|
|
* @label: base label
|
|
|
|
*
|
|
|
|
* Returns non-zero on error.
|
|
|
|
*/
|
|
|
|
extern int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostSecModelAddBaseLabel(virCapsHostSecModel *secmodel,
|
2013-10-18 14:13:21 +02:00
|
|
|
const char *type,
|
|
|
|
const char *label)
|
|
|
|
{
|
|
|
|
if (type == NULL || label == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2021-03-20 00:37:03 +01:00
|
|
|
VIR_EXPAND_N(secmodel->labels, secmodel->nlabels, 1);
|
2019-12-20 13:43:11 +01:00
|
|
|
secmodel->labels[secmodel->nlabels - 1].type = g_strdup(type);
|
|
|
|
secmodel->labels[secmodel->nlabels - 1].label = g_strdup(label);
|
2013-10-18 14:13:21 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-17 12:38:55 -04:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virCapsDomainData *
|
|
|
|
virCapabilitiesDomainDataLookupInternal(virCaps *caps,
|
2015-05-06 18:32:05 -04:00
|
|
|
int ostype,
|
|
|
|
virArch arch,
|
2015-09-17 14:16:55 +05:30
|
|
|
virDomainVirtType domaintype,
|
2015-05-06 18:32:05 -04:00
|
|
|
const char *emulator,
|
|
|
|
const char *machinetype)
|
2015-04-17 12:38:55 -04:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuest *foundguest = NULL;
|
|
|
|
virCapsGuestDomain *founddomain = NULL;
|
|
|
|
virCapsGuestMachine *foundmachine = NULL;
|
|
|
|
virCapsDomainData *ret = NULL;
|
2015-04-17 12:38:55 -04:00
|
|
|
size_t i, j, k;
|
|
|
|
|
2018-08-02 17:12:58 +01:00
|
|
|
VIR_DEBUG("Lookup ostype=%d arch=%d domaintype=%d emulator=%s machine=%s",
|
|
|
|
ostype, arch, domaintype, NULLSTR(emulator), NULLSTR(machinetype));
|
2015-04-17 12:38:55 -04:00
|
|
|
for (i = 0; i < caps->nguests; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuest *guest = caps->guests[i];
|
2015-04-17 12:38:55 -04:00
|
|
|
|
2018-08-02 17:12:58 +01:00
|
|
|
if (ostype != -1 && guest->ostype != ostype) {
|
|
|
|
VIR_DEBUG("Skip os type want=%d vs got=%d", ostype, guest->ostype);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Match os type %d", ostype);
|
|
|
|
|
|
|
|
if ((arch != VIR_ARCH_NONE) && (guest->arch.id != arch)) {
|
|
|
|
VIR_DEBUG("Skip arch want=%d vs got=%d", arch, guest->arch.id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Match arch %d", arch);
|
|
|
|
|
2015-04-17 12:38:55 -04:00
|
|
|
for (j = 0; j < guest->arch.ndomains; j++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestDomain *domain = guest->arch.domains[j];
|
|
|
|
virCapsGuestMachine **machinelist;
|
2015-04-17 12:38:55 -04:00
|
|
|
int nmachines;
|
2018-08-02 17:12:58 +01:00
|
|
|
const char *check_emulator = NULL;
|
|
|
|
|
|
|
|
if (domaintype != VIR_DOMAIN_VIRT_NONE &&
|
|
|
|
(domain->type != domaintype)) {
|
|
|
|
VIR_DEBUG("Skip domain type want=%d vs got=%d", domaintype, domain->type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Match domain type %d", domaintype);
|
|
|
|
|
|
|
|
check_emulator = domain->info.emulator;
|
|
|
|
if (!check_emulator)
|
|
|
|
check_emulator = guest->arch.defaultInfo.emulator;
|
|
|
|
if (emulator && STRNEQ_NULLABLE(check_emulator, emulator)) {
|
|
|
|
VIR_DEBUG("Skip emulator got=%s vs want=%s",
|
|
|
|
emulator, NULLSTR(check_emulator));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Match emulator %s", NULLSTR(emulator));
|
2015-04-17 12:38:55 -04:00
|
|
|
|
|
|
|
if (domain->info.nmachines) {
|
|
|
|
nmachines = domain->info.nmachines;
|
|
|
|
machinelist = domain->info.machines;
|
|
|
|
} else {
|
|
|
|
nmachines = guest->arch.defaultInfo.nmachines;
|
|
|
|
machinelist = guest->arch.defaultInfo.machines;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (k = 0; k < nmachines; k++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine *machine = machinelist[k];
|
2018-08-02 17:12:58 +01:00
|
|
|
|
|
|
|
if (machinetype &&
|
|
|
|
STRNEQ(machine->name, machinetype) &&
|
|
|
|
STRNEQ_NULLABLE(machine->canonical, machinetype)) {
|
|
|
|
VIR_DEBUG("Skip machine type want=%s vs got=%s got=%s",
|
|
|
|
machinetype, machine->name, NULLSTR(machine->canonical));
|
2015-04-17 12:38:55 -04:00
|
|
|
continue;
|
2018-08-02 17:12:58 +01:00
|
|
|
}
|
2018-09-17 17:06:30 +02:00
|
|
|
VIR_DEBUG("Match machine type machine %s", NULLSTR(machinetype));
|
2015-04-17 12:38:55 -04:00
|
|
|
|
|
|
|
foundmachine = machine;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-02 17:12:58 +01:00
|
|
|
if (!foundmachine && nmachines)
|
|
|
|
continue;
|
2015-04-17 12:38:55 -04:00
|
|
|
|
|
|
|
founddomain = domain;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-02 17:12:58 +01:00
|
|
|
if (!founddomain)
|
|
|
|
continue;
|
2015-04-17 12:38:55 -04:00
|
|
|
|
|
|
|
foundguest = guest;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX check default_emulator, see how it uses this */
|
|
|
|
if (!foundguest) {
|
2020-07-02 22:19:01 -04:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2015-04-17 12:38:55 -04:00
|
|
|
if (ostype)
|
|
|
|
virBufferAsprintf(&buf, "ostype=%s ",
|
|
|
|
virDomainOSTypeToString(ostype));
|
|
|
|
if (arch)
|
|
|
|
virBufferAsprintf(&buf, "arch=%s ", virArchToString(arch));
|
2015-09-17 14:16:55 +05:30
|
|
|
if (domaintype > VIR_DOMAIN_VIRT_NONE)
|
2015-04-17 12:38:55 -04:00
|
|
|
virBufferAsprintf(&buf, "domaintype=%s ",
|
|
|
|
virDomainVirtTypeToString(domaintype));
|
|
|
|
if (emulator)
|
2015-05-05 16:52:46 +02:00
|
|
|
virBufferEscapeString(&buf, "emulator=%s ", emulator);
|
2015-04-17 12:38:55 -04:00
|
|
|
if (machinetype)
|
2015-05-05 16:52:46 +02:00
|
|
|
virBufferEscapeString(&buf, "machine=%s ", machinetype);
|
2015-04-17 12:38:55 -04:00
|
|
|
if (virBufferCurrentContent(&buf) &&
|
|
|
|
!virBufferCurrentContent(&buf)[0])
|
|
|
|
virBufferAsprintf(&buf, "%s", _("any configuration"));
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("could not find capabilities for %s"),
|
2015-04-27 14:41:43 +08:00
|
|
|
virBufferCurrentContent(&buf));
|
2020-01-06 18:57:26 -03:00
|
|
|
return ret;
|
2015-04-17 12:38:55 -04:00
|
|
|
}
|
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
ret = g_new0(virCapsDomainData, 1);
|
2015-04-17 12:38:55 -04:00
|
|
|
|
|
|
|
ret->ostype = foundguest->ostype;
|
|
|
|
ret->arch = foundguest->arch.id;
|
|
|
|
if (founddomain) {
|
|
|
|
ret->domaintype = founddomain->type;
|
|
|
|
ret->emulator = founddomain->info.emulator;
|
|
|
|
}
|
|
|
|
if (!ret->emulator)
|
|
|
|
ret->emulator = foundguest->arch.defaultInfo.emulator;
|
|
|
|
if (foundmachine)
|
|
|
|
ret->machinetype = foundmachine->name;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-05-06 18:32:05 -04:00
|
|
|
/**
|
|
|
|
* virCapabilitiesDomainDataLookup:
|
|
|
|
* @caps: capabilities to query
|
|
|
|
* @ostype: guest operating system type, of enum VIR_DOMAIN_OSTYPE
|
|
|
|
* @arch: Architecture to search for
|
2015-09-17 14:16:55 +05:30
|
|
|
* @domaintype: domain type to search for, of enum virDomainVirtType
|
2015-05-06 18:32:05 -04:00
|
|
|
* @emulator: Emulator path to search for
|
|
|
|
* @machinetype: Machine type to search for
|
|
|
|
*
|
|
|
|
* Search capabilities for the passed values, and if found return
|
|
|
|
* virCapabilitiesDomainDataLookup filled in with the default values
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsDomainData *
|
|
|
|
virCapabilitiesDomainDataLookup(virCaps *caps,
|
2015-05-06 18:32:05 -04:00
|
|
|
int ostype,
|
|
|
|
virArch arch,
|
|
|
|
int domaintype,
|
|
|
|
const char *emulator,
|
|
|
|
const char *machinetype)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsDomainData *ret;
|
2015-05-06 18:32:05 -04:00
|
|
|
|
|
|
|
if (arch == VIR_ARCH_NONE) {
|
|
|
|
/* Prefer host arch if its available */
|
|
|
|
ret = virCapabilitiesDomainDataLookupInternal(caps, ostype,
|
|
|
|
caps->host.arch,
|
|
|
|
domaintype,
|
|
|
|
emulator, machinetype);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return virCapabilitiesDomainDataLookupInternal(caps, ostype,
|
|
|
|
arch, domaintype,
|
|
|
|
emulator, machinetype);
|
|
|
|
}
|
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
|
2019-11-26 16:09:33 +00:00
|
|
|
bool
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesDomainSupported(virCaps *caps,
|
2019-11-26 16:09:33 +00:00
|
|
|
int ostype,
|
|
|
|
virArch arch,
|
|
|
|
int virttype)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
g_autofree virCapsDomainData *capsdata = NULL;
|
2019-11-26 16:09:33 +00:00
|
|
|
|
|
|
|
capsdata = virCapabilitiesDomainDataLookup(caps, ostype,
|
|
|
|
arch,
|
|
|
|
virttype,
|
|
|
|
NULL, NULL);
|
|
|
|
|
|
|
|
return capsdata != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesAddStoragePool(virCaps *caps,
|
2019-01-10 07:19:35 -05:00
|
|
|
int poolType)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsStoragePool *pool;
|
2019-01-10 07:19:35 -05:00
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
pool = g_new0(virCapsStoragePool, 1);
|
2019-01-10 07:19:35 -05:00
|
|
|
|
|
|
|
pool->type = poolType;
|
|
|
|
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(caps->pools, caps->npools_max, caps->npools, 1);
|
2019-01-10 07:19:35 -05:00
|
|
|
caps->pools[caps->npools++] = pool;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-26 15:39:38 +02:00
|
|
|
static int
|
|
|
|
virCapsHostNUMACellCPUFormat(virBuffer *buf,
|
|
|
|
const virCapsHostNUMACellCPU *cpus,
|
|
|
|
int ncpus)
|
|
|
|
{
|
|
|
|
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
|
|
|
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
virBufferAsprintf(&attrBuf, " num='%d'", ncpus);
|
|
|
|
|
|
|
|
for (j = 0; j < ncpus; j++) {
|
|
|
|
virBufferAsprintf(&childBuf, "<cpu id='%d'", cpus[j].id);
|
|
|
|
|
|
|
|
if (cpus[j].siblings) {
|
|
|
|
g_autofree char *siblings = NULL;
|
|
|
|
|
|
|
|
if (!(siblings = virBitmapFormat(cpus[j].siblings)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virBufferAsprintf(&childBuf,
|
|
|
|
" socket_id='%d' die_id='%d' core_id='%d' siblings='%s'",
|
|
|
|
cpus[j].socket_id,
|
|
|
|
cpus[j].die_id,
|
|
|
|
cpus[j].core_id,
|
|
|
|
siblings);
|
|
|
|
}
|
|
|
|
virBufferAddLit(&childBuf, "/>\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
virXMLFormatElement(buf, "cpus", &attrBuf, &childBuf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-18 23:06:55 +01:00
|
|
|
static int
|
2021-05-03 15:25:19 +02:00
|
|
|
virCapabilitiesHostNUMAFormat(virBuffer *buf,
|
|
|
|
virCapsHostNUMA *caps)
|
2013-01-18 20:39:00 +01:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/conf/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2013-01-18 20:39:00 +01:00
|
|
|
|
2021-05-03 15:25:19 +02:00
|
|
|
if (!caps)
|
|
|
|
return 0;
|
|
|
|
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAddLit(buf, "<topology>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-11-29 09:55:59 +00:00
|
|
|
virBufferAsprintf(buf, "<cells num='%d'>\n", caps->cells->len);
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-11-29 09:55:59 +00:00
|
|
|
for (i = 0; i < caps->cells->len; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACell *cell = g_ptr_array_index(caps->cells, i);
|
2021-05-14 17:26:32 +02:00
|
|
|
size_t j;
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
virBufferAsprintf(buf, "<cell id='%d'>\n", cell->num);
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(buf, 2);
|
2013-03-07 11:03:36 -05:00
|
|
|
|
|
|
|
/* Print out the numacell memory total if it is available */
|
2019-11-29 09:55:59 +00:00
|
|
|
if (cell->mem)
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAsprintf(buf, "<memory unit='KiB'>%llu</memory>\n",
|
2019-11-29 09:55:59 +00:00
|
|
|
cell->mem);
|
2013-03-07 11:03:36 -05:00
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
for (j = 0; j < cell->npageinfo; j++) {
|
2018-04-23 16:36:53 +02:00
|
|
|
virBufferAsprintf(buf, "<pages unit='KiB' size='%u'>%llu</pages>\n",
|
2019-11-29 09:55:59 +00:00
|
|
|
cell->pageinfo[j].size,
|
|
|
|
cell->pageinfo[j].avail);
|
2014-06-06 18:12:51 +02:00
|
|
|
}
|
|
|
|
|
2021-05-03 11:04:45 +02:00
|
|
|
virNumaDistanceFormat(buf, cell->distances, cell->ndistances);
|
2014-06-03 15:18:27 +02:00
|
|
|
|
2021-04-29 18:58:43 +02:00
|
|
|
if (cell->caches) {
|
|
|
|
virNumaCache *caches = &g_array_index(cell->caches, virNumaCache, 0);
|
|
|
|
virNumaCacheFormat(buf, caches, cell->caches->len);
|
|
|
|
}
|
|
|
|
|
2021-05-26 15:39:38 +02:00
|
|
|
if (virCapsHostNUMACellCPUFormat(buf, cell->cpus, cell->ncpus) < 0)
|
|
|
|
return -1;
|
2013-01-18 23:06:55 +01:00
|
|
|
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</cell>\n");
|
2013-01-18 20:39:00 +01:00
|
|
|
}
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</cells>\n");
|
capabilities: Expose NUMA interconnects
Links between NUMA nodes can have different latencies and
bandwidths. This info is newly defined in ACPI 6.2 under
Heterogeneous Memory Attribute Table (HMAT) table. Linux kernel
learned how to report these values under sysfs and thus we can
expose them in our capabilities XML. The sysfs interface is
documented in kernel's Documentation/admin-guide/mm/numaperf.rst.
Long story short, two nodes can be in initiator-target
relationship. A node can be initiator if it has a CPU or a device
that's capable of initiating memory transfer. Therefore a node
that has just memory can only be target. An initiator-target link
can then have any combination of {bandwidth, latency} - {access,
read, write} attribute (6 in total). However, the standard says
access is applicable iff read and write values are the same.
Therefore, we really have just four combinations of attributes:
bandwidth-read, bandwidth-write, latency-read, latency-write.
This is the combination that kernel reports anyway.
Then, under /sys/system/devices/node/nodeX/acccessN/initiators we
find values for those 4 attributes and also symlinks named
"nodeN" which then represent initiators to nodeX. For instance:
/sys/system/node/node1/access1/initiators/node0 -> ../../node0
/sys/system/node/node1/access1/initiators/read_bandwidth
/sys/system/node/node1/access1/initiators/read_latency
/sys/system/node/node1/access1/initiators/write_bandwidth
/sys/system/node/node1/access1/initiators/write_latency
This means that node0 is initiator and node1 is target and values
of the interconnect can be read.
In theory, there can be separate links to memory side caches too
(e.g. one link from node X to node Y's main memory, another from
node X to node Y's L1 cache, another one to L2 cache and so on).
But sysfs does not express this relationship just yet.
The "accessN" means either "access0" or "access1". The difference
is that while the former expresses the best interconnect between
two nodes including CPUS and I/O devices (such as GPUs and NICs),
the latter includes only CPUs and thus is what we need.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1786309
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2021-05-17 13:36:34 +02:00
|
|
|
|
|
|
|
if (caps->interconnects) {
|
|
|
|
const virNumaInterconnect *interconnects;
|
|
|
|
interconnects = &g_array_index(caps->interconnects, virNumaInterconnect, 0);
|
|
|
|
virNumaInterconnectFormat(buf, interconnects, caps->interconnects->len);
|
|
|
|
}
|
|
|
|
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</topology>\n");
|
2013-01-18 23:06:55 +01:00
|
|
|
return 0;
|
2013-01-18 20:39:00 +01:00
|
|
|
}
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2018-09-20 18:10:50 +08:00
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatResctrlMonitor(virBuffer *buf,
|
|
|
|
virResctrlInfoMon *monitor)
|
2018-09-20 18:10:50 +08:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
2020-07-02 22:19:01 -04:00
|
|
|
g_auto(virBuffer) childrenBuf = VIR_BUFFER_INIT_CHILD(buf);
|
2018-09-20 18:10:50 +08:00
|
|
|
|
|
|
|
/* monitor not supported, no capability */
|
|
|
|
if (!monitor)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* no feature found in monitor means no capability, return */
|
|
|
|
if (monitor->nfeatures == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<monitor ");
|
|
|
|
|
|
|
|
/* CMT might not enabled, if enabled show related attributes. */
|
|
|
|
if (monitor->type == VIR_RESCTRL_MONITOR_TYPE_CACHE)
|
|
|
|
virBufferAsprintf(buf,
|
|
|
|
"level='%u' reuseThreshold='%u' ",
|
|
|
|
monitor->cache_level,
|
|
|
|
monitor->cache_reuse_threshold);
|
|
|
|
virBufferAsprintf(buf,
|
|
|
|
"maxMonitors='%u'>\n",
|
|
|
|
monitor->max_monitor);
|
|
|
|
|
|
|
|
for (i = 0; i < monitor->nfeatures; i++) {
|
|
|
|
virBufferAsprintf(&childrenBuf,
|
|
|
|
"<feature name='%s'/>\n",
|
|
|
|
monitor->features[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddBuffer(buf, &childrenBuf);
|
|
|
|
virBufferAddLit(buf, "</monitor>\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatCaches(virBuffer *buf,
|
|
|
|
virCapsHostCache *cache)
|
2017-03-30 15:01:27 +02:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
2017-05-17 17:08:33 +08:00
|
|
|
size_t j = 0;
|
2017-03-30 15:01:27 +02:00
|
|
|
|
2019-11-14 01:08:20 +08:00
|
|
|
if (!cache->nbanks && !cache->monitor)
|
2017-03-30 15:01:27 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<cache>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
|
2018-09-20 18:10:48 +08:00
|
|
|
for (i = 0; i < cache->nbanks; i++) {
|
2019-10-25 16:16:41 +02:00
|
|
|
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
2019-10-30 12:40:06 +01:00
|
|
|
g_auto(virBuffer) childrenBuf = VIR_BUFFER_INIT_CHILD(buf);
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBank *bank = cache->banks[i];
|
2019-10-25 16:11:34 +02:00
|
|
|
g_autofree char *cpus_str = virBitmapFormat(bank->cpus);
|
2017-11-10 13:19:53 +01:00
|
|
|
const char *unit = NULL;
|
|
|
|
unsigned long long short_size = virFormatIntPretty(bank->size, &unit);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
|
|
|
if (!cpus_str)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let's just *hope* the size is aligned to KiBs so that it does not
|
|
|
|
* bite is back in the future
|
|
|
|
*/
|
2019-10-25 16:16:41 +02:00
|
|
|
virBufferAsprintf(&attrBuf,
|
|
|
|
" id='%u' level='%u' type='%s' "
|
2017-05-17 17:08:33 +08:00
|
|
|
"size='%llu' unit='%s' cpus='%s'",
|
2017-03-30 15:01:27 +02:00
|
|
|
bank->id, bank->level,
|
|
|
|
virCacheTypeToString(bank->type),
|
2017-11-10 13:19:53 +01:00
|
|
|
short_size, unit, cpus_str);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
2017-05-17 17:08:33 +08:00
|
|
|
for (j = 0; j < bank->ncontrols; j++) {
|
2017-11-10 13:19:53 +01:00
|
|
|
const char *min_unit;
|
2021-03-11 08:16:13 +01:00
|
|
|
virResctrlInfoPerCache *controls = bank->controls[j];
|
2017-11-10 13:19:53 +01:00
|
|
|
unsigned long long gran_short_size = controls->granularity;
|
|
|
|
unsigned long long min_short_size = controls->min;
|
|
|
|
|
|
|
|
gran_short_size = virFormatIntPretty(gran_short_size, &unit);
|
|
|
|
min_short_size = virFormatIntPretty(min_short_size, &min_unit);
|
|
|
|
|
|
|
|
/* Only use the smaller unit if they are different */
|
|
|
|
if (min_short_size) {
|
|
|
|
unsigned long long gran_div;
|
|
|
|
unsigned long long min_div;
|
|
|
|
|
|
|
|
gran_div = controls->granularity / gran_short_size;
|
|
|
|
min_div = controls->min / min_short_size;
|
|
|
|
|
|
|
|
if (min_div > gran_div) {
|
|
|
|
min_short_size *= min_div / gran_div;
|
|
|
|
} else if (min_div < gran_div) {
|
|
|
|
unit = min_unit;
|
|
|
|
gran_short_size *= gran_div / min_div;
|
|
|
|
}
|
|
|
|
}
|
2017-06-05 14:00:45 +02:00
|
|
|
|
2018-08-27 19:23:04 +08:00
|
|
|
virBufferAsprintf(&childrenBuf,
|
2017-06-05 14:00:45 +02:00
|
|
|
"<control granularity='%llu'",
|
2017-11-10 13:19:53 +01:00
|
|
|
gran_short_size);
|
2017-06-05 14:00:45 +02:00
|
|
|
|
2017-11-10 13:19:53 +01:00
|
|
|
if (min_short_size)
|
2018-08-27 19:23:04 +08:00
|
|
|
virBufferAsprintf(&childrenBuf, " min='%llu'", min_short_size);
|
2017-06-05 14:00:45 +02:00
|
|
|
|
2018-08-27 19:23:04 +08:00
|
|
|
virBufferAsprintf(&childrenBuf,
|
2017-06-05 14:00:45 +02:00
|
|
|
" unit='%s' type='%s' maxAllocs='%u'/>\n",
|
2017-11-10 13:19:53 +01:00
|
|
|
unit,
|
|
|
|
virCacheTypeToString(controls->scope),
|
|
|
|
controls->max_allocation);
|
2017-05-17 17:08:33 +08:00
|
|
|
}
|
|
|
|
|
2019-10-25 16:16:41 +02:00
|
|
|
virXMLFormatElement(buf, "bank", &attrBuf, &childrenBuf);
|
2017-03-30 15:01:27 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 18:10:50 +08:00
|
|
|
if (virCapabilitiesFormatResctrlMonitor(buf, cache->monitor) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</cache>\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatMemoryBandwidth(virBuffer *buf,
|
|
|
|
virCapsHostMemBW *memBW)
|
2018-07-30 11:12:41 +08:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
|
2019-11-14 01:08:20 +08:00
|
|
|
if (!memBW->nnodes && !memBW->monitor)
|
2018-07-30 11:12:41 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<memory_bandwidth>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
|
2018-09-20 18:10:49 +08:00
|
|
|
for (i = 0; i < memBW->nnodes; i++) {
|
2019-10-25 16:26:35 +02:00
|
|
|
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
|
2019-10-30 12:40:06 +01:00
|
|
|
g_auto(virBuffer) childrenBuf = VIR_BUFFER_INIT_CHILD(buf);
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostMemBWNode *node = memBW->nodes[i];
|
|
|
|
virResctrlInfoMemBWPerNode *control = &node->control;
|
2019-10-25 16:26:35 +02:00
|
|
|
g_autofree char *cpus_str = virBitmapFormat(node->cpus);
|
2018-07-30 11:12:41 +08:00
|
|
|
|
|
|
|
if (!cpus_str)
|
|
|
|
return -1;
|
|
|
|
|
2019-10-25 16:26:35 +02:00
|
|
|
virBufferAsprintf(&attrBuf,
|
|
|
|
" id='%u' cpus='%s'",
|
2018-07-30 11:12:41 +08:00
|
|
|
node->id, cpus_str);
|
|
|
|
|
2018-08-27 19:23:04 +08:00
|
|
|
virBufferAsprintf(&childrenBuf,
|
2021-07-14 15:56:26 +02:00
|
|
|
"<control granularity='%u' min='%u' "
|
2018-07-30 11:12:41 +08:00
|
|
|
"maxAllocs='%u'/>\n",
|
|
|
|
control->granularity, control->min,
|
|
|
|
control->max_allocation);
|
|
|
|
|
2019-10-25 16:26:35 +02:00
|
|
|
virXMLFormatElement(buf, "node", &attrBuf, &childrenBuf);
|
2018-07-30 11:12:41 +08:00
|
|
|
}
|
|
|
|
|
2018-09-20 18:10:50 +08:00
|
|
|
if (virCapabilitiesFormatResctrlMonitor(buf, memBW->monitor) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</memory_bandwidth>\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-09 11:51:17 -05:00
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatHostXML(virCapsHost *host,
|
|
|
|
virBuffer *buf)
|
2008-02-27 04:35:08 +00:00
|
|
|
{
|
2019-01-09 11:51:17 -05:00
|
|
|
size_t i, j;
|
2010-05-25 15:33:51 +01:00
|
|
|
char host_uuid[VIR_UUID_STRING_BUFLEN];
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
/* The lack of some data means we have nothing
|
|
|
|
* minimally to format, so just return. */
|
|
|
|
if (!virUUIDIsValid(host->host_uuid) &&
|
|
|
|
!host->arch && !host->powerMgmt && !host->iommu)
|
|
|
|
return 0;
|
|
|
|
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<host>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-01-09 12:10:59 -05:00
|
|
|
if (virUUIDIsValid(host->host_uuid)) {
|
|
|
|
virUUIDFormat(host->host_uuid, host_uuid);
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<uuid>%s</uuid>\n", host_uuid);
|
2010-05-25 15:33:51 +01:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<cpu>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2014-03-06 16:50:15 +02:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->arch)
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<arch>%s</arch>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
virArchToString(host->arch));
|
|
|
|
if (host->nfeatures) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<features>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-01-09 12:10:59 -05:00
|
|
|
for (i = 0; i < host->nfeatures; i++) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<%s/>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->features[i]);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</features>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 12:10:59 -05:00
|
|
|
virCPUDefFormatBuf(buf, host->cpu);
|
2009-12-18 14:44:55 +01:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
for (i = 0; i < host->nPagesSize; i++) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<pages unit='KiB' size='%u'/>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->pagesSize[i]);
|
2014-06-06 18:12:51 +02:00
|
|
|
}
|
|
|
|
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</cpu>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2011-11-29 14:50:04 +00:00
|
|
|
/* The PM query was successful. */
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->powerMgmt) {
|
2011-11-29 14:50:04 +00:00
|
|
|
/* The host supports some PM features. */
|
2019-01-09 12:10:59 -05:00
|
|
|
unsigned int pm = host->powerMgmt;
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<power_management>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2011-11-29 14:50:04 +00:00
|
|
|
while (pm) {
|
2019-10-03 15:51:30 +01:00
|
|
|
int bit = __builtin_ffs(pm) - 1;
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<%s/>\n",
|
2011-11-29 14:50:04 +00:00
|
|
|
virCapsHostPMTargetTypeToString(bit));
|
|
|
|
pm &= ~(1U << bit);
|
2011-11-22 11:31:22 +08:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</power_management>\n");
|
2011-11-29 14:50:04 +00:00
|
|
|
} else {
|
|
|
|
/* The host does not support any PM feature. */
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<power_management/>\n");
|
2011-11-22 11:31:22 +08:00
|
|
|
}
|
|
|
|
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<iommu support='%s'/>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->iommu ? "yes" : "no");
|
2018-06-01 10:15:59 +02:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->offlineMigrate) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<migration_features>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->liveMigrate)
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<live/>\n");
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->nmigrateTrans) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<uri_transports>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-01-09 12:10:59 -05:00
|
|
|
for (i = 0; i < host->nmigrateTrans; i++) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<uri_transport>%s</uri_transport>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->migrateTrans[i]);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</uri_transports>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</migration_features>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
if (host->netprefix)
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<netprefix>%s</netprefix>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->netprefix);
|
2016-02-03 21:40:33 +00:00
|
|
|
|
2021-05-03 15:25:19 +02:00
|
|
|
if (virCapabilitiesHostNUMAFormat(buf, host->numa) < 0)
|
2020-01-06 18:57:26 -03:00
|
|
|
return -1;
|
2009-03-03 09:44:41 +00:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
if (virCapabilitiesFormatCaches(buf, &host->cache) < 0)
|
2020-01-06 18:57:26 -03:00
|
|
|
return -1;
|
2017-03-30 15:01:27 +02:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
if (virCapabilitiesFormatMemoryBandwidth(buf, &host->memBW) < 0)
|
2020-01-06 18:57:26 -03:00
|
|
|
return -1;
|
2018-07-30 11:12:41 +08:00
|
|
|
|
2019-01-09 12:10:59 -05:00
|
|
|
for (i = 0; i < host->nsecModels; i++) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAddLit(buf, "<secmodel>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferAsprintf(buf, "<model>%s</model>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->secModels[i].model);
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<doi>%s</doi>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->secModels[i].doi);
|
|
|
|
for (j = 0; j < host->secModels[i].nlabels; j++) {
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAsprintf(buf, "<baselabel type='%s'>%s</baselabel>\n",
|
2019-01-09 12:10:59 -05:00
|
|
|
host->secModels[i].labels[j].type,
|
|
|
|
host->secModels[i].labels[j].label);
|
2013-10-18 14:13:21 +02:00
|
|
|
}
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</secmodel>\n");
|
2009-03-03 09:44:41 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 11:51:17 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</host>\n\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-23 17:04:20 +02:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatGuestFeatures(virCapsGuest *guest,
|
|
|
|
virBuffer *buf)
|
2019-10-23 17:04:20 +02:00
|
|
|
{
|
2019-10-30 12:40:06 +01:00
|
|
|
g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
|
2019-10-23 17:04:20 +02:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < VIR_CAPS_GUEST_FEATURE_TYPE_LAST; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestFeature *feature = guest->features + i;
|
2019-10-23 17:04:20 +02:00
|
|
|
|
|
|
|
if (!feature->present)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
virBufferAsprintf(&childBuf, "<%s", virCapsGuestFeatureInfos[i].name);
|
|
|
|
|
|
|
|
if (feature->defaultOn) {
|
|
|
|
virBufferAsprintf(&childBuf, " default='%s'",
|
|
|
|
virTristateSwitchTypeToString(feature->defaultOn));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (feature->toggle) {
|
|
|
|
virBufferAsprintf(&childBuf, " toggle='%s'",
|
|
|
|
virTristateBoolTypeToString(feature->toggle));
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(&childBuf, "/>\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
virXMLFormatElement(buf, "features", NULL, &childBuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-09 12:00:28 -05:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatGuestXML(virCapsGuest **guests,
|
2019-01-09 13:31:50 -05:00
|
|
|
size_t nguests,
|
2021-03-11 08:16:13 +01:00
|
|
|
virBuffer *buf)
|
2019-01-09 11:51:17 -05:00
|
|
|
{
|
|
|
|
size_t i, j, k;
|
|
|
|
|
2019-01-09 13:31:50 -05:00
|
|
|
for (i = 0; i < nguests; i++) {
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAddLit(buf, "<guest>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferAsprintf(buf, "<os_type>%s</os_type>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
virDomainOSTypeToString(guests[i]->ostype));
|
|
|
|
if (guests[i]->arch.id)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<arch name='%s'>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
virArchToString(guests[i]->arch.id));
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
virBufferAsprintf(buf, "<wordsize>%d</wordsize>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
guests[i]->arch.wordsize);
|
|
|
|
if (guests[i]->arch.defaultInfo.emulator)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<emulator>%s</emulator>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
guests[i]->arch.defaultInfo.emulator);
|
|
|
|
if (guests[i]->arch.defaultInfo.loader)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<loader>%s</loader>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
guests[i]->arch.defaultInfo.loader);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-01-09 13:31:50 -05:00
|
|
|
for (j = 0; j < guests[i]->arch.defaultInfo.nmachines; j++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine *machine = guests[i]->arch.defaultInfo.machines[j];
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAddLit(buf, "<machine");
|
2009-07-23 18:31:35 +01:00
|
|
|
if (machine->canonical)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, " canonical='%s'", machine->canonical);
|
2013-06-26 17:46:35 +02:00
|
|
|
if (machine->maxCpus > 0)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, " maxCpus='%d'", machine->maxCpus);
|
2021-01-22 12:16:23 +00:00
|
|
|
if (machine->deprecated)
|
|
|
|
virBufferAddLit(buf, " deprecated='yes'");
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, ">%s</machine>\n", machine->name);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 13:31:50 -05:00
|
|
|
for (j = 0; j < guests[i]->arch.ndomains; j++) {
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<domain type='%s'",
|
2019-01-09 13:31:50 -05:00
|
|
|
virDomainVirtTypeToString(guests[i]->arch.domains[j]->type));
|
|
|
|
if (!guests[i]->arch.domains[j]->info.emulator &&
|
|
|
|
!guests[i]->arch.domains[j]->info.loader &&
|
|
|
|
!guests[i]->arch.domains[j]->info.nmachines) {
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAddLit(buf, "/>\n");
|
2014-12-11 16:11:49 +01:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAddLit(buf, ">\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
2019-01-09 13:31:50 -05:00
|
|
|
if (guests[i]->arch.domains[j]->info.emulator)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<emulator>%s</emulator>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
guests[i]->arch.domains[j]->info.emulator);
|
|
|
|
if (guests[i]->arch.domains[j]->info.loader)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, "<loader>%s</loader>\n",
|
2019-01-09 13:31:50 -05:00
|
|
|
guests[i]->arch.domains[j]->info.loader);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-01-09 13:31:50 -05:00
|
|
|
for (k = 0; k < guests[i]->arch.domains[j]->info.nmachines; k++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsGuestMachine *machine = guests[i]->arch.domains[j]->info.machines[k];
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAddLit(buf, "<machine");
|
2009-07-23 18:31:35 +01:00
|
|
|
if (machine->canonical)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, " canonical='%s'", machine->canonical);
|
2013-06-26 17:46:35 +02:00
|
|
|
if (machine->maxCpus > 0)
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAsprintf(buf, " maxCpus='%d'", machine->maxCpus);
|
|
|
|
virBufferAsprintf(buf, ">%s</machine>\n", machine->name);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</domain>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</arch>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-10-23 17:04:20 +02:00
|
|
|
virCapabilitiesFormatGuestFeatures(guests[i], buf);
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2019-01-09 12:00:28 -05:00
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</guest>\n\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2019-01-09 12:00:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatStoragePoolXML(virCapsStoragePool **pools,
|
2019-01-10 07:19:35 -05:00
|
|
|
size_t npools,
|
2021-03-11 08:16:13 +01:00
|
|
|
virBuffer *buf)
|
2019-01-10 07:19:35 -05:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (npools == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<pool>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "<enum name='type'>\n");
|
|
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
for (i = 0; i < npools; i++)
|
|
|
|
virBufferAsprintf(buf, "<value>%s</value>\n",
|
|
|
|
virStoragePoolTypeToString(pools[i]->type));
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</enum>\n");
|
|
|
|
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
|
|
virBufferAddLit(buf, "</pool>\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-09 12:00:28 -05:00
|
|
|
/**
|
|
|
|
* virCapabilitiesFormatXML:
|
|
|
|
* @caps: capabilities to format
|
|
|
|
*
|
|
|
|
* Convert the capabilities object into an XML representation
|
|
|
|
*
|
|
|
|
* Returns the XML document as a string
|
|
|
|
*/
|
|
|
|
char *
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesFormatXML(virCaps *caps)
|
2019-01-09 12:00:28 -05:00
|
|
|
{
|
2020-07-02 22:19:01 -04:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2019-01-09 12:00:28 -05:00
|
|
|
|
|
|
|
virBufferAddLit(&buf, "<capabilities>\n\n");
|
|
|
|
virBufferAdjustIndent(&buf, 2);
|
|
|
|
|
|
|
|
if (virCapabilitiesFormatHostXML(&caps->host, &buf) < 0)
|
2020-07-02 23:19:26 -04:00
|
|
|
return NULL;
|
2019-01-09 12:00:28 -05:00
|
|
|
|
2019-01-09 13:31:50 -05:00
|
|
|
virCapabilitiesFormatGuestXML(caps->guests, caps->nguests, &buf);
|
2019-01-09 12:00:28 -05:00
|
|
|
|
2019-01-10 07:19:35 -05:00
|
|
|
virCapabilitiesFormatStoragePoolXML(caps->pools, caps->npools, &buf);
|
|
|
|
|
2014-03-06 16:50:15 +02:00
|
|
|
virBufferAdjustIndent(&buf, -2);
|
|
|
|
virBufferAddLit(&buf, "</capabilities>\n");
|
2008-02-27 04:35:08 +00:00
|
|
|
|
2014-03-06 16:50:15 +02:00
|
|
|
return virBufferContentAndReset(&buf);
|
2008-02-27 04:35:08 +00:00
|
|
|
}
|
2013-07-18 11:21:48 +02:00
|
|
|
|
|
|
|
/* get the maximum ID of cpus in the host */
|
|
|
|
static unsigned int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAGetMaxcpu(virCapsHostNUMA *caps)
|
2013-07-18 11:21:48 +02:00
|
|
|
{
|
|
|
|
unsigned int maxcpu = 0;
|
|
|
|
size_t node;
|
|
|
|
size_t cpu;
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
for (node = 0; node < caps->cells->len; node++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACell *cell = g_ptr_array_index(caps->cells, node);
|
2013-07-18 11:21:48 +02:00
|
|
|
|
|
|
|
for (cpu = 0; cpu < cell->ncpus; cpu++) {
|
|
|
|
if (cell->cpus[cpu].id > maxcpu)
|
|
|
|
maxcpu = cell->cpus[cpu].id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return maxcpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set cpus of a numa node in the bitmask */
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAGetCellCpus(virCapsHostNUMA *caps,
|
2019-11-29 09:55:59 +00:00
|
|
|
size_t node,
|
2021-03-11 08:16:13 +01:00
|
|
|
virBitmap *cpumask)
|
2013-07-18 11:21:48 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACell *cell = NULL;
|
2013-07-18 11:21:48 +02:00
|
|
|
size_t cpu;
|
2014-02-11 16:35:33 +01:00
|
|
|
size_t i;
|
2014-02-11 14:32:50 +00:00
|
|
|
/* The numa node numbers can be non-contiguous. Ex: 0,1,16,17. */
|
2019-11-29 09:55:59 +00:00
|
|
|
for (i = 0; i < caps->cells->len; i++) {
|
|
|
|
cell = g_ptr_array_index(caps->cells, i);
|
|
|
|
if (cell->num == node)
|
2014-02-11 14:32:50 +00:00
|
|
|
break;
|
2019-11-29 09:55:59 +00:00
|
|
|
cell = NULL;
|
2014-02-11 14:32:50 +00:00
|
|
|
}
|
2013-07-18 11:21:48 +02:00
|
|
|
|
2014-02-11 14:32:50 +00:00
|
|
|
for (cpu = 0; cell && cpu < cell->ncpus; cpu++) {
|
2013-07-18 11:21:48 +02:00
|
|
|
if (virBitmapSetBit(cpumask, cell->cpus[cpu].id) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cpu '%u' in node '%zu' is out of range "
|
|
|
|
"of the provided bitmap"),
|
|
|
|
cell->cpus[cpu].id, node);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
virBitmap *
|
|
|
|
virCapabilitiesHostNUMAGetCpus(virCapsHostNUMA *caps,
|
|
|
|
virBitmap *nodemask)
|
2013-07-18 11:21:48 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virBitmap *ret = NULL;
|
2019-11-29 09:55:59 +00:00
|
|
|
unsigned int maxcpu = virCapabilitiesHostNUMAGetMaxcpu(caps);
|
2013-07-18 11:21:48 +02:00
|
|
|
ssize_t node = -1;
|
|
|
|
|
2020-10-01 17:42:11 +02:00
|
|
|
ret = virBitmapNew(maxcpu + 1);
|
2013-07-18 11:21:48 +02:00
|
|
|
|
|
|
|
while ((node = virBitmapNextSetBit(nodemask, node)) >= 0) {
|
2019-11-29 09:55:59 +00:00
|
|
|
if (virCapabilitiesHostNUMAGetCellCpus(caps, node, ret) < 0) {
|
2013-07-18 11:21:48 +02:00
|
|
|
virBitmapFree(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2017-03-07 10:40:15 +01:00
|
|
|
|
|
|
|
|
2021-08-19 15:21:41 +02:00
|
|
|
int
|
|
|
|
virCapabilitiesHostNUMAGetMaxNode(virCapsHostNUMA *caps)
|
|
|
|
{
|
|
|
|
virCapsHostNUMACell *cell = g_ptr_array_index(caps->cells, caps->cells->len - 1);
|
|
|
|
|
|
|
|
return cell->num;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-07 10:40:15 +01:00
|
|
|
int
|
|
|
|
virCapabilitiesGetNodeInfo(virNodeInfoPtr nodeinfo)
|
|
|
|
{
|
|
|
|
virArch hostarch = virArchFromHost();
|
|
|
|
unsigned long long memorybytes;
|
|
|
|
|
|
|
|
memset(nodeinfo, 0, sizeof(*nodeinfo));
|
|
|
|
|
2018-07-20 09:50:37 +02:00
|
|
|
if (virStrcpyStatic(nodeinfo->model, virArchToString(hostarch)) < 0)
|
2017-03-07 10:40:15 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virHostMemGetInfo(&memorybytes, NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
nodeinfo->memory = memorybytes / 1024;
|
|
|
|
|
|
|
|
if (virHostCPUGetInfo(hostarch,
|
|
|
|
&nodeinfo->cpus, &nodeinfo->mhz,
|
|
|
|
&nodeinfo->nodes, &nodeinfo->sockets,
|
|
|
|
&nodeinfo->cores, &nodeinfo->threads) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns 1 on success, 0 if the detection failed and -1 on hard error */
|
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
virCapabilitiesFillCPUInfo(int cpu_id G_GNUC_UNUSED,
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACellCPU *cpu G_GNUC_UNUSED)
|
2017-03-07 10:40:15 +01:00
|
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
cpu->id = cpu_id;
|
|
|
|
|
|
|
|
if (virHostCPUGetSocket(cpu_id, &cpu->socket_id) < 0 ||
|
2019-12-16 18:10:29 +00:00
|
|
|
virHostCPUGetDie(cpu_id, &cpu->die_id) < 0 ||
|
2017-03-07 10:40:15 +01:00
|
|
|
virHostCPUGetCore(cpu_id, &cpu->core_id) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(cpu->siblings = virHostCPUGetSiblingsList(cpu_id)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("node cpu info not implemented on this platform"));
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-05-03 12:06:42 +02:00
|
|
|
virCapabilitiesGetNUMADistances(int node,
|
2021-05-03 11:04:45 +02:00
|
|
|
virNumaDistance **distancesRet,
|
2021-05-03 12:06:42 +02:00
|
|
|
int *ndistancesRet)
|
2017-03-07 10:40:15 +01:00
|
|
|
{
|
2021-05-03 11:04:45 +02:00
|
|
|
virNumaDistance *tmp = NULL;
|
2017-03-07 10:40:15 +01:00
|
|
|
int tmp_size = 0;
|
|
|
|
int ret = -1;
|
|
|
|
int *distances = NULL;
|
|
|
|
int ndistances = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (virNumaGetDistances(node, &distances, &ndistances) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!distances) {
|
2021-05-03 12:06:42 +02:00
|
|
|
*distancesRet = NULL;
|
|
|
|
*ndistancesRet = 0;
|
2017-03-07 10:40:15 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-03 11:04:45 +02:00
|
|
|
tmp = g_new0(virNumaDistance, ndistances);
|
2017-03-07 10:40:15 +01:00
|
|
|
|
|
|
|
for (i = 0; i < ndistances; i++) {
|
|
|
|
if (!distances[i])
|
|
|
|
continue;
|
|
|
|
|
2021-05-03 11:04:45 +02:00
|
|
|
tmp[tmp_size].cellid = i;
|
|
|
|
tmp[tmp_size].value = distances[i];
|
2017-03-07 10:40:15 +01:00
|
|
|
tmp_size++;
|
|
|
|
}
|
|
|
|
|
2021-03-20 00:37:05 +01:00
|
|
|
VIR_REALLOC_N(tmp, tmp_size);
|
2017-03-07 10:40:15 +01:00
|
|
|
|
2021-05-03 12:06:42 +02:00
|
|
|
*ndistancesRet = tmp_size;
|
|
|
|
*distancesRet = g_steal_pointer(&tmp);
|
2017-03-07 10:40:15 +01:00
|
|
|
tmp_size = 0;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(distances);
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapabilitiesGetNUMAPagesInfo(int node,
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACellPageInfo **pageinfo,
|
2017-03-07 10:40:15 +01:00
|
|
|
int *npageinfo)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
2018-04-23 16:36:53 +02:00
|
|
|
unsigned int *pages_size = NULL;
|
|
|
|
unsigned long long *pages_avail = NULL;
|
2017-03-07 10:40:15 +01:00
|
|
|
size_t npages, i;
|
|
|
|
|
|
|
|
if (virNumaGetPages(node, &pages_size, &pages_avail, NULL, &npages) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
*pageinfo = g_new0(virCapsHostNUMACellPageInfo, npages);
|
2017-03-07 10:40:15 +01:00
|
|
|
*npageinfo = npages;
|
|
|
|
|
|
|
|
for (i = 0; i < npages; i++) {
|
|
|
|
(*pageinfo)[i].size = pages_size[i];
|
|
|
|
(*pageinfo)[i].avail = pages_avail[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(pages_avail);
|
|
|
|
VIR_FREE(pages_size);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-29 18:58:43 +02:00
|
|
|
static int
|
|
|
|
virCapabilitiesGetNodeCacheReadFile(const char *prefix,
|
|
|
|
const char *dir,
|
|
|
|
const char *file,
|
|
|
|
unsigned int *value)
|
|
|
|
{
|
|
|
|
g_autofree char *path = g_build_filename(prefix, dir, file, NULL);
|
|
|
|
int rv = virFileReadValueUint(value, "%s", path);
|
|
|
|
|
|
|
|
if (rv < 0) {
|
|
|
|
if (rv == -2) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("File '%s' does not exist"),
|
|
|
|
path);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapsHostNUMACellCacheComparator(const void *a,
|
|
|
|
const void *b)
|
|
|
|
{
|
|
|
|
const virNumaCache *aa = a;
|
|
|
|
const virNumaCache *bb = b;
|
|
|
|
|
|
|
|
return aa->level - bb->level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapabilitiesGetNodeCache(int node,
|
|
|
|
GArray **cachesRet)
|
|
|
|
{
|
|
|
|
g_autoptr(DIR) dir = NULL;
|
|
|
|
int direrr = 0;
|
|
|
|
struct dirent *entry;
|
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autoptr(GArray) caches = g_array_new(FALSE, FALSE, sizeof(virNumaCache));
|
|
|
|
|
|
|
|
path = g_strdup_printf(SYSFS_SYSTEM_PATH "/node/node%d/memory_side_cache", node);
|
|
|
|
|
|
|
|
if (virDirOpenIfExists(&dir, path) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (dir && (direrr = virDirRead(dir, &entry, path)) > 0) {
|
|
|
|
const char *dname = STRSKIP(entry->d_name, "index");
|
|
|
|
virNumaCache cache = { 0 };
|
|
|
|
unsigned int indexing;
|
|
|
|
unsigned int write_policy;
|
|
|
|
|
|
|
|
if (!dname)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virStrToLong_ui(dname, NULL, 10, &cache.level) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to parse %s"),
|
|
|
|
entry->d_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
|
|
|
|
"size", &cache.size) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cache.size >>= 10; /* read in bytes but stored in kibibytes */
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
|
|
|
|
"line_size", &cache.line) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
|
|
|
|
"indexing", &indexing) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* see enum cache_indexing in kernel */
|
|
|
|
switch (indexing) {
|
|
|
|
case 0: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_DIRECT; break;
|
|
|
|
case 1: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_FULL; break;
|
|
|
|
case 2: cache.associativity = VIR_NUMA_CACHE_ASSOCIATIVITY_NONE; break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown indexing value '%u'"),
|
|
|
|
indexing);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, entry->d_name,
|
|
|
|
"write_policy", &write_policy) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* see enum cache_write_policy in kernel */
|
|
|
|
switch (write_policy) {
|
|
|
|
case 0: cache.policy = VIR_NUMA_CACHE_POLICY_WRITEBACK; break;
|
|
|
|
case 1: cache.policy = VIR_NUMA_CACHE_POLICY_WRITETHROUGH; break;
|
|
|
|
case 2: cache.policy = VIR_NUMA_CACHE_POLICY_NONE; break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown write_policy value '%u'"),
|
|
|
|
write_policy);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val(caches, cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direrr < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (caches->len > 0) {
|
|
|
|
g_array_sort(caches, virCapsHostNUMACellCacheComparator);
|
|
|
|
*cachesRet = g_steal_pointer(&caches);
|
|
|
|
} else {
|
|
|
|
*cachesRet = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-07 10:40:15 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAInitFake(virCapsHostNUMA *caps)
|
2017-03-07 10:40:15 +01:00
|
|
|
{
|
|
|
|
virNodeInfo nodeinfo;
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACellCPU *cpus;
|
2017-03-07 10:40:15 +01:00
|
|
|
int ncpus;
|
2019-12-18 06:31:53 -05:00
|
|
|
int n, s, c, t;
|
2017-03-07 10:40:15 +01:00
|
|
|
int id, cid;
|
2019-10-14 14:45:33 +02:00
|
|
|
int onlinecpus G_GNUC_UNUSED;
|
2017-03-07 10:40:15 +01:00
|
|
|
bool tmp;
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeInfo(&nodeinfo) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ncpus = VIR_NODEINFO_MAXCPUS(nodeinfo);
|
|
|
|
|
|
|
|
|
2019-12-18 06:31:53 -05:00
|
|
|
id = 0;
|
|
|
|
for (n = 0; n < nodeinfo.nodes; n++) {
|
|
|
|
int nodecpus = nodeinfo.sockets * nodeinfo.cores * nodeinfo.threads;
|
|
|
|
cid = 0;
|
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
cpus = g_new0(virCapsHostNUMACellCPU, nodecpus);
|
2019-12-18 06:31:53 -05:00
|
|
|
|
|
|
|
for (s = 0; s < nodeinfo.sockets; s++) {
|
|
|
|
for (c = 0; c < nodeinfo.cores; c++) {
|
|
|
|
g_autoptr(virBitmap) siblings = virBitmapNew(ncpus);
|
|
|
|
for (t = 0; t < nodeinfo.threads; t++)
|
|
|
|
ignore_value(virBitmapSetBit(siblings, id + t));
|
|
|
|
|
|
|
|
for (t = 0; t < nodeinfo.threads; t++) {
|
|
|
|
if (virHostCPUGetOnline(id, &tmp) < 0)
|
2017-03-07 10:40:15 +01:00
|
|
|
goto error;
|
2019-12-18 06:31:53 -05:00
|
|
|
if (tmp) {
|
|
|
|
cpus[cid].id = id;
|
2019-12-16 18:10:29 +00:00
|
|
|
cpus[cid].die_id = 0;
|
2019-12-18 06:31:53 -05:00
|
|
|
cpus[cid].socket_id = s;
|
|
|
|
cpus[cid].core_id = c;
|
2020-10-02 10:26:34 +02:00
|
|
|
cpus[cid].siblings = virBitmapNewCopy(siblings);
|
2019-12-18 06:31:53 -05:00
|
|
|
cid++;
|
|
|
|
}
|
|
|
|
|
|
|
|
id++;
|
2017-03-07 10:40:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-18 06:31:53 -05:00
|
|
|
virCapabilitiesHostNUMAAddCell(caps, 0,
|
|
|
|
nodeinfo.memory,
|
2021-04-29 18:58:40 +02:00
|
|
|
cid, &cpus,
|
2019-12-18 06:31:53 -05:00
|
|
|
0, NULL,
|
2021-04-29 18:58:43 +02:00
|
|
|
0, NULL,
|
|
|
|
NULL);
|
2019-12-18 06:31:53 -05:00
|
|
|
}
|
2017-03-07 10:40:15 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2019-12-18 06:31:53 -05:00
|
|
|
for (; cid >= 0; cid--)
|
|
|
|
virBitmapFree(cpus[cid].siblings);
|
2017-03-07 10:40:15 +01:00
|
|
|
VIR_FREE(cpus);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
|
capabilities: Expose NUMA interconnects
Links between NUMA nodes can have different latencies and
bandwidths. This info is newly defined in ACPI 6.2 under
Heterogeneous Memory Attribute Table (HMAT) table. Linux kernel
learned how to report these values under sysfs and thus we can
expose them in our capabilities XML. The sysfs interface is
documented in kernel's Documentation/admin-guide/mm/numaperf.rst.
Long story short, two nodes can be in initiator-target
relationship. A node can be initiator if it has a CPU or a device
that's capable of initiating memory transfer. Therefore a node
that has just memory can only be target. An initiator-target link
can then have any combination of {bandwidth, latency} - {access,
read, write} attribute (6 in total). However, the standard says
access is applicable iff read and write values are the same.
Therefore, we really have just four combinations of attributes:
bandwidth-read, bandwidth-write, latency-read, latency-write.
This is the combination that kernel reports anyway.
Then, under /sys/system/devices/node/nodeX/acccessN/initiators we
find values for those 4 attributes and also symlinks named
"nodeN" which then represent initiators to nodeX. For instance:
/sys/system/node/node1/access1/initiators/node0 -> ../../node0
/sys/system/node/node1/access1/initiators/read_bandwidth
/sys/system/node/node1/access1/initiators/read_latency
/sys/system/node/node1/access1/initiators/write_bandwidth
/sys/system/node/node1/access1/initiators/write_latency
This means that node0 is initiator and node1 is target and values
of the interconnect can be read.
In theory, there can be separate links to memory side caches too
(e.g. one link from node X to node Y's main memory, another from
node X to node Y's L1 cache, another one to L2 cache and so on).
But sysfs does not express this relationship just yet.
The "accessN" means either "access0" or "access1". The difference
is that while the former expresses the best interconnect between
two nodes including CPUS and I/O devices (such as GPUs and NICs),
the latter includes only CPUs and thus is what we need.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1786309
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2021-05-17 13:36:34 +02:00
|
|
|
static void
|
|
|
|
virCapabilitiesHostInsertHMAT(GArray *interconnects,
|
|
|
|
unsigned int initiator,
|
|
|
|
unsigned int target,
|
|
|
|
unsigned int read_bandwidth,
|
|
|
|
unsigned int write_bandwidth,
|
|
|
|
unsigned int read_latency,
|
|
|
|
unsigned int write_latency)
|
|
|
|
{
|
|
|
|
virNumaInterconnect ni;
|
|
|
|
|
|
|
|
ni = (virNumaInterconnect) { VIR_NUMA_INTERCONNECT_TYPE_BANDWIDTH,
|
|
|
|
initiator, target, 0, VIR_MEMORY_LATENCY_READ, read_bandwidth};
|
|
|
|
g_array_append_val(interconnects, ni);
|
|
|
|
|
|
|
|
ni = (virNumaInterconnect) { VIR_NUMA_INTERCONNECT_TYPE_BANDWIDTH,
|
|
|
|
initiator, target, 0, VIR_MEMORY_LATENCY_WRITE, write_bandwidth};
|
|
|
|
g_array_append_val(interconnects, ni);
|
|
|
|
|
|
|
|
ni = (virNumaInterconnect) { VIR_NUMA_INTERCONNECT_TYPE_LATENCY,
|
|
|
|
initiator, target, 0, VIR_MEMORY_LATENCY_READ, read_latency};
|
|
|
|
g_array_append_val(interconnects, ni);
|
|
|
|
|
|
|
|
ni = (virNumaInterconnect) { VIR_NUMA_INTERCONNECT_TYPE_LATENCY,
|
|
|
|
initiator, target, 0, VIR_MEMORY_LATENCY_WRITE, write_latency};
|
|
|
|
g_array_append_val(interconnects, ni);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapabilitiesHostNUMAInitInterconnectsNode(GArray *interconnects,
|
|
|
|
unsigned int node)
|
|
|
|
{
|
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autofree char *initPath = NULL;
|
|
|
|
g_autoptr(DIR) dir = NULL;
|
|
|
|
int direrr = 0;
|
|
|
|
struct dirent *entry;
|
|
|
|
unsigned int read_bandwidth;
|
|
|
|
unsigned int write_bandwidth;
|
|
|
|
unsigned int read_latency;
|
|
|
|
unsigned int write_latency;
|
|
|
|
|
|
|
|
/* Unfortunately, kernel does not expose full HMAT table. I mean it does,
|
|
|
|
* in its binary form under /sys/firmware/acpi/tables/HMAT but we don't
|
|
|
|
* want to parse that. But some important info is still exposed, under
|
|
|
|
* "access0" and "access1" directories. The former contains the best
|
|
|
|
* interconnect to given node including CPUs and devices that might do I/O
|
|
|
|
* (such as GPUs and NICs). The latter contains the best interconnect to
|
|
|
|
* given node but only CPUs are considered. Stick with access1 until sysfs
|
|
|
|
* exposes the full table in a sensible way.
|
|
|
|
* NB on most system access0 and access1 contain the same values. */
|
|
|
|
path = g_strdup_printf(SYSFS_SYSTEM_PATH "/node/node%d/access1", node);
|
|
|
|
|
|
|
|
if (!virFileExists(path))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, "initiators",
|
|
|
|
"read_bandwidth",
|
|
|
|
&read_bandwidth) < 0)
|
|
|
|
return -1;
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, "initiators",
|
|
|
|
"write_bandwidth",
|
|
|
|
&write_bandwidth) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Bandwidths are read in MiB but stored in KiB */
|
|
|
|
read_bandwidth <<= 10;
|
|
|
|
write_bandwidth <<= 10;
|
|
|
|
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, "initiators",
|
|
|
|
"read_latency",
|
|
|
|
&read_latency) < 0)
|
|
|
|
return -1;
|
|
|
|
if (virCapabilitiesGetNodeCacheReadFile(path, "initiators",
|
|
|
|
"write_latency",
|
|
|
|
&write_latency) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
initPath = g_strdup_printf("%s/initiators", path);
|
|
|
|
|
|
|
|
if (virDirOpen(&dir, initPath) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((direrr = virDirRead(dir, &entry, path)) > 0) {
|
|
|
|
const char *dname = STRSKIP(entry->d_name, "node");
|
|
|
|
unsigned int initNode;
|
|
|
|
|
|
|
|
if (!dname)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virStrToLong_ui(dname, NULL, 10, &initNode) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to parse %s"),
|
|
|
|
entry->d_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virCapabilitiesHostInsertHMAT(interconnects,
|
|
|
|
initNode, node,
|
|
|
|
read_bandwidth,
|
|
|
|
write_bandwidth,
|
|
|
|
read_latency,
|
|
|
|
write_latency);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapsHostNUMAInterconnectComparator(const void *a,
|
|
|
|
const void *b)
|
|
|
|
{
|
|
|
|
const virNumaInterconnect *aa = a;
|
|
|
|
const virNumaInterconnect *bb = b;
|
|
|
|
|
|
|
|
if (aa->type != bb->type)
|
|
|
|
return aa->type - bb->type;
|
|
|
|
|
|
|
|
if (aa->initiator != bb->initiator)
|
|
|
|
return aa->initiator - bb->initiator;
|
|
|
|
|
|
|
|
if (aa->target != bb->target)
|
|
|
|
return aa->target - bb->target;
|
|
|
|
|
|
|
|
if (aa->cache != bb->cache)
|
|
|
|
return aa->cache - bb->cache;
|
|
|
|
|
|
|
|
if (aa->accessType != bb->accessType)
|
|
|
|
return aa->accessType - bb->accessType;
|
|
|
|
|
|
|
|
return aa->value - bb->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCapabilitiesHostNUMAInitInterconnects(virCapsHostNUMA *caps)
|
|
|
|
{
|
|
|
|
g_autoptr(DIR) dir = NULL;
|
|
|
|
int direrr = 0;
|
|
|
|
struct dirent *entry;
|
|
|
|
const char *path = SYSFS_SYSTEM_PATH "/node/";
|
|
|
|
g_autoptr(GArray) interconnects = g_array_new(FALSE, FALSE, sizeof(virNumaInterconnect));
|
|
|
|
|
|
|
|
if (virDirOpenIfExists(&dir, path) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (dir && (direrr = virDirRead(dir, &entry, path)) > 0) {
|
|
|
|
const char *dname = STRSKIP(entry->d_name, "node");
|
|
|
|
unsigned int node;
|
|
|
|
|
|
|
|
if (!dname)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virStrToLong_ui(dname, NULL, 10, &node) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to parse %s"),
|
|
|
|
entry->d_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCapabilitiesHostNUMAInitInterconnectsNode(interconnects, node) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interconnects->len > 0) {
|
|
|
|
g_array_sort(interconnects, virCapsHostNUMAInterconnectComparator);
|
|
|
|
caps->interconnects = g_steal_pointer(&interconnects);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostNUMAInitReal(virCapsHostNUMA *caps)
|
2017-03-07 10:40:15 +01:00
|
|
|
{
|
|
|
|
int n;
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMACellCPU *cpus = NULL;
|
2017-03-07 10:40:15 +01:00
|
|
|
int ret = -1;
|
|
|
|
int ncpus = 0;
|
|
|
|
int max_node;
|
|
|
|
|
|
|
|
if ((max_node = virNumaGetMaxNode()) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (n = 0; n <= max_node; n++) {
|
2021-05-04 09:47:39 +02:00
|
|
|
g_autoptr(virBitmap) cpumap = NULL;
|
2021-05-03 11:04:45 +02:00
|
|
|
g_autofree virNumaDistance *distances = NULL;
|
2021-05-03 12:06:42 +02:00
|
|
|
int ndistances = 0;
|
2021-05-04 09:47:39 +02:00
|
|
|
g_autofree virCapsHostNUMACellPageInfo *pageinfo = NULL;
|
2021-04-29 18:58:43 +02:00
|
|
|
int npageinfo = 0;
|
2021-05-04 09:47:39 +02:00
|
|
|
unsigned long long memory;
|
2021-04-29 18:58:43 +02:00
|
|
|
g_autoptr(GArray) caches = NULL;
|
2021-05-04 09:47:39 +02:00
|
|
|
int cpu;
|
2017-03-07 10:40:15 +01:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((ncpus = virNumaGetNodeCPUs(n, &cpumap)) < 0) {
|
|
|
|
if (ncpus == -2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
cpus = g_new0(virCapsHostNUMACellCPU, ncpus);
|
2017-03-07 10:40:15 +01:00
|
|
|
cpu = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < virBitmapSize(cpumap); i++) {
|
|
|
|
if (virBitmapIsBitSet(cpumap, i)) {
|
2019-11-29 09:55:59 +00:00
|
|
|
if (virCapabilitiesFillCPUInfo(i, cpus + cpu++) < 0)
|
|
|
|
goto cleanup;
|
2017-03-07 10:40:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-03 12:06:42 +02:00
|
|
|
if (virCapabilitiesGetNUMADistances(n, &distances, &ndistances) < 0)
|
2021-05-27 11:42:19 +02:00
|
|
|
goto cleanup;
|
2017-03-07 10:40:15 +01:00
|
|
|
|
|
|
|
if (virCapabilitiesGetNUMAPagesInfo(n, &pageinfo, &npageinfo) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2021-04-29 18:58:43 +02:00
|
|
|
if (virCapabilitiesGetNodeCache(n, &caches) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2017-03-07 10:40:15 +01:00
|
|
|
/* Detect the amount of memory in the numa cell in KiB */
|
|
|
|
virNumaGetNodeMemory(n, &memory, NULL);
|
|
|
|
memory >>= 10;
|
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
virCapabilitiesHostNUMAAddCell(caps, n, memory,
|
2021-04-29 18:58:40 +02:00
|
|
|
ncpus, &cpus,
|
2021-05-03 12:06:42 +02:00
|
|
|
ndistances, &distances,
|
2021-04-29 18:58:43 +02:00
|
|
|
npageinfo, &pageinfo,
|
|
|
|
&caches);
|
2017-03-07 10:40:15 +01:00
|
|
|
}
|
|
|
|
|
capabilities: Expose NUMA interconnects
Links between NUMA nodes can have different latencies and
bandwidths. This info is newly defined in ACPI 6.2 under
Heterogeneous Memory Attribute Table (HMAT) table. Linux kernel
learned how to report these values under sysfs and thus we can
expose them in our capabilities XML. The sysfs interface is
documented in kernel's Documentation/admin-guide/mm/numaperf.rst.
Long story short, two nodes can be in initiator-target
relationship. A node can be initiator if it has a CPU or a device
that's capable of initiating memory transfer. Therefore a node
that has just memory can only be target. An initiator-target link
can then have any combination of {bandwidth, latency} - {access,
read, write} attribute (6 in total). However, the standard says
access is applicable iff read and write values are the same.
Therefore, we really have just four combinations of attributes:
bandwidth-read, bandwidth-write, latency-read, latency-write.
This is the combination that kernel reports anyway.
Then, under /sys/system/devices/node/nodeX/acccessN/initiators we
find values for those 4 attributes and also symlinks named
"nodeN" which then represent initiators to nodeX. For instance:
/sys/system/node/node1/access1/initiators/node0 -> ../../node0
/sys/system/node/node1/access1/initiators/read_bandwidth
/sys/system/node/node1/access1/initiators/read_latency
/sys/system/node/node1/access1/initiators/write_bandwidth
/sys/system/node/node1/access1/initiators/write_latency
This means that node0 is initiator and node1 is target and values
of the interconnect can be read.
In theory, there can be separate links to memory side caches too
(e.g. one link from node X to node Y's main memory, another from
node X to node Y's L1 cache, another one to L2 cache and so on).
But sysfs does not express this relationship just yet.
The "accessN" means either "access0" or "access1". The difference
is that while the former expresses the best interconnect between
two nodes including CPUS and I/O devices (such as GPUs and NICs),
the latter includes only CPUs and thus is what we need.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1786309
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2021-05-17 13:36:34 +02:00
|
|
|
if (virCapabilitiesHostNUMAInitInterconnects(caps) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2017-03-07 10:40:15 +01:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2021-05-04 08:21:59 +02:00
|
|
|
virCapabilitiesClearHostNUMACellCPUTopology(cpus, ncpus);
|
2017-03-07 10:40:15 +01:00
|
|
|
VIR_FREE(cpus);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-03-11 16:32:13 +01:00
|
|
|
|
2019-11-29 09:55:59 +00:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMA *
|
2019-11-29 09:55:59 +00:00
|
|
|
virCapabilitiesHostNUMANew(void)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMA *caps = NULL;
|
2019-11-29 09:55:59 +00:00
|
|
|
|
|
|
|
caps = g_new0(virCapsHostNUMA, 1);
|
|
|
|
caps->refs = 1;
|
|
|
|
caps->cells = g_ptr_array_new_with_free_func(
|
|
|
|
(GDestroyNotify)virCapabilitiesFreeHostNUMACell);
|
|
|
|
|
|
|
|
return caps;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMA *
|
2019-11-29 09:55:59 +00:00
|
|
|
virCapabilitiesHostNUMANewHost(void)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostNUMA *caps = virCapabilitiesHostNUMANew();
|
2019-11-29 09:55:59 +00:00
|
|
|
|
|
|
|
if (virNumaIsAvailable()) {
|
|
|
|
if (virCapabilitiesHostNUMAInitReal(caps) == 0)
|
|
|
|
return caps;
|
|
|
|
|
|
|
|
virCapabilitiesHostNUMAUnref(caps);
|
|
|
|
caps = virCapabilitiesHostNUMANew();
|
|
|
|
VIR_WARN("Failed to query host NUMA topology, faking single NUMA node");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCapabilitiesHostNUMAInitFake(caps) < 0) {
|
|
|
|
virCapabilitiesHostNUMAUnref(caps);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return caps;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-11 16:32:13 +01:00
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesInitPages(virCaps *caps)
|
2017-03-11 16:32:13 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
unsigned int *pages_size = NULL;
|
|
|
|
size_t npages;
|
|
|
|
|
|
|
|
if (virNumaGetPages(-1 /* Magic constant for overall info */,
|
|
|
|
&pages_size, NULL, NULL, &npages) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2021-03-24 10:32:58 +01:00
|
|
|
caps->host.pagesSize = g_steal_pointer(&pages_size);
|
2017-03-11 16:32:13 +01:00
|
|
|
caps->host.nPagesSize = npages;
|
|
|
|
npages = 0;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(pages_size);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-03-30 15:01:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
bool
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBankEquals(virCapsHostCacheBank *a,
|
|
|
|
virCapsHostCacheBank *b)
|
2017-03-30 15:01:27 +02:00
|
|
|
{
|
|
|
|
return (a->id == b->id &&
|
|
|
|
a->level == b->level &&
|
|
|
|
a->type == b->type &&
|
|
|
|
a->size == b->size &&
|
|
|
|
virBitmapEqual(a->cpus, b->cpus));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBankFree(virCapsHostCacheBank *ptr)
|
2017-03-30 15:01:27 +02:00
|
|
|
{
|
2017-05-17 17:08:33 +08:00
|
|
|
size_t i;
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
if (!ptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
virBitmapFree(ptr->cpus);
|
2017-05-17 17:08:33 +08:00
|
|
|
for (i = 0; i < ptr->ncontrols; i++)
|
2021-01-30 14:05:50 -05:00
|
|
|
g_free(ptr->controls[i]);
|
|
|
|
g_free(ptr->controls);
|
|
|
|
g_free(ptr);
|
2017-03-30 15:01:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 13:19:31 +01:00
|
|
|
|
|
|
|
static int
|
|
|
|
virCapsHostCacheBankSorter(const void *a,
|
|
|
|
const void *b)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBank *ca = *(virCapsHostCacheBank **)a;
|
|
|
|
virCapsHostCacheBank *cb = *(virCapsHostCacheBank **)b;
|
2017-11-10 13:19:31 +01:00
|
|
|
|
|
|
|
if (ca->level < cb->level)
|
|
|
|
return -1;
|
|
|
|
if (ca->level > cb->level)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return ca->id - cb->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-20 13:43:30 +01:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesInitResctrl(virCaps *caps)
|
2017-11-20 13:43:30 +01:00
|
|
|
{
|
|
|
|
if (caps->host.resctrl)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
caps->host.resctrl = virResctrlInfoNew();
|
|
|
|
if (!caps->host.resctrl)
|
|
|
|
return -1;
|
|
|
|
|
2018-02-08 15:47:46 +01:00
|
|
|
return 0;
|
2017-11-20 13:43:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesInitResctrlMemory(virCaps *caps)
|
2018-07-30 11:12:41 +08:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostMemBWNode *node = NULL;
|
2018-07-30 11:12:41 +08:00
|
|
|
size_t i = 0;
|
|
|
|
int ret = -1;
|
2018-09-20 18:10:50 +08:00
|
|
|
const virResctrlMonitorType montype = VIR_RESCTRL_MONITOR_TYPE_MEMBW;
|
|
|
|
const char *prefix = virResctrlMonitorPrefixTypeToString(montype);
|
2018-07-30 11:12:41 +08:00
|
|
|
|
2018-09-20 18:10:48 +08:00
|
|
|
for (i = 0; i < caps->host.cache.nbanks; i++) {
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBank *bank = caps->host.cache.banks[i];
|
2020-10-07 21:12:01 +02:00
|
|
|
node = g_new0(virCapsHostMemBWNode, 1);
|
2018-07-30 11:12:41 +08:00
|
|
|
|
|
|
|
if (virResctrlInfoGetMemoryBandwidth(caps->host.resctrl,
|
|
|
|
bank->level, &node->control) > 0) {
|
|
|
|
node->id = bank->id;
|
2020-10-02 10:34:53 +02:00
|
|
|
node->cpus = virBitmapNewCopy(bank->cpus);
|
2018-07-30 11:12:41 +08:00
|
|
|
|
2021-08-03 14:14:20 +02:00
|
|
|
VIR_APPEND_ELEMENT(caps->host.memBW.nodes, caps->host.memBW.nnodes, node);
|
2018-07-30 11:12:41 +08:00
|
|
|
}
|
|
|
|
virCapsHostMemBWNodeFree(node);
|
|
|
|
node = NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-20 18:10:50 +08:00
|
|
|
if (virResctrlInfoGetMonitorPrefix(caps->host.resctrl, prefix,
|
|
|
|
&caps->host.memBW.monitor) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virCapsHostMemBWNodeFree(node);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesInitCaches(virCaps *caps)
|
2017-03-30 15:01:27 +02:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
2021-03-11 08:16:13 +01:00
|
|
|
virBitmap *cpus = NULL;
|
2017-03-30 15:01:27 +02:00
|
|
|
ssize_t pos = -1;
|
|
|
|
int ret = -1;
|
|
|
|
char *path = NULL;
|
|
|
|
char *type = NULL;
|
|
|
|
struct dirent *ent = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapsHostCacheBank *bank = NULL;
|
2018-09-20 18:10:50 +08:00
|
|
|
const virResctrlMonitorType montype = VIR_RESCTRL_MONITOR_TYPE_CACHE;
|
|
|
|
const char *prefix = virResctrlMonitorPrefixTypeToString(montype);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
|
|
|
/* Minimum level to expose in capabilities. Can be lowered or removed (with
|
|
|
|
* the appropriate code below), but should not be increased, because we'd
|
|
|
|
* lose information. */
|
|
|
|
const int cache_min_level = 3;
|
|
|
|
|
2017-11-20 13:43:30 +01:00
|
|
|
if (virCapabilitiesInitResctrl(caps) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
/* offline CPUs don't provide cache info */
|
|
|
|
if (virFileReadValueBitmap(&cpus, "%s/cpu/online", SYSFS_SYSTEM_PATH) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((pos = virBitmapNextSetBit(cpus, pos)) >= 0) {
|
|
|
|
int rv = -1;
|
2020-10-25 22:40:40 -04:00
|
|
|
g_autoptr(DIR) dirp = NULL;
|
2017-03-30 15:01:27 +02:00
|
|
|
|
|
|
|
VIR_FREE(path);
|
2019-10-22 15:26:14 +02:00
|
|
|
path = g_strdup_printf("%s/cpu/cpu%zd/cache/", SYSFS_SYSTEM_PATH, pos);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
|
|
|
rv = virDirOpenIfExists(&dirp, path);
|
|
|
|
if (rv < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!dirp)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while ((rv = virDirRead(dirp, &ent, path)) > 0) {
|
|
|
|
int kernel_type;
|
|
|
|
unsigned int level;
|
|
|
|
|
|
|
|
if (!STRPREFIX(ent->d_name, "index"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virFileReadValueUint(&level,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/level",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (level < cache_min_level)
|
|
|
|
continue;
|
|
|
|
|
2020-10-07 21:12:01 +02:00
|
|
|
bank = g_new0(virCapsHostCacheBank, 1);
|
2017-03-30 15:01:27 +02:00
|
|
|
bank->level = level;
|
|
|
|
|
|
|
|
if (virFileReadValueUint(&bank->id,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/id",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadValueUint(&bank->level,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/level",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadValueString(&type,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/type",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadValueScaledInt(&bank->size,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/size",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virFileReadValueBitmap(&bank->cpus,
|
|
|
|
"%s/cpu/cpu%zd/cache/%s/shared_cpu_list",
|
|
|
|
SYSFS_SYSTEM_PATH, pos, ent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
kernel_type = virCacheKernelTypeFromString(type);
|
|
|
|
if (kernel_type < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unknown cache type '%s'"), type);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2017-05-17 17:08:33 +08:00
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
bank->type = kernel_type;
|
2017-05-12 06:46:20 -04:00
|
|
|
VIR_FREE(type);
|
2017-03-30 15:01:27 +02:00
|
|
|
|
2018-09-20 18:10:48 +08:00
|
|
|
for (i = 0; i < caps->host.cache.nbanks; i++) {
|
|
|
|
if (virCapsHostCacheBankEquals(bank, caps->host.cache.banks[i]))
|
2017-03-30 15:01:27 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-09-20 18:10:48 +08:00
|
|
|
if (i == caps->host.cache.nbanks) {
|
2017-11-20 13:43:30 +01:00
|
|
|
/* If it is a new cache, then update its resctrl information. */
|
|
|
|
if (virResctrlInfoGetCache(caps->host.resctrl,
|
|
|
|
bank->level,
|
|
|
|
bank->size,
|
|
|
|
&bank->ncontrols,
|
|
|
|
&bank->controls) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2021-08-03 14:14:20 +02:00
|
|
|
VIR_APPEND_ELEMENT(caps->host.cache.banks, caps->host.cache.nbanks, bank);
|
2017-03-30 15:01:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
virCapsHostCacheBankFree(bank);
|
|
|
|
bank = NULL;
|
|
|
|
}
|
|
|
|
if (rv < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2017-11-10 13:19:31 +01:00
|
|
|
/* Sort the array in order for the tests to be predictable. This way we can
|
|
|
|
* still traverse the directory instead of guessing names (in case there is
|
|
|
|
* 'index1' and 'index3' but no 'index2'). */
|
2021-06-14 12:46:02 +02:00
|
|
|
if (caps->host.cache.banks) {
|
|
|
|
qsort(caps->host.cache.banks, caps->host.cache.nbanks,
|
|
|
|
sizeof(*caps->host.cache.banks), virCapsHostCacheBankSorter);
|
|
|
|
}
|
2017-11-10 13:19:31 +01:00
|
|
|
|
2018-07-30 11:12:41 +08:00
|
|
|
if (virCapabilitiesInitResctrlMemory(caps) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2018-09-20 18:10:50 +08:00
|
|
|
if (virResctrlInfoGetMonitorPrefix(caps->host.resctrl, prefix,
|
|
|
|
&caps->host.cache.monitor) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2017-03-30 15:01:27 +02:00
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
2017-05-12 06:46:20 -04:00
|
|
|
VIR_FREE(type);
|
2017-03-30 15:01:27 +02:00
|
|
|
VIR_FREE(path);
|
|
|
|
virCapsHostCacheBankFree(bank);
|
2017-05-29 10:04:36 +02:00
|
|
|
virBitmapFree(cpus);
|
2017-03-30 15:01:27 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2018-06-01 10:15:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-03-11 08:16:13 +01:00
|
|
|
virCapabilitiesHostInitIOMMU(virCaps *caps)
|
2018-06-01 10:15:59 +02:00
|
|
|
{
|
|
|
|
caps->host.iommu = virHostHasIOMMU();
|
|
|
|
}
|