Skip to content

Commit

Permalink
plugins: scoreboard API
Browse files Browse the repository at this point in the history
We introduce a cpu local storage, automatically managed (and extended)
by QEMU itself. Plugin allocate a scoreboard, and don't have to deal
with how many cpus are launched.

This API will be used by new inline functions but callbacks can benefit
from this as well. This way, they can operate without a global lock for
simple operations.

At any point during execution, any scoreboard will be dimensioned with
at least qemu_plugin_num_vcpus entries.

New functions:
- qemu_plugin_scoreboard_find
- qemu_plugin_scoreboard_free
- qemu_plugin_scoreboard_new

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Message-Id: <20240304130036.124418-2-pierrick.bouvier@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-Id: <20240305121005.3528075-15-alex.bennee@linaro.org>
  • Loading branch information
pbo-linaro authored and stsquad committed Mar 6, 2024
1 parent b9504c9 commit a3c2cf0
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 0 deletions.
6 changes: 6 additions & 0 deletions include/qemu/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ struct qemu_plugin_insn {
bool mem_only;
};

/* A scoreboard is an array of values, indexed by vcpu_index */
struct qemu_plugin_scoreboard {
GArray *data;
QLIST_ENTRY(qemu_plugin_scoreboard) entry;
};

/*
* qemu_plugin_insn allocate and cleanup functions. We don't expect to
* cleanup many of these structures. They are reused for each fresh
Expand Down
31 changes: 31 additions & 0 deletions include/qemu/qemu-plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
struct qemu_plugin_tb;
/** struct qemu_plugin_insn - Opaque handle for a translated instruction */
struct qemu_plugin_insn;
/** struct qemu_plugin_scoreboard - Opaque handle for a scoreboard */
struct qemu_plugin_scoreboard;

/**
* enum qemu_plugin_cb_flags - type of callback
Expand Down Expand Up @@ -752,5 +754,34 @@ QEMU_PLUGIN_API
int qemu_plugin_read_register(struct qemu_plugin_register *handle,
GByteArray *buf);

/**
* qemu_plugin_scoreboard_new() - alloc a new scoreboard
*
* @element_size: size (in bytes) for one entry
*
* Returns a pointer to a new scoreboard. It must be freed using
* qemu_plugin_scoreboard_free.
*/
QEMU_PLUGIN_API
struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size);

/**
* qemu_plugin_scoreboard_free() - free a scoreboard
* @score: scoreboard to free
*/
QEMU_PLUGIN_API
void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score);

/**
* qemu_plugin_scoreboard_find() - get pointer to an entry of a scoreboard
* @score: scoreboard to query
* @vcpu_index: entry index
*
* Returns address of entry of a scoreboard matching a given vcpu_index. This
* address can be modified later if scoreboard is resized.
*/
QEMU_PLUGIN_API
void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score,
unsigned int vcpu_index);

#endif /* QEMU_QEMU_PLUGIN_H */
19 changes: 19 additions & 0 deletions plugins/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,22 @@ int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)

return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg));
}

struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size)
{
return plugin_scoreboard_new(element_size);
}

void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score)
{
plugin_scoreboard_free(score);
}

void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score,
unsigned int vcpu_index)
{
g_assert(vcpu_index < qemu_plugin_num_vcpus());
/* we can't use g_array_index since entry size is not statically known */
char *base_ptr = score->data->data;
return base_ptr + vcpu_index * g_array_get_element_size(score->data);
}
57 changes: 57 additions & 0 deletions plugins/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "qemu/lockable.h"
#include "qemu/option.h"
#include "qemu/plugin.h"
#include "qemu/queue.h"
#include "qemu/rcu_queue.h"
#include "qemu/xxhash.h"
#include "qemu/rcu.h"
Expand Down Expand Up @@ -215,6 +216,35 @@ CPUPluginState *qemu_plugin_create_vcpu_state(void)
return g_new0(CPUPluginState, 1);
}

static void plugin_grow_scoreboards__locked(CPUState *cpu)
{
if (cpu->cpu_index < plugin.scoreboard_alloc_size) {
return;
}

bool need_realloc = FALSE;
while (cpu->cpu_index >= plugin.scoreboard_alloc_size) {
plugin.scoreboard_alloc_size *= 2;
need_realloc = TRUE;
}


if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) {
/* nothing to do, we just updated sizes for future scoreboards */
return;
}

/* cpus must be stopped, as tb might still use an existing scoreboard. */
start_exclusive();
struct qemu_plugin_scoreboard *score;
QLIST_FOREACH(score, &plugin.scoreboards, entry) {
g_array_set_size(score->data, plugin.scoreboard_alloc_size);
}
/* force all tb to be flushed, as scoreboard pointers were changed. */
tb_flush(cpu);
end_exclusive();
}

void qemu_plugin_vcpu_init_hook(CPUState *cpu)
{
bool success;
Expand All @@ -225,6 +255,7 @@ void qemu_plugin_vcpu_init_hook(CPUState *cpu)
success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index,
&cpu->cpu_index);
g_assert(success);
plugin_grow_scoreboards__locked(cpu);
qemu_rec_mutex_unlock(&plugin.lock);

plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
Expand Down Expand Up @@ -578,6 +609,8 @@ static void __attribute__((__constructor__)) plugin_init(void)
qemu_rec_mutex_init(&plugin.lock);
plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal);
plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal);
QLIST_INIT(&plugin.scoreboards);
plugin.scoreboard_alloc_size = 16; /* avoid frequent reallocation */
QTAILQ_INIT(&plugin.ctxs);
qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16,
QHT_MODE_AUTO_RESIZE);
Expand All @@ -588,3 +621,27 @@ int plugin_num_vcpus(void)
{
return plugin.num_vcpus;
}

struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size)
{
struct qemu_plugin_scoreboard *score =
g_malloc0(sizeof(struct qemu_plugin_scoreboard));
score->data = g_array_new(FALSE, TRUE, element_size);
g_array_set_size(score->data, plugin.scoreboard_alloc_size);

qemu_rec_mutex_lock(&plugin.lock);
QLIST_INSERT_HEAD(&plugin.scoreboards, score, entry);
qemu_rec_mutex_unlock(&plugin.lock);

return score;
}

void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score)
{
qemu_rec_mutex_lock(&plugin.lock);
QLIST_REMOVE(score, entry);
qemu_rec_mutex_unlock(&plugin.lock);

g_array_free(score->data, TRUE);
g_free(score);
}
6 changes: 6 additions & 0 deletions plugins/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct qemu_plugin_state {
* but with the HT we avoid adding a field to CPUState.
*/
GHashTable *cpu_ht;
QLIST_HEAD(, qemu_plugin_scoreboard) scoreboards;
size_t scoreboard_alloc_size;
DECLARE_BITMAP(mask, QEMU_PLUGIN_EV_MAX);
/*
* @lock protects the struct as well as ctx->uninstalling.
Expand Down Expand Up @@ -101,4 +103,8 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb);

int plugin_num_vcpus(void);

struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size);

void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score);

#endif /* PLUGIN_H */
3 changes: 3 additions & 0 deletions plugins/qemu-plugins.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
qemu_plugin_register_vcpu_tb_exec_inline;
qemu_plugin_register_vcpu_tb_trans_cb;
qemu_plugin_reset;
qemu_plugin_scoreboard_free;
qemu_plugin_scoreboard_find;
qemu_plugin_scoreboard_new;
qemu_plugin_start_code;
qemu_plugin_tb_get_insn;
qemu_plugin_tb_n_insns;
Expand Down

0 comments on commit a3c2cf0

Please sign in to comment.