All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Pranith Kumar <bobby.prani@gmail.com>
Cc: "Emilio G. Cota" <cota@braap.org>, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v3 08/50] plugin: add core code
Date: Wed, 19 Jun 2019 12:46:54 +0100	[thread overview]
Message-ID: <871rzpajbl.fsf@zen.linaroharston> (raw)
In-Reply-To: <CADYwmhGiU_1GrBrR_tzBx+Lw+Hs3=Hi3AoPxRwkEj2pv9awqUg@mail.gmail.com>


Pranith Kumar <bobby.prani@gmail.com> writes:

> On Fri, Jun 14, 2019 at 10:30 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> Signed-off-by: Emilio G. Cota <cota@braap.org>
>> [AJB: moved directory and merged various fixes]
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>
>> ---
>> v3
>>   - moved to plugins/
>>   - merged
>>     plugin: remove uninstall_cb_t and just use simple_cb_t
>>     plugin: handle uninstall when !current_cpu
>>     plugin: introduce qemu_plugin_reset
>>   - split into core/loader
>>   - use GArray/GByteArray/GPtrArray instead of homegrown funcs
>> ---
>>  Makefile              |  10 +-
>>  Makefile.target       |   2 +
>>  include/qemu/plugin.h | 261 +++++++++++++++++++++
>>  include/qom/cpu.h     |   6 +
>>  plugins/Makefile.objs |   6 +
>>  plugins/core.c        | 524 ++++++++++++++++++++++++++++++++++++++++++
>>  plugins/loader.c      | 353 ++++++++++++++++++++++++++++
>>  plugins/plugin.h      |  95 ++++++++
>>  8 files changed, 1256 insertions(+), 1 deletion(-)
>>  create mode 100644 include/qemu/plugin.h
>>  create mode 100644 plugins/Makefile.objs
>>  create mode 100644 plugins/core.c
>>  create mode 100644 plugins/loader.c
>>  create mode 100644 plugins/plugin.h
>>
>> diff --git a/Makefile b/Makefile
>> index 8e2fc6624c..37d396ab88 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -827,8 +827,13 @@ endif
>>
>>  ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
>>
>> -install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
>> +install-includedir:
>> +       $(INSTALL_DIR) "$(DESTDIR)$(includedir)"
>> +
>> +install: all $(if $(BUILD_DOCS),install-doc) \
>> +       install-datadir install-localstatedir install-includedir \
>>         $(if $(INSTALL_BLOBS),$(edk2-decompressed))
>> +
>>  ifneq ($(TOOLS),)
>>         $(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
>>  endif
>> @@ -888,6 +893,9 @@ endif
>>                 "$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
>>  ifdef CONFIG_GTK
>>         $(MAKE) -C po $@
>> +endif
>> +ifeq ($(CONFIG_PLUGIN),y)
>> +       $(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h"
>>  endif
>>         $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
>>         set -e; for x in $(KEYMAPS); do \
>> diff --git a/Makefile.target b/Makefile.target
>> index ecd856e3a3..d492e24da2 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -118,6 +118,8 @@ obj-y += target/$(TARGET_BASE_ARCH)/
>>  obj-y += disas.o
>>  obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
>>
>> +obj-$(CONFIG_PLUGIN) += plugins/
>> +
>>  #########################################################
>>  # Linux user emulator target
>>
>> diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
>> new file mode 100644
>> index 0000000000..93e39f44d2
>> --- /dev/null
>> +++ b/include/qemu/plugin.h
>> @@ -0,0 +1,261 @@
>> +/*
>> + * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
>> + *
>> + * License: GNU GPL, version 2 or later.
>> + *   See the COPYING file in the top-level directory.
>> + */
>> +#ifndef QEMU_PLUGIN_H
>> +#define QEMU_PLUGIN_H
>> +
>> +#include "qemu/config-file.h"
>> +#include "qemu/qemu-plugin.h"
>> +#include "qemu/error-report.h"
>> +#include "qemu/queue.h"
>> +#include "qemu/option.h"
>> +
>> +/*
>> + * Option parsing/processing.
>> + * Note that we can load an arbitrary number of plugins.
>> + */
>> +struct qemu_plugin_desc;
>> +typedef QTAILQ_HEAD(, qemu_plugin_desc) QemuPluginList;
>> +
>> +#ifdef CONFIG_PLUGIN
>> +extern QemuOptsList qemu_plugin_opts;
>> +
>> +static inline void qemu_plugin_add_opts(void)
>> +{
>> +    qemu_add_opts(&qemu_plugin_opts);
>> +}
>> +
>> +void qemu_plugin_opt_parse(const char *optarg, QemuPluginList *head);
>> +int qemu_plugin_load_list(QemuPluginList *head);
>> +#else /* !CONFIG_PLUGIN */
>> +static inline void qemu_plugin_add_opts(void)
>> +{ }
>> +
>> +static inline void qemu_plugin_opt_parse(const char *optarg,
>> +                                         QemuPluginList *head)
>> +{
>> +    error_report("plugin interface not enabled in this build");
>> +    exit(1);
>> +}
>> +
>> +static inline int qemu_plugin_load_list(QemuPluginList *head)
>> +{
>> +    return 0;
>> +}
>> +#endif /* !CONFIG_PLUGIN */
>> +
>> +/*
>> + * Events that plugins can subscribe to.
>> + */
>> +enum qemu_plugin_event {
>> +    QEMU_PLUGIN_EV_VCPU_INIT,
>> +    QEMU_PLUGIN_EV_VCPU_EXIT,
>> +    QEMU_PLUGIN_EV_VCPU_TB_TRANS,
>> +    QEMU_PLUGIN_EV_VCPU_IDLE,
>> +    QEMU_PLUGIN_EV_VCPU_RESUME,
>> +    QEMU_PLUGIN_EV_VCPU_SYSCALL,
>> +    QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
>> +    QEMU_PLUGIN_EV_FLUSH,
>> +    QEMU_PLUGIN_EV_ATEXIT,
>> +    QEMU_PLUGIN_EV_MAX,
>> +};
>
> All the events but for MAX are descriptive. Care to add a comment for
> the last one?

Ok.

>
>> +
>> +union qemu_plugin_cb_sig {
>> +    qemu_plugin_simple_cb_t          simple;
>> +    qemu_plugin_udata_cb_t           udata;
>> +    qemu_plugin_vcpu_simple_cb_t     vcpu_simple;
>> +    qemu_plugin_vcpu_udata_cb_t      vcpu_udata;
>> +    qemu_plugin_vcpu_tb_trans_cb_t   vcpu_tb_trans;
>> +    qemu_plugin_vcpu_mem_cb_t        vcpu_mem;
>> +    qemu_plugin_vcpu_mem_haddr_cb_t  vcpu_mem_haddr;
>> +    qemu_plugin_vcpu_syscall_cb_t    vcpu_syscall;
>> +    qemu_plugin_vcpu_syscall_ret_cb_t vcpu_syscall_ret;
>> +    void *generic;
>> +};
>> +
>> +enum plugin_dyn_cb_type {
>> +    PLUGIN_CB_INSN,
>> +    PLUGIN_CB_MEM,
>> +    PLUGIN_CB_HADDR,
>> +    PLUGIN_N_CB_TYPES,
>> +};
>
> This might be better to have the types as powers of 2. It will allow
> us to define a plugin by comibining multiple types using OR.

To what end? It's not like you can have multiple prototypes to the call back.

>
>
>> +
>> +enum plugin_dyn_cb_subtype {
>> +    PLUGIN_CB_REGULAR,
>> +    PLUGIN_CB_INLINE,
>> +    PLUGIN_N_CB_SUBTYPES,
>> +};
>> +
>> +/*
>> + * A dynamic callback has an insertion point that is determined at run-time.
>> + * Usually the insertion point is somewhere in the code cache; think for
>> + * instance of a callback to be called upon the execution of a particular TB.
>> + */
>> +struct qemu_plugin_dyn_cb {
>> +    union qemu_plugin_cb_sig f;
>> +    void *userp;
>> +    unsigned tcg_flags;
>> +    enum plugin_dyn_cb_type type;
>> +    /* @rw applies to mem callbacks only (both regular and inline) */
>> +    enum qemu_plugin_mem_rw rw;
>> +    /* fields specific to each dyn_cb type go here */
>> +    union {
>> +        struct {
>> +            bool haddr;
>> +        } mem;
>> +        struct {
>> +            enum qemu_plugin_op op;
>> +            uint64_t imm;
>> +        } inline_insn;
>> +    };
>> +};
>> +
>> +struct qemu_plugin_insn {
>> +    GByteArray *data;
>> +    uint64_t vaddr;
>> +    void *haddr;
>> +    GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
>> +    bool calls_helpers;
>> +    bool mem_helper;
>> +};
>> +
>> +/*
>> + * qemu_plugin_insn allocate and cleanup functions. We don't expect to
>> + * cleanup many of these structures. They are reused for each fresh
>> + * translation.
>> + */
>> +
>> +static inline void qemu_plugin_insn_cleanup_fn(gpointer data)
>> +{
>> +    struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
>> +    g_byte_array_free(insn->data, true);
>> +}
>> +
>> +static inline struct qemu_plugin_insn * qemu_plugin_insn_alloc(void)
>> +{
>> +    int i, j;
>> +    struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
>> +    insn->data = g_byte_array_sized_new(4);
>> +
>> +    for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
>> +        for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
>> +            insn->cbs[i][j] = g_array_new(false, false,
>> +                                          sizeof(struct qemu_plugin_dyn_cb));
>> +        }
>> +    }
>> +    return insn;
>> +}
>> +
>> +struct qemu_plugin_tb {
>> +    GPtrArray *insns;
>> +    size_t n;
>> +    uint64_t vaddr;
>> +    uint64_t vaddr2;
>> +    void *haddr1;
>> +    void *haddr2;
>> +    GArray *cbs[PLUGIN_N_CB_SUBTYPES];
>> +};
>> +
>> +/**
>> + * qemu_plugin_tb_insn_get(): get next plugin record for translation.
>> + *
>> + */
>> +static inline
>> +struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb)
>> +{
>> +    struct qemu_plugin_insn *insn;
>> +    int i, j;
>> +
>> +    if (unlikely(tb->n == tb->insns->len)) {
>> +        struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
>> +        g_ptr_array_add(tb->insns, new_insn);
>> +    }
>> +    insn = g_ptr_array_index(tb->insns, tb->n++);
>> +    g_byte_array_set_size(insn->data, 0);
>> +    insn->calls_helpers = false;
>> +    insn->mem_helper = false;
>> +
>> +    for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
>> +        for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
>> +            g_array_set_size(insn->cbs[i][j], 0);
>> +        }
>> +    }
>> +
>> +    return insn;
>> +}
>> +
>> +#ifdef CONFIG_PLUGIN
>> +
>> +void qemu_plugin_vcpu_init_hook(CPUState *cpu);
>> +void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
>> +void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb);
>> +void qemu_plugin_vcpu_idle_cb(CPUState *cpu);
>> +void qemu_plugin_vcpu_resume_cb(CPUState *cpu);
>> +void
>> +qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1,
>> +                         uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
>> +                         uint64_t a6, uint64_t a7, uint64_t a8);
>> +void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret);
>> +
>> +void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, void *haddr,
>> +                             uint32_t meminfo);
>> +
>> +void qemu_plugin_flush_cb(void);
>> +
>> +void qemu_plugin_atexit_cb(void);
>> +
>> +void qemu_plugin_add_dyn_cb_arr(GArray *arr);
>> +
>> +void qemu_plugin_disable_mem_helpers(CPUState *cpu);
>> +
>> +#else /* !CONFIG_PLUGIN */
>> +
>> +static inline void qemu_plugin_vcpu_init_hook(CPUState *cpu)
>> +{ }
>> +
>> +static inline void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
>> +{ }
>> +
>> +static inline void qemu_plugin_tb_trans_cb(CPUState *cpu,
>> +                                           struct qemu_plugin_tb *tb)
>> +{ }
>> +
>> +static inline void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
>> +{ }
>> +
>> +static inline void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
>> +{ }
>> +
>> +static inline void
>> +qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
>> +                         uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6,
>> +                         uint64_t a7, uint64_t a8)
>> +{ }
>> +
>> +static inline
>> +void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
>> +{ }
>> +
>> +static inline void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
>> +                                           void *haddr, uint32_t meminfo)
>> +{ }
>> +
>> +static inline void qemu_plugin_flush_cb(void)
>> +{ }
>> +
>> +static inline void qemu_plugin_atexit_cb(void)
>> +{ }
>> +
>> +static inline
>> +void qemu_plugin_add_dyn_cb_arr(GArray *arr)
>> +{ }
>> +
>> +static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu)
>> +{ }
>> +
>> +#endif /* !CONFIG_PLUGIN */
>> +
>> +#endif /* QEMU_PLUGIN_H */
>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>> index 08481ad304..27b67a4e49 100644
>> --- a/include/qom/cpu.h
>> +++ b/include/qom/cpu.h
>> @@ -29,6 +29,7 @@
>>  #include "qemu/rcu_queue.h"
>>  #include "qemu/queue.h"
>>  #include "qemu/thread.h"
>> +#include "qemu/plugin.h"
>>
>>  typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size,
>>                                       void *opaque);
>> @@ -345,6 +346,7 @@ struct qemu_work_item;
>>   * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
>>   *                        to @trace_dstate).
>>   * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
>> + * @plugin_mask: Plugin event bitmap. Modified only via async work.
>>   * @ignore_memory_transaction_failures: Cached copy of the MachineState
>>   *    flag of the same name: allows the board to suppress calling of the
>>   *    CPU do_transaction_failed hook function.
>> @@ -430,6 +432,10 @@ struct CPUState {
>>      DECLARE_BITMAP(trace_dstate_delayed, CPU_TRACE_DSTATE_MAX_EVENTS);
>>      DECLARE_BITMAP(trace_dstate, CPU_TRACE_DSTATE_MAX_EVENTS);
>>
>> +    DECLARE_BITMAP(plugin_mask, QEMU_PLUGIN_EV_MAX);
>> +
>> +    GArray *plugin_mem_cbs;
>> +
>>      /* TODO Move common fields from CPUArchState here. */
>>      int cpu_index;
>>      int cluster_index;
>> diff --git a/plugins/Makefile.objs b/plugins/Makefile.objs
>> new file mode 100644
>> index 0000000000..58940335bc
>> --- /dev/null
>> +++ b/plugins/Makefile.objs
>> @@ -0,0 +1,6 @@
>> +#
>> +# Plugin Support
>> +#
>> +
>> +obj-y += loader.o
>> +obj-y += core.o
>> diff --git a/plugins/core.c b/plugins/core.c
>> new file mode 100644
>> index 0000000000..8c46373773
>> --- /dev/null
>> +++ b/plugins/core.c
>> @@ -0,0 +1,524 @@
>> +/* plugin.c - QEMU Plugin interface
>> + *
>> + * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
>> + *
>> + * License: GNU GPL, version 2 or later.
>> + *   See the COPYING file in the top-level directory.
>> + */
>> +#include "qemu/osdep.h"
>> +#include "qemu/error-report.h"
>> +#include "qemu/config-file.h"
>> +#include "qapi/error.h"
>> +#include "qemu/option.h"
>> +#include "qemu/rcu_queue.h"
>> +#include "qemu/xxhash.h"
>> +#include "qemu/rcu.h"
>> +#include "qom/cpu.h"
>> +#include "exec/cpu-common.h"
>> +#include <dlfcn.h>
>> +
>> +#include "cpu.h"
>> +#include "exec/exec-all.h"
>> +#include "exec/helper-proto.h"
>> +/* #include "exec/plugin-gen.h" */
>> +#include "sysemu/sysemu.h"
>> +#include "tcg/tcg.h"
>> +#include "tcg/tcg-op.h"
>> +#include "trace/mem-internal.h" /* mem_info macros */
>> +#include "plugin.h"
>> +
>> +struct qemu_plugin_cb {
>> +    struct qemu_plugin_ctx *ctx;
>> +    union qemu_plugin_cb_sig f;
>> +    void *udata;
>> +    QLIST_ENTRY(qemu_plugin_cb) entry;
>> +};
>> +
>> +QLIST_HEAD(qemu_plugin_cb_head, qemu_plugin_cb);
>> +
>> +struct qemu_plugin_state plugin;
>> +
>> +struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id)
>> +{
>> +    struct qemu_plugin_ctx *ctx;
>> +    qemu_plugin_id_t *id_p;
>> +
>> +    id_p = g_hash_table_lookup(plugin.id_ht, &id);
>> +    ctx = container_of(id_p, struct qemu_plugin_ctx, id);
>> +    if (ctx == NULL) {
>> +        error_report("plugin: invalid plugin id %" PRIu64, id);
>> +        abort();
>> +    }
>> +    return ctx;
>> +}
>> +
>> +static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data)
>> +{
>> +    bitmap_copy(cpu->plugin_mask, &data.host_ulong, QEMU_PLUGIN_EV_MAX);
>> +    cpu_tb_jmp_cache_clear(cpu);
>> +}
>> +
>> +static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata)
>> +{
>> +    CPUState *cpu = container_of(k, CPUState, cpu_index);
>> +    run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask);
>> +
>> +    if (cpu->created) {
>> +        async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
>> +    } else {
>> +        plugin_cpu_update__async(cpu, mask);
>> +    }
>> +}
>> +
>> +void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
>> +                                  enum qemu_plugin_event ev)
>> +{
>> +    struct qemu_plugin_cb *cb = ctx->callbacks[ev];
>> +
>> +    if (cb == NULL) {
>> +        return;
>> +    }
>> +    QLIST_REMOVE_RCU(cb, entry);
>> +    g_free(cb);
>> +    ctx->callbacks[ev] = NULL;
>> +    if (QLIST_EMPTY_RCU(&plugin.cb_lists[ev])) {
>> +        clear_bit(ev, plugin.mask);
>> +        g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked, NULL);
>> +    }
>> +}
>> +
>> +static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +
>> +    switch (ev) {
>> +    case QEMU_PLUGIN_EV_VCPU_INIT:
>> +    case QEMU_PLUGIN_EV_VCPU_EXIT:
>> +    case QEMU_PLUGIN_EV_VCPU_IDLE:
>> +    case QEMU_PLUGIN_EV_VCPU_RESUME:
>> +        /* iterate safely; plugins might uninstall themselves at any time */
>> +        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +            qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple;
>> +
>> +            func(cb->ctx->id, cpu->cpu_index);
>> +        }
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
>> +
>> +static void plugin_cb__simple(enum qemu_plugin_event ev)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +
>> +    switch (ev) {
>> +    case QEMU_PLUGIN_EV_FLUSH:
>> +        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +            qemu_plugin_simple_cb_t func = cb->f.simple;
>> +
>> +            func(cb->ctx->id);
>> +        }
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
>> +
>> +static void plugin_cb__udata(enum qemu_plugin_event ev)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +
>> +    switch (ev) {
>> +    case QEMU_PLUGIN_EV_ATEXIT:
>> +        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +            qemu_plugin_udata_cb_t func = cb->f.udata;
>> +
>> +            func(cb->ctx->id, cb->udata);
>> +        }
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
>> +
>> +static void
>> +do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                      void *func, void *udata)
>> +{
>> +    struct qemu_plugin_ctx *ctx;
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    ctx = plugin_id_to_ctx_locked(id);
>> +    /* if the plugin is on its way out, ignore this request */
>> +    if (unlikely(ctx->uninstalling)) {
>> +        goto out_unlock;
>> +    }
>> +    if (func) {
>> +        struct qemu_plugin_cb *cb = ctx->callbacks[ev];
>> +
>> +        if (cb) {
>> +            cb->f.generic = func;
>> +            cb->udata = udata;
>> +        } else {
>> +            cb = g_new(struct qemu_plugin_cb, 1);
>> +            cb->ctx = ctx;
>> +            cb->f.generic = func;
>> +            cb->udata = udata;
>> +            ctx->callbacks[ev] = cb;
>> +            QLIST_INSERT_HEAD_RCU(&plugin.cb_lists[ev], cb, entry);
>> +            if (!test_bit(ev, plugin.mask)) {
>> +                set_bit(ev, plugin.mask);
>> +                g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked,
>> +                                     NULL);
>> +            }
>> +        }
>> +    } else {
>> +        plugin_unregister_cb__locked(ctx, ev);
>> +    }
>> + out_unlock:
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +}
>> +
>> +void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                        void *func)
>> +{
>> +    do_plugin_register_cb(id, ev, func, NULL);
>> +}
>> +
>> +void
>> +plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                         void *func, void *udata)
>> +{
>> +    do_plugin_register_cb(id, ev, func, udata);
>> +}
>> +
>> +void qemu_plugin_vcpu_init_hook(CPUState *cpu)
>> +{
>> +    bool success;
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL);
>> +    success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index,
>> +                                  &cpu->cpu_index);
>> +    g_assert(success);
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +
>> +    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
>> +}
>> +
>> +void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
>> +{
>> +    bool success;
>> +
>> +    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT);
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index);
>> +    g_assert(success);
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +}
>> +
>> +struct plugin_for_each_args {
>> +    struct qemu_plugin_ctx *ctx;
>> +    qemu_plugin_vcpu_simple_cb_t cb;
>> +};
>> +
>> +static void plugin_vcpu_for_each(gpointer k, gpointer v, gpointer udata)
>> +{
>> +    struct plugin_for_each_args *args = udata;
>> +    int cpu_index = *(int *)k;
>> +
>> +    args->cb(args->ctx->id, cpu_index);
>> +}
>> +
>> +void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
>> +                               qemu_plugin_vcpu_simple_cb_t cb)
>> +{
>> +    struct plugin_for_each_args args;
>> +
>> +    if (cb == NULL) {
>> +        return;
>> +    }
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    args.ctx = plugin_id_to_ctx_locked(id);
>> +    args.cb = cb;
>> +    g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args);
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +}
>> +
>> +/* Allocate and return a callback record */
>> +static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
>> +{
>> +    GArray *cbs = *arr;
>> +
>> +    if (!cbs) {
>> +        cbs = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), 1);
>> +        *arr = cbs;
>> +    }
>> +
>> +    g_array_set_size(cbs, cbs->len + 1);
>> +    return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
>> +}
>> +
>> +void plugin_register_inline_op(GArray **arr,
>> +                               enum qemu_plugin_mem_rw rw,
>> +                               enum qemu_plugin_op op, void *ptr,
>> +                               uint64_t imm)
>> +{
>> +    struct qemu_plugin_dyn_cb *dyn_cb;
>> +
>> +    dyn_cb = plugin_get_dyn_cb(arr);
>> +    dyn_cb->userp = ptr;
>> +    dyn_cb->type = PLUGIN_CB_INLINE;
>> +    dyn_cb->rw = rw;
>> +    dyn_cb->inline_insn.op = op;
>> +    dyn_cb->inline_insn.imm = imm;
>> +}
>> +
>> +static inline uint32_t cb_to_tcg_flags(enum qemu_plugin_cb_flags flags)
>> +{
>> +    uint32_t ret;
>> +
>> +    switch (flags) {
>> +    case QEMU_PLUGIN_CB_RW_REGS:
>> +        ret = 0;
>> +    case QEMU_PLUGIN_CB_R_REGS:
>> +        ret = TCG_CALL_NO_WG;
>> +        break;
>> +    case QEMU_PLUGIN_CB_NO_REGS:
>> +    default:
>> +        ret = TCG_CALL_NO_RWG;
>> +    }
>> +    return ret;
>> +}
>> +
>> +inline void
>> +plugin_register_dyn_cb__udata(GArray **arr,
>> +                              qemu_plugin_vcpu_udata_cb_t cb,
>> +                              enum qemu_plugin_cb_flags flags, void *udata)
>> +{
>> +    struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
>> +
>> +    dyn_cb->userp = udata;
>> +    dyn_cb->tcg_flags = cb_to_tcg_flags(flags);
>> +    dyn_cb->f.vcpu_udata = cb;
>> +    dyn_cb->type = PLUGIN_CB_REGULAR;
>> +}
>> +
>> +void plugin_register_vcpu_mem_cb(GArray **arr,
>> +                                 void *cb,
>> +                                 enum qemu_plugin_cb_flags flags,
>> +                                 enum qemu_plugin_mem_rw rw,
>> +                                 void *udata, bool haddr)
>> +{
>> +    struct qemu_plugin_dyn_cb *dyn_cb;
>> +
>> +    dyn_cb = plugin_get_dyn_cb(arr);
>> +    dyn_cb->userp = udata;
>> +    dyn_cb->tcg_flags = cb_to_tcg_flags(flags);
>> +    dyn_cb->type = PLUGIN_CB_REGULAR;
>> +    dyn_cb->rw = rw;
>> +    dyn_cb->mem.haddr = haddr;
>> +    dyn_cb->f.generic = cb;
>> +}
>> +
>> +void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS;
>> +
>> +    /* no plugin_mask check here; caller should have checked */
>> +
>> +    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +        qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans;
>> +
>> +        func(cb->ctx->id, cpu->cpu_index, tb);
>> +    }
>> +}
>> +
>> +void
>> +qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
>> +                         uint64_t a3, uint64_t a4, uint64_t a5,
>> +                         uint64_t a6, uint64_t a7, uint64_t a8)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL;
>> +
>> +    if (!test_bit(ev, cpu->plugin_mask)) {
>> +        return;
>> +    }
>> +
>> +    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +        qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall;
>> +
>> +        func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8);
>> +    }
>> +}
>> +
>> +void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
>> +{
>> +    struct qemu_plugin_cb *cb, *next;
>> +    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET;
>> +
>> +    if (!test_bit(ev, cpu->plugin_mask)) {
>> +        return;
>> +    }
>> +
>> +    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>> +        qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret;
>> +
>> +        func(cb->ctx->id, cpu->cpu_index, num, ret);
>> +    }
>> +}
>> +
>> +void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
>> +{
>> +    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
>> +}
>> +
>> +void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
>> +{
>> +    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
>> +}
>> +
>> +void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
>> +                                       qemu_plugin_vcpu_simple_cb_t cb)
>> +{
>> +    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_IDLE, cb);
>> +}
>> +
>> +void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
>> +                                         qemu_plugin_vcpu_simple_cb_t cb)
>> +{
>> +    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb);
>> +}
>> +
>> +void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
>> +                                   qemu_plugin_simple_cb_t cb)
>> +{
>> +    plugin_register_cb(id, QEMU_PLUGIN_EV_FLUSH, cb);
>> +}
>> +
>> +static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp)
>> +{
>> +    g_array_free((GArray *) p, true);
>> +    return true;
>> +}
>> +
>> +void qemu_plugin_flush_cb(void)
>> +{
>> +    qht_iter_remove(&plugin.dyn_cb_arr_ht, free_dyn_cb_arr, NULL);
>> +    qht_reset(&plugin.dyn_cb_arr_ht);
>> +
>> +    plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
>> +}
>> +
>> +void exec_inline_op(struct qemu_plugin_dyn_cb *cb)
>> +{
>> +    uint64_t *val = cb->userp;
>> +
>> +    switch (cb->inline_insn.op) {
>> +    case QEMU_PLUGIN_INLINE_ADD_U64:
>> +        *val += cb->inline_insn.imm;
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +}
>> +
>> +void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, void *haddr,
>> +                             uint32_t info)
>> +{
>> +    GArray *arr = cpu->plugin_mem_cbs;
>> +    size_t i;
>> +
>> +    if (arr == NULL) {
>> +        return;
>> +    }
>> +    for (i = 0; i < arr->len; i++) {
>> +        struct qemu_plugin_dyn_cb *cb =
>> +            &g_array_index(arr, struct qemu_plugin_dyn_cb, i);
>> +        int w = !!(info & TRACE_MEM_ST) + 1;
>> +
>> +        if (!(w & cb->rw)) {
>> +                break;
>> +        }
>> +        switch (cb->type) {
>> +        case PLUGIN_CB_REGULAR:
>> +
>> +            if (cb->mem.haddr) {
>> +                cb->f.vcpu_mem_haddr(cpu->cpu_index, info, vaddr, haddr,
>> +                                     cb->userp);
>> +            } else {
>> +                cb->f.vcpu_mem(cpu->cpu_index, info, vaddr, cb->userp);
>> +            }
>> +            break;
>> +        case PLUGIN_CB_INLINE:
>> +            exec_inline_op(cb);
>> +            break;
>> +        default:
>> +            g_assert_not_reached();
>> +        }
>> +    }
>> +}
>> +
>> +void qemu_plugin_atexit_cb(void)
>> +{
>> +    plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT);
>> +}
>> +
>> +void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
>> +                                    qemu_plugin_udata_cb_t cb,
>> +                                    void *udata)
>> +{
>> +    plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata);
>> +}
>> +
>> +uint64_t qemu_plugin_ram_addr_from_host(void *haddr)
>> +{
>> +#ifdef CONFIG_SOFTMMU
>> +    ram_addr_t ram_addr;
>> +
>> +    g_assert(haddr);
>> +    ram_addr = qemu_ram_addr_from_host(haddr);
>> +    if (ram_addr == RAM_ADDR_INVALID) {
>> +        error_report("Bad ram pointer %p", haddr);
>> +        abort();
>> +    }
>> +    return ram_addr;
>> +#else
>> +    return 0;
>> +#endif
>> +}
>> +
>> +/*
>> + * Call this function after longjmp'ing to the main loop. It's possible that the
>> + * last instruction of a TB might have used helpers, and therefore the
>> + * "disable" instruction will never execute because it ended up as dead code.
>> + */
>> +void qemu_plugin_disable_mem_helpers(CPUState *cpu)
>> +{
>> +    cpu->plugin_mem_cbs = NULL;
>> +}
>> +
>> +static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
>> +{
>> +    return ap == bp;
>> +}
>> +
>> +static void __attribute__((__constructor__)) plugin_init(void)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < QEMU_PLUGIN_EV_MAX; i++) {
>> +        QLIST_INIT(&plugin.cb_lists[i]);
>> +    }
>> +    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);
>> +    QTAILQ_INIT(&plugin.ctxs);
>> +    qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16,
>> +             QHT_MODE_AUTO_RESIZE);
>> +    atexit(qemu_plugin_atexit_cb);
>> +}
>> diff --git a/plugins/loader.c b/plugins/loader.c
>> new file mode 100644
>> index 0000000000..bdd875868f
>> --- /dev/null
>> +++ b/plugins/loader.c
>> @@ -0,0 +1,353 @@
>> +#include "qemu/osdep.h"
>> +#include "qemu/error-report.h"
>> +#include "qemu/config-file.h"
>> +#include "qapi/error.h"
>> +#include "qemu/option.h"
>> +#include "qemu/rcu_queue.h"
>> +#include "qemu/qht.h"
>> +#include "qemu/bitmap.h"
>> +#include "qemu/xxhash.h"
>> +#include "qemu/plugin.h"
>> +#include "qom/cpu.h"
>> +#include <dlfcn.h>
>> +#include "cpu.h"
>> +#include "exec/exec-all.h"
>> +
>> +#include "plugin.h"
>> +
>> +/*
>> + * For convenience we use a bitmap for plugin.mask, but really all we need is a
>> + * u32, which is what we store in TranslationBlock.
>> + */
>> +QEMU_BUILD_BUG_ON(QEMU_PLUGIN_EV_MAX > 32);
>> +
>> +struct qemu_plugin_desc {
>> +    char *path;
>> +    char **argv;
>> +    QTAILQ_ENTRY(qemu_plugin_desc) entry;
>> +    int argc;
>> +};
>> +
>> +struct qemu_plugin_parse_arg {
>> +    QemuPluginList *head;
>> +    struct qemu_plugin_desc *curr;
>> +};
>> +
>> +QemuOptsList qemu_plugin_opts = {
>> +    .name = "plugin",
>> +    .implied_opt_name = "file",
>> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head),
>> +    .desc = {
>> +        /* do our own parsing to support multiple plugins */
>> +        { /* end of list */ }
>> +    },
>> +};
>> +
>> +typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, int, char **);
>> +
>> +extern struct qemu_plugin_state plugin;
>> +
>> +void qemu_plugin_add_dyn_cb_arr(GArray *arr)
>> +{
>> +    uint32_t hash = qemu_xxhash2((uint64_t)(uintptr_t)arr);
>> +    bool inserted;
>> +
>> +    inserted = qht_insert(&plugin.dyn_cb_arr_ht, arr, hash, NULL);
>> +    g_assert(inserted);
>> +}
>> +
>> +static struct qemu_plugin_desc *plugin_find_desc(QemuPluginList *head,
>> +                                                 const char *path)
>> +{
>> +    struct qemu_plugin_desc *desc;
>> +
>> +    QTAILQ_FOREACH(desc, head, entry) {
>> +        if (strcmp(desc->path, path) == 0) {
>> +            return desc;
>> +        }
>> +    }
>> +    return NULL;
>> +}
>> +
>> +static int plugin_add(void *opaque, const char *name, const char *value,
>> +                      Error **errp)
>> +{
>> +    struct qemu_plugin_parse_arg *arg = opaque;
>> +    struct qemu_plugin_desc *p;
>> +
>> +    if (strcmp(name, "file") == 0) {
>> +        if (strcmp(value, "") == 0) {
>> +            error_setg(errp, "requires a non-empty argument");
>> +            return 1;
>> +        }
>> +        p = plugin_find_desc(arg->head, value);
>> +        if (p == NULL) {
>> +            p = g_new0(struct qemu_plugin_desc, 1);
>> +            p->path = g_strdup(value);
>> +            QTAILQ_INSERT_TAIL(arg->head, p, entry);
>> +        }
>> +        arg->curr = p;
>> +    } else if (strcmp(name, "arg") == 0) {
>> +        if (arg->curr == NULL) {
>> +            error_setg(errp, "missing earlier '-plugin file=' option");
>> +            return 1;
>> +        }
>> +        p = arg->curr;
>> +        p->argc++;
>> +        p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *));
>> +        p->argv[p->argc - 1] = g_strdup(value);
>> +    } else {
>> +        error_setg(errp, "-plugin: unexpected parameter '%s'; ignored", name);
>> +    }
>> +    return 0;
>> +}
>> +
>> +void qemu_plugin_opt_parse(const char *optarg, QemuPluginList *head)
>> +{
>> +    struct qemu_plugin_parse_arg arg;
>> +    QemuOpts *opts;
>> +
>> +    opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optarg, true);
>> +    if (opts == NULL) {
>> +        exit(1);
>> +    }
>> +    arg.head = head;
>> +    arg.curr = NULL;
>> +    qemu_opt_foreach(opts, plugin_add, &arg, &error_fatal);
>> +    qemu_opts_del(opts);
>> +}
>> +
>> +/*
>> + * From: https://en.wikipedia.org/wiki/Xorshift
>> + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
>> + * guaranteed to be >= INT_MAX).
>> + */
>> +static uint64_t xorshift64star(uint64_t x)
>> +{
>> +    x ^= x >> 12; /* a */
>> +    x ^= x << 25; /* b */
>> +    x ^= x >> 27; /* c */
>> +    return x * UINT64_C(2685821657736338717);
>> +}
>> +
>> +static int plugin_load(struct qemu_plugin_desc *desc)
>> +{
>> +    qemu_plugin_install_func_t install;
>> +    struct qemu_plugin_ctx *ctx;
>> +    char *err;
>> +    int rc;
>> +
>> +    ctx = qemu_memalign(qemu_dcache_linesize, sizeof(*ctx));
>> +    memset(ctx, 0, sizeof(*ctx));
>> +    ctx->desc = desc;
>> +
>> +    ctx->handle = dlopen(desc->path, RTLD_NOW);
>> +    if (ctx->handle == NULL) {
>> +        error_report("%s: %s", __func__, dlerror());
>> +        goto err_dlopen;
>> +    }
>> +
>> +    /* clear any previous dlerror, call dlsym, then check dlerror */
>> +    dlerror();
>> +    install = dlsym(ctx->handle, "qemu_plugin_install");
>> +    err = dlerror();
>> +    if (err) {
>> +        error_report("%s: %s", __func__, err);
>> +        goto err_symbol;
>> +    }
>> +    /* symbol was found; it could be NULL though */
>> +    if (install == NULL) {
>> +        error_report("%s: %s: qemu_plugin_install is NULL",
>> +                     __func__, desc->path);
>> +        goto err_symbol;
>> +    }
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +
>> +    /* find an unused random id with &ctx as the seed */
>> +    ctx->id = (uint64_t)(uintptr_t)ctx;
>> +    for (;;) {
>> +        void *existing;
>> +
>> +        ctx->id = xorshift64star(ctx->id);
>> +        existing = g_hash_table_lookup(plugin.id_ht, &ctx->id);
>> +        if (likely(existing == NULL)) {
>> +            bool success;
>> +
>> +            success = g_hash_table_insert(plugin.id_ht, &ctx->id, &ctx->id);
>> +            g_assert(success);
>> +            break;
>> +        }
>> +    }
>> +    QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
>> +    ctx->installing = true;
>> +    rc = install(ctx->id, desc->argc, desc->argv);
>> +    ctx->installing = false;
>> +    if (rc) {
>> +        error_report("%s: qemu_plugin_install returned error code %d",
>> +                     __func__, rc);
>> +        /*
>> +         * we cannot rely on the plugin doing its own cleanup, so
>> +         * call a full uninstall if the plugin did not yet call it.
>> +         */
>> +        /* if (!ctx->uninstalling) { */
>> +        /*     qemu_plugin_uninstall(ctx->id, NULL); */
>> +        /* } */
>> +    }
>> +
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +    return rc;
>> +
>> + err_symbol:
>> +    if (dlclose(ctx->handle)) {
>> +        warn_report("%s: %s", __func__, dlerror());
>> +    }
>> + err_dlopen:
>> +    qemu_vfree(ctx);
>> +    return 1;
>> +}
>> +
>> +/* call after having removed @desc from the list */
>> +static void plugin_desc_free(struct qemu_plugin_desc *desc)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < desc->argc; i++) {
>> +        g_free(desc->argv[i]);
>> +    }
>> +    g_free(desc->argv);
>> +    g_free(desc->path);
>> +    g_free(desc);
>> +}
>> +
>> +/**
>> + * qemu_plugin_load_list - load a list of plugins
>> + * @head: head of the list of descriptors of the plugins to be loaded
>> + *
>> + * Returns 0 if all plugins in the list are installed, !0 otherwise.
>> + *
>> + * Note: the descriptor of each successfully installed plugin is removed
>> + * from the list given by @head.
>> + */
>> +int qemu_plugin_load_list(QemuPluginList *head)
>> +{
>> +    struct qemu_plugin_desc *desc, *next;
>> +
>> +    QTAILQ_FOREACH_SAFE(desc, head, entry, next) {
>> +        int err;
>> +
>> +        err = plugin_load(desc);
>> +        if (err) {
>> +            return err;
>> +        }
>> +        QTAILQ_REMOVE(head, desc, entry);
>> +    }
>> +    return 0;
>> +}
>> +
>> +struct qemu_plugin_reset_data {
>> +    struct qemu_plugin_ctx *ctx;
>> +    qemu_plugin_simple_cb_t cb;
>> +    bool reset;
>> +};
>> +
>> +static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data)
>> +{
>> +    struct qemu_plugin_ctx *ctx = data->ctx;
>> +    enum qemu_plugin_event ev;
>> +    bool success;
>> +
>> +    /*
>> +     * After updating the subscription lists there is no need to wait for an RCU
>> +     * grace period to elapse, because right now we either are in a "safe async"
>> +     * work environment (i.e. all vCPUs are asleep), or no vCPUs have yet been
>> +     * created.
>> +     */
>> +    for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
>> +        plugin_unregister_cb__locked(ctx, ev);
>> +    }
>> +
>> +    if (data->reset) {
>> +        g_assert(ctx->resetting);
>> +        if (data->cb) {
>> +            data->cb(ctx->id);
>> +        }
>> +        ctx->resetting = false;
>> +        g_free(data);
>> +        return;
>> +    }
>> +
>> +    g_assert(ctx->uninstalling);
>> +    /* we cannot dlclose if we are going to return to plugin code */
>> +    if (ctx->installing) {
>> +        error_report("Calling qemu_plugin_uninstall from the install function "
>> +                     "is a bug. Instead, return !0 from the install function.");
>> +        abort();
>> +    }
>> +
>> +    success = g_hash_table_remove(plugin.id_ht, &ctx->id);
>> +    g_assert(success);
>> +    QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
>> +    if (data->cb) {
>> +        data->cb(ctx->id);
>> +    }
>> +    if (dlclose(ctx->handle)) {
>> +        warn_report("%s: %s", __func__, dlerror());
>> +    }
>> +    plugin_desc_free(ctx->desc);
>> +    qemu_vfree(ctx);
>> +    g_free(data);
>> +}
>> +
>> +static void plugin_reset_destroy(struct qemu_plugin_reset_data *data)
>> +{
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    plugin_reset_destroy__locked(data);
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +}
>> +
>> +static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg)
>> +{
>> +    struct qemu_plugin_reset_data *data = arg.host_ptr;
>> +
>> +    g_assert(cpu_in_exclusive_work_context(cpu));
>> +    tb_flush(cpu);
>> +    plugin_reset_destroy(data);
>> +}
>> +
>> +void plugin_reset_uninstall(qemu_plugin_id_t id,
>> +                            qemu_plugin_simple_cb_t cb,
>> +                            bool reset)
>> +{
>> +    struct qemu_plugin_reset_data *data;
>> +    struct qemu_plugin_ctx *ctx;
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    ctx = plugin_id_to_ctx_locked(id);
>> +    if (ctx->uninstalling || (reset && ctx->resetting)) {
>> +        qemu_rec_mutex_unlock(&plugin.lock);
>> +        return;
>> +    }
>> +    ctx->resetting = reset;
>> +    ctx->uninstalling = !reset;
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +
>> +    data = g_new(struct qemu_plugin_reset_data, 1);
>> +    data->ctx = ctx;
>> +    data->cb = cb;
>> +    data->reset = reset;
>> +    /*
>> +     * Only flush the code cache if the vCPUs have been created. If so,
>> +     * current_cpu must be non-NULL.
>> +     */
>> +    if (current_cpu) {
>> +        async_safe_run_on_cpu(current_cpu, plugin_flush_destroy,
>> +                              RUN_ON_CPU_HOST_PTR(data));
>> +    } else {
>> +        /*
>> +         * If current_cpu isn't set, then we don't have yet any vCPU threads
>> +         * and we therefore can remove the callbacks synchronously.
>> +         */
>> +        plugin_reset_destroy(data);
>> +    }
>> +}
>> diff --git a/plugins/plugin.h b/plugins/plugin.h
>> new file mode 100644
>> index 0000000000..e89f9ef0ea
>> --- /dev/null
>> +++ b/plugins/plugin.h
>> @@ -0,0 +1,95 @@
>> +/*
>> + * Plugin Shared Internal Functions
>> + *
>> + * Copyright (C) 2019, Linaro
>> + *
>> + * License: GNU GPL, version 2 or later.
>> + *   See the COPYING file in the top-level directory.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef _PLUGIN_INTERNAL_H_
>> +#define _PLUGIN_INTERNAL_H_
>> +
>> +/* global state */
>> +struct qemu_plugin_state {
>> +    QTAILQ_HEAD(, qemu_plugin_ctx) ctxs;
>> +    QLIST_HEAD(, qemu_plugin_cb) cb_lists[QEMU_PLUGIN_EV_MAX];
>> +    /*
>> +     * Use the HT as a hash map by inserting k == v, which saves memory as
>> +     * documented by GLib. The parent struct is obtained with container_of().
>> +     */
>> +    GHashTable *id_ht;
>> +    /*
>> +     * Use the HT as a hash map. Note that we could use a list here,
>> +     * but with the HT we avoid adding a field to CPUState.
>> +     */
>> +    GHashTable *cpu_ht;
>> +    DECLARE_BITMAP(mask, QEMU_PLUGIN_EV_MAX);
>> +    /*
>> +     * @lock protects the struct as well as ctx->uninstalling.
>> +     * The lock must be acquired by all API ops.
>> +     * The lock is recursive, which greatly simplifies things, e.g.
>> +     * callback registration from qemu_plugin_vcpu_for_each().
>> +     */
>> +    QemuRecMutex lock;
>> +    /*
>> +     * HT of callbacks invoked from helpers. All entries are freed when
>> +     * the code cache is flushed.
>> +     */
>> +    struct qht dyn_cb_arr_ht;
>> +};
>> +
>> +
>> +struct qemu_plugin_ctx {
>> +    void *handle; /* dlopen */
>> +    qemu_plugin_id_t id;
>> +    struct qemu_plugin_cb *callbacks[QEMU_PLUGIN_EV_MAX];
>> +    QTAILQ_ENTRY(qemu_plugin_ctx) entry;
>> +    /*
>> +     * keep a reference to @desc until uninstall, so that plugins do not have
>> +     * to strdup plugin args.
>> +     */
>> +    struct qemu_plugin_desc *desc;
>> +    bool installing;
>> +    bool uninstalling;
>> +    bool resetting;
>> +};
>> +
>> +struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id);
>> +
>> +void plugin_register_inline_op(GArray **arr,
>> +                               enum qemu_plugin_mem_rw rw,
>> +                               enum qemu_plugin_op op, void *ptr,
>> +                               uint64_t imm);
>> +
>> +void plugin_reset_uninstall(qemu_plugin_id_t id,
>> +                            qemu_plugin_simple_cb_t cb,
>> +                            bool reset);
>> +
>> +void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                        void *func);
>> +
>> +void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
>> +                                  enum qemu_plugin_event ev);
>> +
>> +void
>> +plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                         void *func, void *udata);
>> +
>> +void
>> +plugin_register_dyn_cb__udata(GArray **arr,
>> +                              qemu_plugin_vcpu_udata_cb_t cb,
>> +                              enum qemu_plugin_cb_flags flags, void *udata);
>> +
>> +
>> +void plugin_register_vcpu_mem_cb(GArray **arr,
>> +                                 void *cb,
>> +                                 enum qemu_plugin_cb_flags flags,
>> +                                 enum qemu_plugin_mem_rw rw,
>> +                                 void *udata, bool haddr);
>> +
>> +void exec_inline_op(struct qemu_plugin_dyn_cb *cb);
>> +
>> +#endif /* _PLUGIN_INTERNAL_H_ */
>> --
>> 2.20.1
>>
>>


--
Alex Bennée


  parent reply	other threads:[~2019-06-19 11:49 UTC|newest]

Thread overview: 121+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
2019-06-17  2:12   ` Richard Henderson
2019-06-17  8:22     ` Alex Bennée
2019-06-17 23:29       ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/ Alex Bennée
2019-06-17  2:13   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context() Alex Bennée
2019-06-17  2:15   ` Richard Henderson
2019-06-20  9:50     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 04/50] translate-all: use cpu_in_exclusive_work_context() in tb_flush Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document Alex Bennée
2019-06-19  3:34   ` Pranith Kumar
2019-06-20 13:38     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 06/50] configure: add --enable-plugins (MOVE TO END) Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API Alex Bennée
2019-06-19  3:34   ` Pranith Kumar
2019-06-19 11:32     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 08/50] plugin: add core code Alex Bennée
     [not found]   ` <CADYwmhGiU_1GrBrR_tzBx+Lw+Hs3=Hi3AoPxRwkEj2pv9awqUg@mail.gmail.com>
2019-06-19 11:46     ` Alex Bennée [this message]
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 09/50] plugin: add implementation of the api Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 10/50] queue: add QTAILQ_REMOVE_SEVERAL Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 11/50] cputlb: document get_page_addr_code Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 12/50] cputlb: introduce get_page_addr_code_hostp Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 13/50] plugin-gen: add module for TCG-related code Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr Alex Bennée
2019-06-17 20:19   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp Alex Bennée
2019-06-17 20:43   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER Alex Bennée
2019-06-17 20:43   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers Alex Bennée
2019-06-17 20:47   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS Alex Bennée
2019-06-17 20:47   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
2019-06-17 20:51   ` Richard Henderson
2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
2019-06-28 17:11     ` Alex Bennée
2019-06-28 17:58       ` Aaron Lindsay OS via Qemu-devel
2019-06-28 20:52         ` Alex Bennée
2019-07-01 14:40           ` Aaron Lindsay OS via Qemu-devel
2019-07-01 15:00             ` Alex Bennée
2019-07-02 14:07               ` Aaron Lindsay OS via Qemu-devel
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush Alex Bennée
2019-06-17 20:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit Alex Bennée
2019-06-17 20:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
2019-06-17 20:56   ` Richard Henderson
2019-06-19  3:35   ` Pranith Kumar
2019-07-01 14:20     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events Alex Bennée
2019-06-17 21:00   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append Alex Bennée
2019-06-17 21:03   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q} Alex Bennée
2019-06-17 21:06   ` Richard Henderson
2019-07-30 12:41     ` Alex Bennée
2019-07-30 13:23       ` Richard Henderson
2019-07-30 14:08         ` Alex Bennée
2019-07-30 17:04         ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append Alex Bennée
2019-06-17 22:28   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
2019-06-17 22:30   ` Richard Henderson
2019-06-19  9:39   ` David Gibson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 28/50] target/sh4: " Alex Bennée
2019-06-17 22:33   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 29/50] target/i386: " Alex Bennée
2019-06-17 22:33   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 30/50] target/hppa: " Alex Bennée
2019-06-17 22:34   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 31/50] target/m68k: " Alex Bennée
2019-06-17 22:35   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 32/50] target/alpha: " Alex Bennée
2019-06-17 22:35   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 33/50] target/riscv: " Alex Bennée
2019-06-14 17:11   ` [Qemu-riscv] " Alex Bennée
2019-06-17 22:38   ` [Qemu-devel] " Richard Henderson
2019-06-17 22:38     ` [Qemu-riscv] " Richard Henderson
2019-06-19 10:49     ` Palmer Dabbelt
2019-06-19 10:49       ` [Qemu-riscv] " Palmer Dabbelt
2019-09-27 21:47       ` Alistair Francis
2019-09-27 21:47         ` Alistair Francis
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 34/50] target/sparc: " Alex Bennée
2019-06-17 22:39   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 35/50] target/xtensa: " Alex Bennée
2019-06-17 22:41   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 36/50] target/openrisc: " Alex Bennée
2019-06-17 22:41   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins Alex Bennée
2019-06-17 22:44   ` Richard Henderson
2019-06-20 16:51     ` Alex Bennée
2019-07-01 16:01     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 38/50] plugin: add API symbols to qemu-plugins.symbols Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option Alex Bennée
2019-06-17 22:53   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 40/50] linux-user: " Alex Bennée
2019-06-17 22:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 41/50] tests/plugin: add sample plugins Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 42/50] tests/tcg: enable plugin testing Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 43/50] tests/plugin: add a hotblocks plugin Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper Alex Bennée
2019-06-17 23:09   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 45/50] tests/plugin: add instruction execution breakdown Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 46/50] tests/plugin: add hotpages plugin to breakdown memory access patterns Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub Alex Bennée
2019-06-17 23:22   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 48/50] include/exec: wrap cpu_ldst.h in CONFIG_TCG Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo Alex Bennée
2019-06-17 23:23   ` Richard Henderson
2019-06-14 17:12 ` [Qemu-devel] [PATCH v3 50/50] TODO: API changes to make? Alex Bennée
2019-06-14 17:41 ` [Qemu-devel] [PATCH v3 00/50] tcg plugin support Aleksandar Markovic
2019-06-14 18:39   ` Alex Bennée
2019-06-14 19:47 ` no-reply
2019-06-14 19:48 ` no-reply
2019-06-20 13:53 ` Pranith Kumar
2019-06-21  8:21   ` Alex Bennée
2019-06-21 17:36     ` Pranith Kumar
2019-07-01 16:51       ` Alex Bennée

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=871rzpajbl.fsf@zen.linaroharston \
    --to=alex.bennee@linaro.org \
    --cc=bobby.prani@gmail.com \
    --cc=cota@braap.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.