mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
cpu_x86: Refactor storage of CPUID data to add support for KVM features
The CPUID functions were stored in multiple arrays according to a specified prefix of those. This made it very hard to add another prefix to store KVM CPUID features (0x40000000). Instead of hardcoding a third array this patch changes the approach used: The code is refactored to use a single array where the CPUID functions are stored ordered by the cpuid function so that they don't depend on the specific prefix and don't waste memory. The code is also less complex using this approach. A trateoff to this is the change from O(N) complexity to O(N^2) in x86DataAdd and x86DataSubtract. The rest of the functions were already using O(N^2) algorithms.
This commit is contained in:
parent
4ffcb0208c
commit
f80a11c921
@ -84,14 +84,13 @@ enum compare_result {
|
|||||||
|
|
||||||
|
|
||||||
struct virCPUx86DataIterator {
|
struct virCPUx86DataIterator {
|
||||||
virCPUx86Data *data;
|
const virCPUx86Data *data;
|
||||||
int pos;
|
int pos;
|
||||||
bool extended;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define virCPUx86DataIteratorInit(data) \
|
#define virCPUx86DataIteratorInit(data) \
|
||||||
{ data, -1, false }
|
{ data, -1 }
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -120,6 +119,9 @@ static void
|
|||||||
x86cpuidSetBits(virCPUx86CPUID *cpuid,
|
x86cpuidSetBits(virCPUx86CPUID *cpuid,
|
||||||
const virCPUx86CPUID *mask)
|
const virCPUx86CPUID *mask)
|
||||||
{
|
{
|
||||||
|
if (!mask)
|
||||||
|
return;
|
||||||
|
|
||||||
cpuid->eax |= mask->eax;
|
cpuid->eax |= mask->eax;
|
||||||
cpuid->ebx |= mask->ebx;
|
cpuid->ebx |= mask->ebx;
|
||||||
cpuid->ecx |= mask->ecx;
|
cpuid->ecx |= mask->ecx;
|
||||||
@ -131,6 +133,9 @@ static void
|
|||||||
x86cpuidClearBits(virCPUx86CPUID *cpuid,
|
x86cpuidClearBits(virCPUx86CPUID *cpuid,
|
||||||
const virCPUx86CPUID *mask)
|
const virCPUx86CPUID *mask)
|
||||||
{
|
{
|
||||||
|
if (!mask)
|
||||||
|
return;
|
||||||
|
|
||||||
cpuid->eax &= ~mask->eax;
|
cpuid->eax &= ~mask->eax;
|
||||||
cpuid->ebx &= ~mask->ebx;
|
cpuid->ebx &= ~mask->ebx;
|
||||||
cpuid->ecx &= ~mask->ecx;
|
cpuid->ecx &= ~mask->ecx;
|
||||||
@ -142,42 +147,45 @@ static void
|
|||||||
x86cpuidAndBits(virCPUx86CPUID *cpuid,
|
x86cpuidAndBits(virCPUx86CPUID *cpuid,
|
||||||
const virCPUx86CPUID *mask)
|
const virCPUx86CPUID *mask)
|
||||||
{
|
{
|
||||||
|
if (!mask)
|
||||||
|
return;
|
||||||
|
|
||||||
cpuid->eax &= mask->eax;
|
cpuid->eax &= mask->eax;
|
||||||
cpuid->ebx &= mask->ebx;
|
cpuid->ebx &= mask->ebx;
|
||||||
cpuid->ecx &= mask->ecx;
|
cpuid->ecx &= mask->ecx;
|
||||||
cpuid->edx &= mask->edx;
|
cpuid->edx &= mask->edx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
virCPUx86CPUIDSorter(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
virCPUx86CPUID *da = (virCPUx86CPUID *) a;
|
||||||
|
virCPUx86CPUID *db = (virCPUx86CPUID *) b;
|
||||||
|
|
||||||
|
if (da->function > db->function)
|
||||||
|
return 1;
|
||||||
|
else if (da->function < db->function)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* skips all zero CPUID leafs */
|
/* skips all zero CPUID leafs */
|
||||||
static virCPUx86CPUID *
|
static virCPUx86CPUID *
|
||||||
x86DataCpuidNext(struct virCPUx86DataIterator *iterator)
|
x86DataCpuidNext(struct virCPUx86DataIterator *iterator)
|
||||||
{
|
{
|
||||||
virCPUx86CPUID *ret;
|
const virCPUx86Data *data = iterator->data;
|
||||||
virCPUx86Data *data = iterator->data;
|
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
do {
|
while (++iterator->pos < data->len) {
|
||||||
ret = NULL;
|
if (!x86cpuidMatch(data->data + iterator->pos, &cpuidNull))
|
||||||
iterator->pos++;
|
return data->data + iterator->pos;
|
||||||
|
}
|
||||||
|
|
||||||
if (!iterator->extended) {
|
return NULL;
|
||||||
if (iterator->pos < data->basic_len)
|
|
||||||
ret = data->basic + iterator->pos;
|
|
||||||
else {
|
|
||||||
iterator->extended = true;
|
|
||||||
iterator->pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iterator->extended && iterator->pos < data->extended_len) {
|
|
||||||
ret = data->extended + iterator->pos;
|
|
||||||
}
|
|
||||||
} while (ret && x86cpuidMatch(ret, &cpuidNull));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -185,36 +193,23 @@ static virCPUx86CPUID *
|
|||||||
x86DataCpuid(const virCPUx86Data *data,
|
x86DataCpuid(const virCPUx86Data *data,
|
||||||
uint32_t function)
|
uint32_t function)
|
||||||
{
|
{
|
||||||
virCPUx86CPUID *cpuids;
|
|
||||||
int len;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (function < CPUX86_EXTENDED) {
|
for (i = 0; i < data->len; i++) {
|
||||||
cpuids = data->basic;
|
if (data->data[i].function == function)
|
||||||
len = data->basic_len;
|
return data->data + i;
|
||||||
i = function;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cpuids = data->extended;
|
|
||||||
len = data->extended_len;
|
|
||||||
i = function - CPUX86_EXTENDED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < len && !x86cpuidMatch(cpuids + i, &cpuidNull))
|
return NULL;
|
||||||
return cpuids + i;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
virCPUx86DataFree(virCPUx86Data *data)
|
virCPUx86DataFree(virCPUx86Data *data)
|
||||||
{
|
{
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VIR_FREE(data->basic);
|
VIR_FREE(data->data);
|
||||||
VIR_FREE(data->extended);
|
|
||||||
VIR_FREE(data);
|
VIR_FREE(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,78 +246,37 @@ x86DataCopy(const virCPUx86Data *data)
|
|||||||
virCPUx86Data *copy = NULL;
|
virCPUx86Data *copy = NULL;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (VIR_ALLOC(copy) < 0
|
if (VIR_ALLOC(copy) < 0 ||
|
||||||
|| VIR_ALLOC_N(copy->basic, data->basic_len) < 0
|
VIR_ALLOC_N(copy->data, data->len) < 0) {
|
||||||
|| VIR_ALLOC_N(copy->extended, data->extended_len) < 0) {
|
|
||||||
virCPUx86DataFree(copy);
|
virCPUx86DataFree(copy);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy->basic_len = data->basic_len;
|
copy->len = data->len;
|
||||||
for (i = 0; i < data->basic_len; i++)
|
for (i = 0; i < data->len; i++)
|
||||||
copy->basic[i] = data->basic[i];
|
copy->data[i] = data->data[i];
|
||||||
|
|
||||||
copy->extended_len = data->extended_len;
|
|
||||||
for (i = 0; i < data->extended_len; i++)
|
|
||||||
copy->extended[i] = data->extended[i];
|
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
x86DataExpand(virCPUx86Data *data,
|
|
||||||
int basic_by,
|
|
||||||
int extended_by)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (basic_by > 0) {
|
|
||||||
size_t len = data->basic_len;
|
|
||||||
if (VIR_EXPAND_N(data->basic, data->basic_len, basic_by) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (i = 0; i < basic_by; i++)
|
|
||||||
data->basic[len + i].function = len + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extended_by > 0) {
|
|
||||||
size_t len = data->extended_len;
|
|
||||||
if (VIR_EXPAND_N(data->extended, data->extended_len, extended_by) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (i = 0; i < extended_by; i++)
|
|
||||||
data->extended[len + i].function = len + i + CPUX86_EXTENDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
virCPUx86DataAddCPUID(virCPUx86Data *data,
|
virCPUx86DataAddCPUID(virCPUx86Data *data,
|
||||||
const virCPUx86CPUID *cpuid)
|
const virCPUx86CPUID *cpuid)
|
||||||
{
|
{
|
||||||
unsigned int basic_by = 0;
|
virCPUx86CPUID *existing;
|
||||||
unsigned int extended_by = 0;
|
|
||||||
virCPUx86CPUID **cpuids;
|
|
||||||
unsigned int pos;
|
|
||||||
|
|
||||||
if (cpuid->function < CPUX86_EXTENDED) {
|
if ((existing = x86DataCpuid(data, cpuid->function))) {
|
||||||
pos = cpuid->function;
|
x86cpuidSetBits(existing, cpuid);
|
||||||
basic_by = pos + 1 - data->basic_len;
|
|
||||||
cpuids = &data->basic;
|
|
||||||
} else {
|
} else {
|
||||||
pos = cpuid->function - CPUX86_EXTENDED;
|
if (VIR_APPEND_ELEMENT_COPY(data->data, data->len,
|
||||||
extended_by = pos + 1 - data->extended_len;
|
*((virCPUx86CPUID *)cpuid)) < 0)
|
||||||
cpuids = &data->extended;
|
return -1;
|
||||||
|
|
||||||
|
qsort(data->data, data->len,
|
||||||
|
sizeof(virCPUx86CPUID), virCPUx86CPUIDSorter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x86DataExpand(data, basic_by, extended_by) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
x86cpuidSetBits((*cpuids) + pos, cpuid);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,21 +285,19 @@ static int
|
|||||||
x86DataAdd(virCPUx86Data *data1,
|
x86DataAdd(virCPUx86Data *data1,
|
||||||
const virCPUx86Data *data2)
|
const virCPUx86Data *data2)
|
||||||
{
|
{
|
||||||
size_t i;
|
struct virCPUx86DataIterator iter = virCPUx86DataIteratorInit(data2);
|
||||||
|
virCPUx86CPUID *cpuid1;
|
||||||
|
virCPUx86CPUID *cpuid2;
|
||||||
|
|
||||||
if (x86DataExpand(data1,
|
while ((cpuid2 = x86DataCpuidNext(&iter))) {
|
||||||
data2->basic_len - data1->basic_len,
|
cpuid1 = x86DataCpuid(data1, cpuid2->function);
|
||||||
data2->extended_len - data1->extended_len) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (i = 0; i < data2->basic_len; i++) {
|
if (cpuid1) {
|
||||||
x86cpuidSetBits(data1->basic + i,
|
x86cpuidSetBits(cpuid1, cpuid2);
|
||||||
data2->basic + i);
|
} else {
|
||||||
}
|
if (virCPUx86DataAddCPUID(data1, cpuid2) < 0)
|
||||||
|
return -1;
|
||||||
for (i = 0; i < data2->extended_len; i++) {
|
}
|
||||||
x86cpuidSetBits(data1->extended + i,
|
|
||||||
data2->extended + i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -356,19 +308,13 @@ static void
|
|||||||
x86DataSubtract(virCPUx86Data *data1,
|
x86DataSubtract(virCPUx86Data *data1,
|
||||||
const virCPUx86Data *data2)
|
const virCPUx86Data *data2)
|
||||||
{
|
{
|
||||||
size_t i;
|
struct virCPUx86DataIterator iter = virCPUx86DataIteratorInit(data1);
|
||||||
unsigned int len;
|
virCPUx86CPUID *cpuid1;
|
||||||
|
virCPUx86CPUID *cpuid2;
|
||||||
|
|
||||||
len = MIN(data1->basic_len, data2->basic_len);
|
while ((cpuid1 = x86DataCpuidNext(&iter))) {
|
||||||
for (i = 0; i < len; i++) {
|
cpuid2 = x86DataCpuid(data2, cpuid1->function);
|
||||||
x86cpuidClearBits(data1->basic + i,
|
x86cpuidClearBits(cpuid1, cpuid2);
|
||||||
data2->basic + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = MIN(data1->extended_len, data2->extended_len);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
x86cpuidClearBits(data1->extended + i,
|
|
||||||
data2->extended + i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1763,25 +1709,23 @@ cpuidCall(virCPUx86CPUID *cpuid)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cpuidSet(uint32_t base, virCPUx86CPUID **set)
|
cpuidSet(uint32_t base, virCPUx86Data *data)
|
||||||
{
|
{
|
||||||
uint32_t max;
|
uint32_t max;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
virCPUx86CPUID cpuid = { base, 0, 0, 0, 0 };
|
virCPUx86CPUID cpuid = { base, 0, 0, 0, 0 };
|
||||||
|
|
||||||
cpuidCall(&cpuid);
|
cpuidCall(&cpuid);
|
||||||
max = cpuid.eax - base;
|
max = cpuid.eax;
|
||||||
|
|
||||||
if (VIR_ALLOC_N(*set, max + 1) < 0)
|
for (i = base; i <= max; i++) {
|
||||||
return -1;
|
cpuid.function = i;
|
||||||
|
|
||||||
for (i = 0; i <= max; i++) {
|
|
||||||
cpuid.function = base | i;
|
|
||||||
cpuidCall(&cpuid);
|
cpuidCall(&cpuid);
|
||||||
(*set)[i] = cpuid;
|
if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return max + 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1790,18 +1734,15 @@ x86NodeData(virArch arch)
|
|||||||
{
|
{
|
||||||
virCPUDataPtr cpuData = NULL;
|
virCPUDataPtr cpuData = NULL;
|
||||||
virCPUx86Data *data;
|
virCPUx86Data *data;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (VIR_ALLOC(data) < 0)
|
if (VIR_ALLOC(data) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if ((ret = cpuidSet(CPUX86_BASIC, &data->basic)) < 0)
|
if (cpuidSet(CPUX86_BASIC, data) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
data->basic_len = ret;
|
|
||||||
|
|
||||||
if ((ret = cpuidSet(CPUX86_EXTENDED, &data->extended)) < 0)
|
if (cpuidSet(CPUX86_EXTENDED, data) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
data->extended_len = ret;
|
|
||||||
|
|
||||||
if (!(cpuData = virCPUx86MakeData(arch, &data)))
|
if (!(cpuData = virCPUx86MakeData(arch, &data)))
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* cpu_x86_data.h: x86 specific CPU data
|
* cpu_x86_data.h: x86 specific CPU data
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009-2010 Red Hat, Inc.
|
* Copyright (C) 2009-2010, 2013 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -40,10 +40,8 @@ struct _virCPUx86CPUID {
|
|||||||
|
|
||||||
typedef struct _virCPUx86Data virCPUx86Data;
|
typedef struct _virCPUx86Data virCPUx86Data;
|
||||||
struct _virCPUx86Data {
|
struct _virCPUx86Data {
|
||||||
size_t basic_len;
|
size_t len;
|
||||||
virCPUx86CPUID *basic;
|
virCPUx86CPUID *data;
|
||||||
size_t extended_len;
|
|
||||||
virCPUx86CPUID *extended;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __VIR_CPU_X86_DATA_H__ */
|
#endif /* __VIR_CPU_X86_DATA_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user