util: macmap: Convert to use GSList for storing macs instead of string lists

Since adding and removing is the main use case for the macmap module,
convert the code to a more efficient data structure.

The refactor also optimizes the loading from file where previously we'd
do a hash lookup + list lenght calculation for every entry.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Peter Krempa 2021-02-05 14:25:16 +01:00
parent 4ebc278657
commit 00dfd9c97d
3 changed files with 81 additions and 64 deletions

View File

@ -50,21 +50,18 @@ struct virMacMap {
static virClassPtr virMacMapClass; static virClassPtr virMacMapClass;
static int
virMacMapHashFree(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
g_strfreev(payload);
return 0;
}
static void static void
virMacMapDispose(void *obj) virMacMapDispose(void *obj)
{ {
virMacMapPtr mgr = obj; virMacMapPtr mgr = obj;
virHashForEach(mgr->macs, virMacMapHashFree, NULL); GHashTableIter htitr;
void *value;
g_hash_table_iter_init(&htitr, mgr->macs);
while (g_hash_table_iter_next(&htitr, NULL, &value))
g_slist_free_full(value, g_free);
virHashFree(mgr->macs); virHashFree(mgr->macs);
} }
@ -80,48 +77,57 @@ static int virMacMapOnceInit(void)
VIR_ONCE_GLOBAL_INIT(virMacMap); VIR_ONCE_GLOBAL_INIT(virMacMap);
static int static void
virMacMapAddLocked(virMacMapPtr mgr, virMacMapAddLocked(virMacMapPtr mgr,
const char *domain, const char *domain,
const char *mac) const char *mac)
{ {
char **macsList = NULL; GSList *orig_list;
GSList *list;
GSList *next;
if ((macsList = virHashLookup(mgr->macs, domain)) && list = orig_list = g_hash_table_lookup(mgr->macs, domain);
virStringListHasString((const char**) macsList, mac)) {
return 0; for (next = list; next; next = next->next) {
if (STREQ((const char *) next->data, mac))
return;
} }
if (virStringListAdd(&macsList, mac) < 0 || list = g_slist_append(list, g_strdup(mac));
virHashUpdateEntry(mgr->macs, domain, macsList) < 0)
return -1;
return 0; if (list != orig_list)
g_hash_table_insert(mgr->macs, g_strdup(domain), list);
} }
static int static void
virMacMapRemoveLocked(virMacMapPtr mgr, virMacMapRemoveLocked(virMacMapPtr mgr,
const char *domain, const char *domain,
const char *mac) const char *mac)
{ {
char **macsList = NULL; GSList *orig_list;
char **newMacsList = NULL; GSList *list;
GSList *next;
if (!(macsList = virHashLookup(mgr->macs, domain))) list = orig_list = g_hash_table_lookup(mgr->macs, domain);
return 0;
newMacsList = macsList; if (!orig_list)
virStringListRemove(&newMacsList, mac); return;
if (!newMacsList) {
virHashSteal(mgr->macs, domain); for (next = list; next; next = next->next) {
} else { if (STREQ((const char *) next->data, mac)) {
if (macsList != newMacsList && list = g_slist_remove_link(list, next);
virHashUpdateEntry(mgr->macs, domain, newMacsList) < 0) g_slist_free_full(next, g_free);
return -1; break;
}
} }
return 0; if (list != orig_list) {
if (list)
g_hash_table_insert(mgr->macs, g_strdup(domain), list);
else
g_hash_table_remove(mgr->macs, domain);
}
} }
@ -162,6 +168,7 @@ virMacMapLoadFile(virMacMapPtr mgr,
virJSONValuePtr macs; virJSONValuePtr macs;
const char *domain; const char *domain;
size_t j; size_t j;
GSList *vals = NULL;
if (!(domain = virJSONValueObjectGetString(tmp, "domain"))) { if (!(domain = virJSONValueObjectGetString(tmp, "domain"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@ -175,13 +182,21 @@ virMacMapLoadFile(virMacMapPtr mgr,
return -1; return -1;
} }
if (g_hash_table_contains(mgr->macs, domain)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("duplicate domain '%s'"), domain);
return -1;
}
for (j = 0; j < virJSONValueArraySize(macs); j++) { for (j = 0; j < virJSONValueArraySize(macs); j++) {
virJSONValuePtr macJSON = virJSONValueArrayGet(macs, j); virJSONValuePtr macJSON = virJSONValueArrayGet(macs, j);
const char *mac = virJSONValueGetString(macJSON);
if (virMacMapAddLocked(mgr, domain, mac) < 0) vals = g_slist_prepend(vals, g_strdup(virJSONValueGetString(macJSON)));
return -1;
} }
vals = g_slist_reverse(vals);
g_hash_table_insert(mgr->macs, g_strdup(domain), vals);
} }
return 0; return 0;
@ -195,11 +210,11 @@ virMACMapHashDumper(void *payload,
{ {
g_autoptr(virJSONValue) obj = virJSONValueNewObject(); g_autoptr(virJSONValue) obj = virJSONValueNewObject();
g_autoptr(virJSONValue) arr = virJSONValueNewArray(); g_autoptr(virJSONValue) arr = virJSONValueNewArray();
const char **macs = payload; GSList *macs = payload;
size_t i; GSList *next;
for (i = 0; macs[i]; i++) { for (next = macs; next; next = next->next) {
virJSONValuePtr m = virJSONValueNewString(macs[i]); virJSONValuePtr m = virJSONValueNewString((const char *) next->data);
if (!m || if (!m ||
virJSONValueArrayAppend(arr, m) < 0) { virJSONValueArrayAppend(arr, m) < 0) {
@ -279,8 +294,8 @@ virMacMapNew(const char *file)
return NULL; return NULL;
virObjectLock(mgr); virObjectLock(mgr);
if (!(mgr->macs = virHashNew(NULL)))
goto error; mgr->macs = virHashNew(NULL);
if (file && if (file &&
virMacMapLoadFile(mgr, file) < 0) virMacMapLoadFile(mgr, file) < 0)
@ -301,12 +316,10 @@ virMacMapAdd(virMacMapPtr mgr,
const char *domain, const char *domain,
const char *mac) const char *mac)
{ {
int ret;
virObjectLock(mgr); virObjectLock(mgr);
ret = virMacMapAddLocked(mgr, domain, mac); virMacMapAddLocked(mgr, domain, mac);
virObjectUnlock(mgr); virObjectUnlock(mgr);
return ret; return 0;
} }
@ -315,20 +328,19 @@ virMacMapRemove(virMacMapPtr mgr,
const char *domain, const char *domain,
const char *mac) const char *mac)
{ {
int ret;
virObjectLock(mgr); virObjectLock(mgr);
ret = virMacMapRemoveLocked(mgr, domain, mac); virMacMapRemoveLocked(mgr, domain, mac);
virObjectUnlock(mgr); virObjectUnlock(mgr);
return ret; return 0;
} }
const char *const * /* note that the returned pointer may be invalidated by other APIs in this module */
GSList *
virMacMapLookup(virMacMapPtr mgr, virMacMapLookup(virMacMapPtr mgr,
const char *domain) const char *domain)
{ {
const char *const *ret; GSList *ret;
virObjectLock(mgr); virObjectLock(mgr);
ret = virHashLookup(mgr->macs, domain); ret = virHashLookup(mgr->macs, domain);

View File

@ -20,6 +20,8 @@
#pragma once #pragma once
#include "internal.h"
typedef struct virMacMap virMacMap; typedef struct virMacMap virMacMap;
typedef virMacMap *virMacMapPtr; typedef virMacMap *virMacMapPtr;
@ -37,8 +39,8 @@ int virMacMapRemove(virMacMapPtr mgr,
const char *domain, const char *domain,
const char *mac); const char *mac);
const char *const *virMacMapLookup(virMacMapPtr mgr, GSList *virMacMapLookup(virMacMapPtr mgr,
const char *domain); const char *domain);
int virMacMapWriteFile(virMacMapPtr mgr, int virMacMapWriteFile(virMacMapPtr mgr,
const char *filename); const char *filename);

View File

@ -36,7 +36,8 @@ testMACLookup(const void *opaque)
{ {
const struct testData *data = opaque; const struct testData *data = opaque;
virMacMapPtr mgr = NULL; virMacMapPtr mgr = NULL;
const char * const * macs; GSList *macs;
GSList *next;
size_t i, j; size_t i, j;
char *file = NULL; char *file = NULL;
int ret = -1; int ret = -1;
@ -48,26 +49,27 @@ testMACLookup(const void *opaque)
macs = virMacMapLookup(mgr, data->domain); macs = virMacMapLookup(mgr, data->domain);
for (i = 0; macs && macs[i]; i++) { for (next = macs; next; next = next->next) {
for (j = 0; data->macs && data->macs[j]; j++) { for (j = 0; data->macs && data->macs[j]; j++) {
if (STREQ(macs[i], data->macs[j])) if (STREQ((const char *) next->data, data->macs[j]))
break; break;
} }
if (!data->macs || !data->macs[j]) { if (!data->macs || !data->macs[j]) {
fprintf(stderr, fprintf(stderr,
"Unexpected %s in the returned list of MACs\n", macs[i]); "Unexpected %s in the returned list of MACs\n",
(const char *) next->data);
goto cleanup; goto cleanup;
} }
} }
for (i = 0; data->macs && data->macs[i]; i++) { for (i = 0; data->macs && data->macs[i]; i++) {
for (j = 0; macs && macs[j]; j++) { for (next = macs; next; next = next->next) {
if (STREQ(data->macs[i], macs[j])) if (STREQ(data->macs[i], (const char *) next->data))
break; break;
} }
if (!macs || !macs[j]) { if (!next) {
fprintf(stderr, fprintf(stderr,
"Expected %s in the returned list of MACs\n", data->macs[i]); "Expected %s in the returned list of MACs\n", data->macs[i]);
goto cleanup; goto cleanup;
@ -87,7 +89,7 @@ testMACRemove(const void *opaque)
{ {
const struct testData *data = opaque; const struct testData *data = opaque;
virMacMapPtr mgr = NULL; virMacMapPtr mgr = NULL;
const char * const * macs; GSList *macs;
size_t i; size_t i;
char *file = NULL; char *file = NULL;
int ret = -1; int ret = -1;
@ -107,7 +109,8 @@ testMACRemove(const void *opaque)
if ((macs = virMacMapLookup(mgr, data->domain))) { if ((macs = virMacMapLookup(mgr, data->domain))) {
fprintf(stderr, fprintf(stderr,
"Not removed all MACs for domain %s: %s\n", data->domain, macs[0]); "Not removed all MACs for domain %s: %s\n",
data->domain, (const char *) macs->data);
goto cleanup; goto cleanup;
} }