[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] migration: savevm_state_insert_handler: constant-time element in
From: |
Scott Cheloha |
Subject: |
[PATCH] migration: savevm_state_insert_handler: constant-time element insertion |
Date: |
Wed, 16 Oct 2019 11:41:56 -0500 |
Registering a SaveStateEntry object via savevm_state_insert_handler()
is an O(n) operation because the list is a priority queue maintained by
walking the list from head to tail to find a suitable insertion point.
This adds considerable overhead for VMs with many such objects. For
instance, ppc64 machines with large maxmem (8T+) spend ~10% or more of
their CPU time in savevm_state_insert_handler() before attempting to
boot a kernel.
If we track the head for each priority's subqueue we can insert new
elements in constant time.
This commit also introduces a new function, savevm_state_remove_handler(),
which abstracts the logic for replacing the head of an element's subqueue
when removing it.
Signed-off-by: Scott Cheloha <address@hidden>
---
migration/savevm.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/migration/savevm.c b/migration/savevm.c
index 8d95e261f6..f7a2d36bba 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -250,6 +250,7 @@ typedef struct SaveStateEntry {
typedef struct SaveState {
QTAILQ_HEAD(, SaveStateEntry) handlers;
+ SaveStateEntry *handler_pri_head[MIG_PRI_MAX + 1];
int global_section_id;
uint32_t len;
const char *name;
@@ -261,6 +262,7 @@ typedef struct SaveState {
static SaveState savevm_state = {
.handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers),
+ .handler_pri_head = { [MIG_PRI_DEFAULT ... MIG_PRI_MAX] = NULL },
.global_section_id = 0,
};
@@ -709,20 +711,43 @@ static void savevm_state_handler_insert(SaveStateEntry
*nse)
{
MigrationPriority priority = save_state_priority(nse);
SaveStateEntry *se;
+ int i;
assert(priority <= MIG_PRI_MAX);
- QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
- if (save_state_priority(se) < priority) {
+ for (i = priority - 1; i >= 0; i--) {
+ se = savevm_state.handler_pri_head[i];
+ if (se != NULL) {
+ assert(save_state_priority(se) < priority);
break;
}
}
- if (se) {
+ if (i >= 0) {
QTAILQ_INSERT_BEFORE(se, nse, entry);
} else {
QTAILQ_INSERT_TAIL(&savevm_state.handlers, nse, entry);
}
+
+ if (savevm_state.handler_pri_head[priority] == NULL) {
+ savevm_state.handler_pri_head[priority] = nse;
+ }
+}
+
+static void savevm_state_handler_remove(SaveStateEntry *se)
+{
+ SaveStateEntry *next;
+ MigrationPriority priority = save_state_priority(se);
+
+ if (se == savevm_state.handler_pri_head[priority]) {
+ next = QTAILQ_NEXT(se, entry);
+ if (next != NULL && save_state_priority(next) == priority) {
+ savevm_state.handler_pri_head[priority] = next;
+ } else {
+ savevm_state.handler_pri_head[priority] = NULL;
+ }
+ }
+ QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
}
/* TODO: Individual devices generally have very little idea about the rest
@@ -777,7 +802,7 @@ void unregister_savevm(DeviceState *dev, const char *idstr,
void *opaque)
QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) {
if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
- QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
+ savevm_state_handler_remove(se);
g_free(se->compat);
g_free(se);
}
@@ -841,7 +866,7 @@ void vmstate_unregister(DeviceState *dev, const
VMStateDescription *vmsd,
QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) {
if (se->vmsd == vmsd && se->opaque == opaque) {
- QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
+ savevm_state_handler_remove(se);
g_free(se->compat);
g_free(se);
}
--
2.23.0
- [PATCH] migration: savevm_state_insert_handler: constant-time element insertion,
Scott Cheloha <=