mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 17:34:18 +03:00
Add flag to BaselineCPU API to return detailed CPU features
Currently the virConnectBaselineCPU API does not expose the CPU features that are part of the CPU's model. This patch adds a new flag, VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, that causes the API to explicitly list all features that are part of that model. Signed-off-by: Don Dugger <donald.d.dugger@intel.com> Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
10ec64105b
commit
d4952d36d0
@ -4007,6 +4007,15 @@ int virConnectCompareCPU(virConnectPtr conn,
|
||||
unsigned int flags);
|
||||
|
||||
|
||||
/**
|
||||
* virConnectBaselineCPUFlags
|
||||
*
|
||||
* Flags when getting XML description of a computed CPU
|
||||
*/
|
||||
typedef enum {
|
||||
VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES = (1 << 0), /* show all features */
|
||||
} virConnectBaselineCPUFlags;
|
||||
|
||||
/**
|
||||
* virConnectBaselineCPU:
|
||||
*
|
||||
|
@ -167,7 +167,7 @@ cpuDecode(virCPUDefPtr cpu,
|
||||
return -1;
|
||||
}
|
||||
|
||||
return driver->decode(cpu, data, models, nmodels, preferred);
|
||||
return driver->decode(cpu, data, models, nmodels, preferred, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -276,7 +276,8 @@ char *
|
||||
cpuBaselineXML(const char **xmlCPUs,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels)
|
||||
unsigned int nmodels,
|
||||
unsigned int flags)
|
||||
{
|
||||
xmlDocPtr doc = NULL;
|
||||
xmlXPathContextPtr ctxt = NULL;
|
||||
@ -323,7 +324,7 @@ cpuBaselineXML(const char **xmlCPUs,
|
||||
doc = NULL;
|
||||
}
|
||||
|
||||
if (!(cpu = cpuBaseline(cpus, ncpus, models, nmodels)))
|
||||
if (!(cpu = cpuBaseline(cpus, ncpus, models, nmodels, flags)))
|
||||
goto error;
|
||||
|
||||
cpustr = virCPUDefFormat(cpu, 0);
|
||||
@ -350,7 +351,8 @@ virCPUDefPtr
|
||||
cpuBaseline(virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels)
|
||||
unsigned int nmodels,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct cpuArchDriver *driver;
|
||||
size_t i;
|
||||
@ -392,7 +394,7 @@ cpuBaseline(virCPUDefPtr *cpus,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return driver->baseline(cpus, ncpus, models, nmodels);
|
||||
return driver->baseline(cpus, ncpus, models, nmodels, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,7 +53,8 @@ typedef int
|
||||
const virCPUDataPtr data,
|
||||
const char **models,
|
||||
unsigned int nmodels,
|
||||
const char *preferred);
|
||||
const char *preferred,
|
||||
unsigned int flags);
|
||||
|
||||
typedef int
|
||||
(*cpuArchEncode) (virArch arch,
|
||||
@ -81,7 +82,8 @@ typedef virCPUDefPtr
|
||||
(*cpuArchBaseline) (virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels);
|
||||
unsigned int nmodels,
|
||||
unsigned int flags);
|
||||
|
||||
typedef int
|
||||
(*cpuArchUpdate) (virCPUDefPtr guest,
|
||||
@ -149,13 +151,15 @@ extern char *
|
||||
cpuBaselineXML(const char **xmlCPUs,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels);
|
||||
unsigned int nmodels,
|
||||
unsigned int flags);
|
||||
|
||||
extern virCPUDefPtr
|
||||
cpuBaseline (virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels);
|
||||
unsigned int nmodels,
|
||||
unsigned int flags);
|
||||
|
||||
extern int
|
||||
cpuUpdate (virCPUDefPtr guest,
|
||||
|
@ -44,8 +44,12 @@ ArmDecode(virCPUDefPtr cpu ATTRIBUTE_UNUSED,
|
||||
const virCPUDataPtr data ATTRIBUTE_UNUSED,
|
||||
const char **models ATTRIBUTE_UNUSED,
|
||||
unsigned int nmodels ATTRIBUTE_UNUSED,
|
||||
const char *preferred ATTRIBUTE_UNUSED)
|
||||
const char *preferred ATTRIBUTE_UNUSED,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,8 @@ static virCPUDefPtr
|
||||
genericBaseline(virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels)
|
||||
unsigned int nmodels,
|
||||
unsigned int flags)
|
||||
{
|
||||
virCPUDefPtr cpu = NULL;
|
||||
virCPUFeatureDefPtr features = NULL;
|
||||
@ -121,6 +122,8 @@ genericBaseline(virCPUDefPtr *cpus,
|
||||
unsigned int count;
|
||||
size_t i, j;
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, NULL);
|
||||
|
||||
if (!cpuModelIsAllowed(cpus[0]->model, models, nmodels)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("CPU model %s is not supported by hypervisor"),
|
||||
|
@ -304,12 +304,15 @@ ppcDecode(virCPUDefPtr cpu,
|
||||
const virCPUDataPtr data,
|
||||
const char **models,
|
||||
unsigned int nmodels,
|
||||
const char *preferred ATTRIBUTE_UNUSED)
|
||||
const char *preferred ATTRIBUTE_UNUSED,
|
||||
unsigned int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
struct ppc_map *map;
|
||||
const struct ppc_model *model;
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, -1);
|
||||
|
||||
if (data == NULL || (map = ppcLoadMap()) == NULL)
|
||||
return -1;
|
||||
|
||||
@ -377,7 +380,8 @@ static virCPUDefPtr
|
||||
ppcBaseline(virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels)
|
||||
unsigned int nmodels,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ppc_map *map = NULL;
|
||||
const struct ppc_model *model;
|
||||
@ -385,6 +389,8 @@ ppcBaseline(virCPUDefPtr *cpus,
|
||||
virCPUDefPtr cpu = NULL;
|
||||
size_t i;
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, NULL);
|
||||
|
||||
if (!(map = ppcLoadMap()))
|
||||
goto error;
|
||||
|
||||
|
@ -48,8 +48,12 @@ s390Decode(virCPUDefPtr cpu ATTRIBUTE_UNUSED,
|
||||
const virCPUDataPtr data ATTRIBUTE_UNUSED,
|
||||
const char **models ATTRIBUTE_UNUSED,
|
||||
unsigned int nmodels ATTRIBUTE_UNUSED,
|
||||
const char *preferred ATTRIBUTE_UNUSED)
|
||||
const char *preferred ATTRIBUTE_UNUSED,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1319,13 +1319,42 @@ x86GuestData(virCPUDefPtr host,
|
||||
return x86Compute(host, guest, data, message);
|
||||
}
|
||||
|
||||
static int
|
||||
x86AddFeatures(virCPUDefPtr cpu,
|
||||
struct x86_map *map)
|
||||
{
|
||||
const struct x86_model *candidate;
|
||||
const struct x86_feature *feature = map->features;
|
||||
|
||||
candidate = map->models;
|
||||
while (candidate != NULL) {
|
||||
if (STREQ(cpu->model, candidate->name))
|
||||
break;
|
||||
candidate = candidate->next;
|
||||
}
|
||||
if (!candidate) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("%s not a known CPU model"), cpu->model);
|
||||
return -1;
|
||||
}
|
||||
while (feature != NULL) {
|
||||
if (x86DataIsSubset(candidate->data, feature->data) &&
|
||||
virCPUDefAddFeature(cpu, feature->name,
|
||||
VIR_CPU_FEATURE_REQUIRE) < 0)
|
||||
return -1;
|
||||
feature = feature->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
x86Decode(virCPUDefPtr cpu,
|
||||
const struct cpuX86Data *data,
|
||||
const char **models,
|
||||
unsigned int nmodels,
|
||||
const char *preferred)
|
||||
const char *preferred,
|
||||
unsigned int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
struct x86_map *map;
|
||||
@ -1334,6 +1363,8 @@ x86Decode(virCPUDefPtr cpu,
|
||||
virCPUDefPtr cpuModel = NULL;
|
||||
size_t i;
|
||||
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, -1);
|
||||
|
||||
if (data == NULL || (map = x86LoadMap()) == NULL)
|
||||
return -1;
|
||||
|
||||
@ -1406,6 +1437,9 @@ x86Decode(virCPUDefPtr cpu,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES &&
|
||||
x86AddFeatures(cpuModel, map) < 0)
|
||||
goto out;
|
||||
cpu->model = cpuModel->model;
|
||||
cpu->vendor = cpuModel->vendor;
|
||||
cpu->nfeatures = cpuModel->nfeatures;
|
||||
@ -1426,9 +1460,10 @@ x86DecodeCPUData(virCPUDefPtr cpu,
|
||||
const virCPUDataPtr data,
|
||||
const char **models,
|
||||
unsigned int nmodels,
|
||||
const char *preferred)
|
||||
const char *preferred,
|
||||
unsigned int flags)
|
||||
{
|
||||
return x86Decode(cpu, data->data.x86, models, nmodels, preferred);
|
||||
return x86Decode(cpu, data->data.x86, models, nmodels, preferred, flags);
|
||||
}
|
||||
|
||||
|
||||
@ -1674,7 +1709,8 @@ static virCPUDefPtr
|
||||
x86Baseline(virCPUDefPtr *cpus,
|
||||
unsigned int ncpus,
|
||||
const char **models,
|
||||
unsigned int nmodels)
|
||||
unsigned int nmodels,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct x86_map *map = NULL;
|
||||
struct x86_model *base_model = NULL;
|
||||
@ -1755,7 +1791,7 @@ x86Baseline(virCPUDefPtr *cpus,
|
||||
if (vendor && x86DataAddCpuid(base_model->data, &vendor->cpuid) < 0)
|
||||
goto error;
|
||||
|
||||
if (x86Decode(cpu, base_model->data, models, nmodels, NULL) < 0)
|
||||
if (x86Decode(cpu, base_model->data, models, nmodels, NULL, flags) < 0)
|
||||
goto error;
|
||||
|
||||
if (!outputVendor)
|
||||
|
@ -18524,11 +18524,16 @@ error:
|
||||
* @conn: virConnect connection
|
||||
* @xmlCPUs: array of XML descriptions of host CPUs
|
||||
* @ncpus: number of CPUs in xmlCPUs
|
||||
* @flags: extra flags; not used yet, so callers should always pass 0
|
||||
* @flags: bitwise-OR of virConnectBaselineCPUFlags
|
||||
*
|
||||
* Computes the most feature-rich CPU which is compatible with all given
|
||||
* host CPUs.
|
||||
*
|
||||
* If @flags includes VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES then libvirt
|
||||
* will explicitly list all CPU features that are part of the host CPU,
|
||||
* without this flag features that are part of the CPU model will not be
|
||||
* listed.
|
||||
*
|
||||
* Returns XML description of the computed CPU or NULL on error.
|
||||
*/
|
||||
char *
|
||||
|
@ -11056,12 +11056,12 @@ qemuConnectBaselineCPU(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
{
|
||||
char *cpu = NULL;
|
||||
|
||||
virCheckFlags(0, NULL);
|
||||
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, NULL);
|
||||
|
||||
if (virConnectBaselineCPUEnsureACL(conn) < 0)
|
||||
goto cleanup;
|
||||
|
||||
cpu = cpuBaselineXML(xmlCPUs, ncpus, NULL, 0);
|
||||
cpu = cpuBaselineXML(xmlCPUs, ncpus, NULL, 0, flags);
|
||||
|
||||
cleanup:
|
||||
return cpu;
|
||||
|
@ -75,6 +75,7 @@ struct data {
|
||||
const char *modelsName;
|
||||
unsigned int nmodels;
|
||||
const char *preferred;
|
||||
unsigned int flags;
|
||||
int result;
|
||||
};
|
||||
|
||||
@ -330,7 +331,7 @@ cpuTestBaseline(const void *arg)
|
||||
if (!(cpus = cpuTestLoadMultiXML(data->arch, data->name, &ncpus)))
|
||||
goto cleanup;
|
||||
|
||||
baseline = cpuBaseline(cpus, ncpus, NULL, 0);
|
||||
baseline = cpuBaseline(cpus, ncpus, NULL, 0, data->flags);
|
||||
if (data->result < 0) {
|
||||
virResetLastError();
|
||||
if (!baseline)
|
||||
@ -510,12 +511,12 @@ mymain(void)
|
||||
}
|
||||
|
||||
#define DO_TEST(arch, api, name, host, cpu, \
|
||||
models, nmodels, preferred, result) \
|
||||
models, nmodels, preferred, flags, result) \
|
||||
do { \
|
||||
static struct data data = { \
|
||||
arch, api, host, cpu, models, \
|
||||
models == NULL ? NULL : #models, \
|
||||
nmodels, preferred, result \
|
||||
nmodels, preferred, flags, result \
|
||||
}; \
|
||||
if (cpuTestRun(name, &data) < 0) \
|
||||
ret = -1; \
|
||||
@ -524,31 +525,31 @@ mymain(void)
|
||||
#define DO_TEST_COMPARE(arch, host, cpu, result) \
|
||||
DO_TEST(arch, API_COMPARE, \
|
||||
host "/" cpu " (" #result ")", \
|
||||
host, cpu, NULL, 0, NULL, result)
|
||||
host, cpu, NULL, 0, NULL, 0, result)
|
||||
|
||||
#define DO_TEST_UPDATE(arch, host, cpu, result) \
|
||||
do { \
|
||||
DO_TEST(arch, API_UPDATE, \
|
||||
cpu " on " host, \
|
||||
host, cpu, NULL, 0, NULL, 0); \
|
||||
host, cpu, NULL, 0, NULL, 0, 0); \
|
||||
DO_TEST_COMPARE(arch, host, host "+" cpu, result); \
|
||||
} while (0)
|
||||
|
||||
#define DO_TEST_BASELINE(arch, name, result) \
|
||||
#define DO_TEST_BASELINE(arch, name, flags, result) \
|
||||
DO_TEST(arch, API_BASELINE, name, NULL, "baseline-" name, \
|
||||
NULL, 0, NULL, result)
|
||||
NULL, 0, NULL, flags, result)
|
||||
|
||||
#define DO_TEST_HASFEATURE(arch, host, feature, result) \
|
||||
DO_TEST(arch, API_HAS_FEATURE, \
|
||||
host "/" feature " (" #result ")", \
|
||||
host, feature, NULL, 0, NULL, result)
|
||||
host, feature, NULL, 0, NULL, 0, result)
|
||||
|
||||
#define DO_TEST_GUESTDATA(arch, host, cpu, models, preferred, result) \
|
||||
DO_TEST(arch, API_GUEST_DATA, \
|
||||
host "/" cpu " (" #models ", pref=" #preferred ")", \
|
||||
host, cpu, models, \
|
||||
models == NULL ? 0 : sizeof(models) / sizeof(char *), \
|
||||
preferred, result)
|
||||
preferred, 0, result)
|
||||
|
||||
/* host to host comparison */
|
||||
DO_TEST_COMPARE("x86", "host", "host", VIR_CPU_COMPARE_IDENTICAL);
|
||||
@ -593,11 +594,12 @@ mymain(void)
|
||||
DO_TEST_UPDATE("x86", "host", "host-passthrough", VIR_CPU_COMPARE_IDENTICAL);
|
||||
|
||||
/* computing baseline CPUs */
|
||||
DO_TEST_BASELINE("x86", "incompatible-vendors", -1);
|
||||
DO_TEST_BASELINE("x86", "no-vendor", 0);
|
||||
DO_TEST_BASELINE("x86", "some-vendors", 0);
|
||||
DO_TEST_BASELINE("x86", "1", 0);
|
||||
DO_TEST_BASELINE("x86", "2", 0);
|
||||
DO_TEST_BASELINE("x86", "incompatible-vendors", 0, -1);
|
||||
DO_TEST_BASELINE("x86", "no-vendor", 0, 0);
|
||||
DO_TEST_BASELINE("x86", "some-vendors", 0, 0);
|
||||
DO_TEST_BASELINE("x86", "1", 0, 0);
|
||||
DO_TEST_BASELINE("x86", "2", 0, 0);
|
||||
DO_TEST_BASELINE("x86", "3", VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES, 0);
|
||||
|
||||
/* CPU features */
|
||||
DO_TEST_HASFEATURE("x86", "host", "vmx", YES);
|
||||
|
35
tests/cputestdata/x86-baseline-3-result.xml
Normal file
35
tests/cputestdata/x86-baseline-3-result.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<cpu mode='custom' match='exact'>
|
||||
<model fallback='allow'>Westmere</model>
|
||||
<feature policy='require' name='lahf_lm'/>
|
||||
<feature policy='require' name='lm'/>
|
||||
<feature policy='require' name='nx'/>
|
||||
<feature policy='require' name='syscall'/>
|
||||
<feature policy='require' name='aes'/>
|
||||
<feature policy='require' name='popcnt'/>
|
||||
<feature policy='require' name='sse4.2'/>
|
||||
<feature policy='require' name='sse4.1'/>
|
||||
<feature policy='require' name='cx16'/>
|
||||
<feature policy='require' name='ssse3'/>
|
||||
<feature policy='require' name='pni'/>
|
||||
<feature policy='require' name='sse2'/>
|
||||
<feature policy='require' name='sse'/>
|
||||
<feature policy='require' name='fxsr'/>
|
||||
<feature policy='require' name='mmx'/>
|
||||
<feature policy='require' name='clflush'/>
|
||||
<feature policy='require' name='pse36'/>
|
||||
<feature policy='require' name='pat'/>
|
||||
<feature policy='require' name='cmov'/>
|
||||
<feature policy='require' name='mca'/>
|
||||
<feature policy='require' name='pge'/>
|
||||
<feature policy='require' name='mtrr'/>
|
||||
<feature policy='require' name='sep'/>
|
||||
<feature policy='require' name='apic'/>
|
||||
<feature policy='require' name='cx8'/>
|
||||
<feature policy='require' name='mce'/>
|
||||
<feature policy='require' name='pae'/>
|
||||
<feature policy='require' name='msr'/>
|
||||
<feature policy='require' name='tsc'/>
|
||||
<feature policy='require' name='pse'/>
|
||||
<feature policy='require' name='de'/>
|
||||
<feature policy='require' name='fpu'/>
|
||||
</cpu>
|
7
tests/cputestdata/x86-baseline-3.xml
Normal file
7
tests/cputestdata/x86-baseline-3.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<cpuTest>
|
||||
<cpu>
|
||||
<arch>x86_64</arch>
|
||||
<model>Westmere</model>
|
||||
<topology sockets='1' cores='2' threads='1'/>
|
||||
</cpu>
|
||||
</cpuTest>
|
@ -6157,6 +6157,10 @@ static const vshCmdOptDef opts_cpu_baseline[] = {
|
||||
.flags = VSH_OFLAG_REQ,
|
||||
.help = N_("file containing XML CPU descriptions")
|
||||
},
|
||||
{.name = "features",
|
||||
.type = VSH_OT_BOOL,
|
||||
.help = N_("Show features that are part of the CPU model type")
|
||||
},
|
||||
{.name = NULL}
|
||||
};
|
||||
|
||||
@ -6168,6 +6172,7 @@ cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
|
||||
char *buffer;
|
||||
char *result = NULL;
|
||||
const char **list = NULL;
|
||||
unsigned int flags = 0;
|
||||
int count = 0;
|
||||
|
||||
xmlDocPtr xml = NULL;
|
||||
@ -6177,6 +6182,9 @@ cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
size_t i;
|
||||
|
||||
if (vshCommandOptBool(cmd, "features"))
|
||||
flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES;
|
||||
|
||||
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
||||
return false;
|
||||
|
||||
@ -6220,7 +6228,7 @@ cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
|
||||
list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
|
||||
}
|
||||
|
||||
result = virConnectBaselineCPU(ctl->conn, list, count, 0);
|
||||
result = virConnectBaselineCPU(ctl->conn, list, count, flags);
|
||||
|
||||
if (result) {
|
||||
vshPrint(ctl, "%s", result);
|
||||
|
@ -485,13 +485,16 @@ cell and the total free memory on the machine. Finally, with a
|
||||
numeric argument or with --cellno plus a cell number it will display
|
||||
the free memory for the specified cell only.
|
||||
|
||||
=item B<cpu-baseline> I<FILE>
|
||||
=item B<cpu-baseline> I<FILE> [I<--features>]
|
||||
|
||||
Compute baseline CPU which will be supported by all host CPUs given in <file>.
|
||||
The list of host CPUs is built by extracting all <cpu> elements from the
|
||||
<file>. Thus, the <file> can contain either a set of <cpu> elements separated
|
||||
by new lines or even a set of complete <capabilities> elements printed by
|
||||
B<capabilities> command.
|
||||
B<capabilities> command. If I<--features> is specified then the
|
||||
resulting XML description will explicitly include all features that make
|
||||
up the CPU, without this option features that are part of the CPU model
|
||||
will not be listed in the XML description.
|
||||
|
||||
=item B<cpu-compare> I<FILE>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user