diff mbox

[RFC,v3,09/13] cpus.c: introduce simple callback support

Message ID 1436516626-8322-10-git-send-email-a.rigo@virtualopensystems.com
State New
Headers show

Commit Message

Alvise Rigo July 10, 2015, 8:23 a.m. UTC
In order to perfom "lazy" TLB invalidation requests, introduce a
queue of callbacks at every vCPU disposal that will be fired just
before entering the next TB.

Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
---
 cpus.c            | 34 ++++++++++++++++++++++++++++++++++
 exec.c            |  1 +
 include/qom/cpu.h | 20 ++++++++++++++++++++
 3 files changed, 55 insertions(+)

Comments

Paolo Bonzini July 10, 2015, 9:36 a.m. UTC | #1
On 10/07/2015 10:23, Alvise Rigo wrote:
> In order to perfom "lazy" TLB invalidation requests, introduce a
> queue of callbacks at every vCPU disposal that will be fired just
> before entering the next TB.
> 
> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>

Why is async_run_on_cpu not enough?

Paolo

> ---
>  cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>  exec.c            |  1 +
>  include/qom/cpu.h | 20 ++++++++++++++++++++
>  3 files changed, 55 insertions(+)
> 
> diff --git a/cpus.c b/cpus.c
> index f4d938e..b9f0329 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>          cpu->icount_extra = count;
>      }
>      qemu_mutex_unlock_iothread();
> +    cpu_exit_callbacks_call_all(cpu);
>      ret = cpu_exec(env);
>      cpu->tcg_executing = 0;
>  
> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>      cpu->exit_request = 0;
>  }
>  
> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
> +                           void *opaque)
> +{
> +    CPUExitCB *cb;
> +
> +    cb = g_malloc(sizeof(*cb));
> +    cb->callback = callback;
> +    cb->opaque = opaque;
> +
> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
> +}
> +
> +void cpu_exit_callbacks_call_all(CPUState *cpu)
> +{
> +    CPUExitCB *cb, *next;
> +
> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
> +        return;
> +    }
> +
> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
> +        cb->callback(cpu, cb->opaque);
> +
> +        /* one-shot callbacks, remove it after using it */
> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
> +        g_free(cb);
> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
> +    }
> +}
> +
>  void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
>  {
>      /* XXX: implement xxx_cpu_list for targets that still miss it */
> diff --git a/exec.c b/exec.c
> index 51958ed..322f2c6 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>      cpu->numa_node = 0;
>      QTAILQ_INIT(&cpu->breakpoints);
>      QTAILQ_INIT(&cpu->watchpoints);
> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>  #ifndef CONFIG_USER_ONLY
>      cpu->as = &address_space_memory;
>      cpu->thread_id = qemu_get_thread_id();
> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> index 8d121b3..0ec020b 100644
> --- a/include/qom/cpu.h
> +++ b/include/qom/cpu.h
> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>      QTAILQ_ENTRY(CPUWatchpoint) entry;
>  } CPUWatchpoint;
>  
> +/* vCPU exit callbacks */
> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
> +struct CPUExitCBs {
> +    QemuMutex mutex;
> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
> +};
> +
> +typedef struct CPUExitCB {
> +    CPUExitCallback callback;
> +    void *opaque;
> +
> +    QTAILQ_ENTRY(CPUExitCB) entry;
> +} CPUExitCB;
> +
> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
> +                           void *opaque);
> +void cpu_exit_callbacks_call_all(CPUState *cpu);
> +
>  /* Rendezvous support */
>  #define TCG_RDV_POLLING_PERIOD 10
>  typedef struct CpuExitRendezvous {
> @@ -305,6 +323,8 @@ struct CPUState {
>  
>      void *opaque;
>  
> +    /* One-shot callbacks for stopping requests. */
> +    struct CPUExitCBs exit_cbs;
>      volatile int pending_rdv;
>  
>      /* In order to avoid passing too many arguments to the MMIO helpers,
>
Alvise Rigo July 10, 2015, 9:47 a.m. UTC | #2
I tried to use it, but it would then create a deadlock at a very early
stage of the stress test.
The problem is likely related to the fact that flush_queued_work
happens with the global mutex locked.

As Frederick suggested, we can use the newly introduced
flush_queued_safe_work for this.

Regards,
alvise

On Fri, Jul 10, 2015 at 11:36 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>
>
> On 10/07/2015 10:23, Alvise Rigo wrote:
>> In order to perfom "lazy" TLB invalidation requests, introduce a
>> queue of callbacks at every vCPU disposal that will be fired just
>> before entering the next TB.
>>
>> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
>> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
>> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
>
> Why is async_run_on_cpu not enough?
>
> Paolo
>
>> ---
>>  cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>>  exec.c            |  1 +
>>  include/qom/cpu.h | 20 ++++++++++++++++++++
>>  3 files changed, 55 insertions(+)
>>
>> diff --git a/cpus.c b/cpus.c
>> index f4d938e..b9f0329 100644
>> --- a/cpus.c
>> +++ b/cpus.c
>> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>>          cpu->icount_extra = count;
>>      }
>>      qemu_mutex_unlock_iothread();
>> +    cpu_exit_callbacks_call_all(cpu);
>>      ret = cpu_exec(env);
>>      cpu->tcg_executing = 0;
>>
>> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>>      cpu->exit_request = 0;
>>  }
>>
>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>> +                           void *opaque)
>> +{
>> +    CPUExitCB *cb;
>> +
>> +    cb = g_malloc(sizeof(*cb));
>> +    cb->callback = callback;
>> +    cb->opaque = opaque;
>> +
>> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
>> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
>> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>> +}
>> +
>> +void cpu_exit_callbacks_call_all(CPUState *cpu)
>> +{
>> +    CPUExitCB *cb, *next;
>> +
>> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
>> +        return;
>> +    }
>> +
>> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
>> +        cb->callback(cpu, cb->opaque);
>> +
>> +        /* one-shot callbacks, remove it after using it */
>> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
>> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
>> +        g_free(cb);
>> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>> +    }
>> +}
>> +
>>  void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
>>  {
>>      /* XXX: implement xxx_cpu_list for targets that still miss it */
>> diff --git a/exec.c b/exec.c
>> index 51958ed..322f2c6 100644
>> --- a/exec.c
>> +++ b/exec.c
>> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>>      cpu->numa_node = 0;
>>      QTAILQ_INIT(&cpu->breakpoints);
>>      QTAILQ_INIT(&cpu->watchpoints);
>> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>>  #ifndef CONFIG_USER_ONLY
>>      cpu->as = &address_space_memory;
>>      cpu->thread_id = qemu_get_thread_id();
>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>> index 8d121b3..0ec020b 100644
>> --- a/include/qom/cpu.h
>> +++ b/include/qom/cpu.h
>> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>>      QTAILQ_ENTRY(CPUWatchpoint) entry;
>>  } CPUWatchpoint;
>>
>> +/* vCPU exit callbacks */
>> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
>> +struct CPUExitCBs {
>> +    QemuMutex mutex;
>> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
>> +};
>> +
>> +typedef struct CPUExitCB {
>> +    CPUExitCallback callback;
>> +    void *opaque;
>> +
>> +    QTAILQ_ENTRY(CPUExitCB) entry;
>> +} CPUExitCB;
>> +
>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>> +                           void *opaque);
>> +void cpu_exit_callbacks_call_all(CPUState *cpu);
>> +
>>  /* Rendezvous support */
>>  #define TCG_RDV_POLLING_PERIOD 10
>>  typedef struct CpuExitRendezvous {
>> @@ -305,6 +323,8 @@ struct CPUState {
>>
>>      void *opaque;
>>
>> +    /* One-shot callbacks for stopping requests. */
>> +    struct CPUExitCBs exit_cbs;
>>      volatile int pending_rdv;
>>
>>      /* In order to avoid passing too many arguments to the MMIO helpers,
>>
fred.konrad@greensocs.com July 10, 2015, 9:53 a.m. UTC | #3
On 10/07/2015 11:47, alvise rigo wrote:
> I tried to use it, but it would then create a deadlock at a very early
> stage of the stress test.
> The problem is likely related to the fact that flush_queued_work
> happens with the global mutex locked.
>
> As Frederick suggested, we can use the newly introduced
> flush_queued_safe_work for this.
>
> Regards,
> alvise

It depends on the purpose.

async safe work will requires all VCPU to be exited (eg: like your 
rendez-vous).
async work doesn't it will just do the work when it's outside cpu-exec().

Theorically this is required only when a VCPU flushes the TLB of an 
other VCPU.
That's the behaviour in tlb_flush_all tlb_page_flush_all. The "normal" 
tlb_flush should
just work as it only plays with it's own CPUState.

Fred

> On Fri, Jul 10, 2015 at 11:36 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>>
>> On 10/07/2015 10:23, Alvise Rigo wrote:
>>> In order to perfom "lazy" TLB invalidation requests, introduce a
>>> queue of callbacks at every vCPU disposal that will be fired just
>>> before entering the next TB.
>>>
>>> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
>>> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
>>> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
>> Why is async_run_on_cpu not enough?
>>
>> Paolo
>>
>>> ---
>>>   cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>>>   exec.c            |  1 +
>>>   include/qom/cpu.h | 20 ++++++++++++++++++++
>>>   3 files changed, 55 insertions(+)
>>>
>>> diff --git a/cpus.c b/cpus.c
>>> index f4d938e..b9f0329 100644
>>> --- a/cpus.c
>>> +++ b/cpus.c
>>> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>>>           cpu->icount_extra = count;
>>>       }
>>>       qemu_mutex_unlock_iothread();
>>> +    cpu_exit_callbacks_call_all(cpu);
>>>       ret = cpu_exec(env);
>>>       cpu->tcg_executing = 0;
>>>
>>> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>>>       cpu->exit_request = 0;
>>>   }
>>>
>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>> +                           void *opaque)
>>> +{
>>> +    CPUExitCB *cb;
>>> +
>>> +    cb = g_malloc(sizeof(*cb));
>>> +    cb->callback = callback;
>>> +    cb->opaque = opaque;
>>> +
>>> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>> +}
>>> +
>>> +void cpu_exit_callbacks_call_all(CPUState *cpu)
>>> +{
>>> +    CPUExitCB *cb, *next;
>>> +
>>> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
>>> +        return;
>>> +    }
>>> +
>>> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
>>> +        cb->callback(cpu, cb->opaque);
>>> +
>>> +        /* one-shot callbacks, remove it after using it */
>>> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>> +        g_free(cb);
>>> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>> +    }
>>> +}
>>> +
>>>   void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
>>>   {
>>>       /* XXX: implement xxx_cpu_list for targets that still miss it */
>>> diff --git a/exec.c b/exec.c
>>> index 51958ed..322f2c6 100644
>>> --- a/exec.c
>>> +++ b/exec.c
>>> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>>>       cpu->numa_node = 0;
>>>       QTAILQ_INIT(&cpu->breakpoints);
>>>       QTAILQ_INIT(&cpu->watchpoints);
>>> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>>>   #ifndef CONFIG_USER_ONLY
>>>       cpu->as = &address_space_memory;
>>>       cpu->thread_id = qemu_get_thread_id();
>>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>>> index 8d121b3..0ec020b 100644
>>> --- a/include/qom/cpu.h
>>> +++ b/include/qom/cpu.h
>>> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>>>       QTAILQ_ENTRY(CPUWatchpoint) entry;
>>>   } CPUWatchpoint;
>>>
>>> +/* vCPU exit callbacks */
>>> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
>>> +struct CPUExitCBs {
>>> +    QemuMutex mutex;
>>> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
>>> +};
>>> +
>>> +typedef struct CPUExitCB {
>>> +    CPUExitCallback callback;
>>> +    void *opaque;
>>> +
>>> +    QTAILQ_ENTRY(CPUExitCB) entry;
>>> +} CPUExitCB;
>>> +
>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>> +                           void *opaque);
>>> +void cpu_exit_callbacks_call_all(CPUState *cpu);
>>> +
>>>   /* Rendezvous support */
>>>   #define TCG_RDV_POLLING_PERIOD 10
>>>   typedef struct CpuExitRendezvous {
>>> @@ -305,6 +323,8 @@ struct CPUState {
>>>
>>>       void *opaque;
>>>
>>> +    /* One-shot callbacks for stopping requests. */
>>> +    struct CPUExitCBs exit_cbs;
>>>       volatile int pending_rdv;
>>>
>>>       /* In order to avoid passing too many arguments to the MMIO helpers,
>>>
Alvise Rigo July 10, 2015, 10:06 a.m. UTC | #4
On Fri, Jul 10, 2015 at 11:53 AM, Frederic Konrad
<fred.konrad@greensocs.com> wrote:
> On 10/07/2015 11:47, alvise rigo wrote:
>>
>> I tried to use it, but it would then create a deadlock at a very early
>> stage of the stress test.
>> The problem is likely related to the fact that flush_queued_work
>> happens with the global mutex locked.
>>
>> As Frederick suggested, we can use the newly introduced
>> flush_queued_safe_work for this.
>>
>> Regards,
>> alvise
>
>
> It depends on the purpose.
>
> async safe work will requires all VCPU to be exited (eg: like your
> rendez-vous).
> async work doesn't it will just do the work when it's outside cpu-exec().

I guess that still this should work. If you look at helper_le_ldlink_name, only
the vCPUs in cpu-exec() receive a rendez-vous request while *all*
vCPUs are asked to perform a TLB flush.

alvise

>
> Theorically this is required only when a VCPU flushes the TLB of an other
> VCPU.
> That's the behaviour in tlb_flush_all tlb_page_flush_all. The "normal"
> tlb_flush should
> just work as it only plays with it's own CPUState.
>
> Fred
>
>
>> On Fri, Jul 10, 2015 at 11:36 AM, Paolo Bonzini <pbonzini@redhat.com>
>> wrote:
>>>
>>>
>>> On 10/07/2015 10:23, Alvise Rigo wrote:
>>>>
>>>> In order to perfom "lazy" TLB invalidation requests, introduce a
>>>> queue of callbacks at every vCPU disposal that will be fired just
>>>> before entering the next TB.
>>>>
>>>> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
>>>> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
>>>> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
>>>
>>> Why is async_run_on_cpu not enough?
>>>
>>> Paolo
>>>
>>>> ---
>>>>   cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>>>>   exec.c            |  1 +
>>>>   include/qom/cpu.h | 20 ++++++++++++++++++++
>>>>   3 files changed, 55 insertions(+)
>>>>
>>>> diff --git a/cpus.c b/cpus.c
>>>> index f4d938e..b9f0329 100644
>>>> --- a/cpus.c
>>>> +++ b/cpus.c
>>>> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>>>>           cpu->icount_extra = count;
>>>>       }
>>>>       qemu_mutex_unlock_iothread();
>>>> +    cpu_exit_callbacks_call_all(cpu);
>>>>       ret = cpu_exec(env);
>>>>       cpu->tcg_executing = 0;
>>>>
>>>> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>>>>       cpu->exit_request = 0;
>>>>   }
>>>>
>>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>>> +                           void *opaque)
>>>> +{
>>>> +    CPUExitCB *cb;
>>>> +
>>>> +    cb = g_malloc(sizeof(*cb));
>>>> +    cb->callback = callback;
>>>> +    cb->opaque = opaque;
>>>> +
>>>> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>>> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>>> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>>> +}
>>>> +
>>>> +void cpu_exit_callbacks_call_all(CPUState *cpu)
>>>> +{
>>>> +    CPUExitCB *cb, *next;
>>>> +
>>>> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next)
>>>> {
>>>> +        cb->callback(cpu, cb->opaque);
>>>> +
>>>> +        /* one-shot callbacks, remove it after using it */
>>>> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>>> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>>> +        g_free(cb);
>>>> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>>> +    }
>>>> +}
>>>> +
>>>>   void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char
>>>> *optarg)
>>>>   {
>>>>       /* XXX: implement xxx_cpu_list for targets that still miss it */
>>>> diff --git a/exec.c b/exec.c
>>>> index 51958ed..322f2c6 100644
>>>> --- a/exec.c
>>>> +++ b/exec.c
>>>> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>>>>       cpu->numa_node = 0;
>>>>       QTAILQ_INIT(&cpu->breakpoints);
>>>>       QTAILQ_INIT(&cpu->watchpoints);
>>>> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>>>>   #ifndef CONFIG_USER_ONLY
>>>>       cpu->as = &address_space_memory;
>>>>       cpu->thread_id = qemu_get_thread_id();
>>>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>>>> index 8d121b3..0ec020b 100644
>>>> --- a/include/qom/cpu.h
>>>> +++ b/include/qom/cpu.h
>>>> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>>>>       QTAILQ_ENTRY(CPUWatchpoint) entry;
>>>>   } CPUWatchpoint;
>>>>
>>>> +/* vCPU exit callbacks */
>>>> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
>>>> +struct CPUExitCBs {
>>>> +    QemuMutex mutex;
>>>> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
>>>> +};
>>>> +
>>>> +typedef struct CPUExitCB {
>>>> +    CPUExitCallback callback;
>>>> +    void *opaque;
>>>> +
>>>> +    QTAILQ_ENTRY(CPUExitCB) entry;
>>>> +} CPUExitCB;
>>>> +
>>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>>> +                           void *opaque);
>>>> +void cpu_exit_callbacks_call_all(CPUState *cpu);
>>>> +
>>>>   /* Rendezvous support */
>>>>   #define TCG_RDV_POLLING_PERIOD 10
>>>>   typedef struct CpuExitRendezvous {
>>>> @@ -305,6 +323,8 @@ struct CPUState {
>>>>
>>>>       void *opaque;
>>>>
>>>> +    /* One-shot callbacks for stopping requests. */
>>>> +    struct CPUExitCBs exit_cbs;
>>>>       volatile int pending_rdv;
>>>>
>>>>       /* In order to avoid passing too many arguments to the MMIO
>>>> helpers,
>>>>
>
Paolo Bonzini July 10, 2015, 10:24 a.m. UTC | #5
On 10/07/2015 11:47, alvise rigo wrote:
> I tried to use it, but it would then create a deadlock at a very early
> stage of the stress test.
> The problem is likely related to the fact that flush_queued_work
> happens with the global mutex locked.

Let's fix that and move the global mutex inside the callbacks.  I can
take a look.

Paolo

> As Frederick suggested, we can use the newly introduced
> flush_queued_safe_work for this.
> 
> Regards,
> alvise
> 
> On Fri, Jul 10, 2015 at 11:36 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>>
>>
>> On 10/07/2015 10:23, Alvise Rigo wrote:
>>> In order to perfom "lazy" TLB invalidation requests, introduce a
>>> queue of callbacks at every vCPU disposal that will be fired just
>>> before entering the next TB.
>>>
>>> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
>>> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
>>> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
>>
>> Why is async_run_on_cpu not enough?
>>
>> Paolo
>>
>>> ---
>>>  cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>>>  exec.c            |  1 +
>>>  include/qom/cpu.h | 20 ++++++++++++++++++++
>>>  3 files changed, 55 insertions(+)
>>>
>>> diff --git a/cpus.c b/cpus.c
>>> index f4d938e..b9f0329 100644
>>> --- a/cpus.c
>>> +++ b/cpus.c
>>> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>>>          cpu->icount_extra = count;
>>>      }
>>>      qemu_mutex_unlock_iothread();
>>> +    cpu_exit_callbacks_call_all(cpu);
>>>      ret = cpu_exec(env);
>>>      cpu->tcg_executing = 0;
>>>
>>> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>>>      cpu->exit_request = 0;
>>>  }
>>>
>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>> +                           void *opaque)
>>> +{
>>> +    CPUExitCB *cb;
>>> +
>>> +    cb = g_malloc(sizeof(*cb));
>>> +    cb->callback = callback;
>>> +    cb->opaque = opaque;
>>> +
>>> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>> +}
>>> +
>>> +void cpu_exit_callbacks_call_all(CPUState *cpu)
>>> +{
>>> +    CPUExitCB *cb, *next;
>>> +
>>> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
>>> +        return;
>>> +    }
>>> +
>>> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
>>> +        cb->callback(cpu, cb->opaque);
>>> +
>>> +        /* one-shot callbacks, remove it after using it */
>>> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>> +        g_free(cb);
>>> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>> +    }
>>> +}
>>> +
>>>  void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
>>>  {
>>>      /* XXX: implement xxx_cpu_list for targets that still miss it */
>>> diff --git a/exec.c b/exec.c
>>> index 51958ed..322f2c6 100644
>>> --- a/exec.c
>>> +++ b/exec.c
>>> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>>>      cpu->numa_node = 0;
>>>      QTAILQ_INIT(&cpu->breakpoints);
>>>      QTAILQ_INIT(&cpu->watchpoints);
>>> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>>>  #ifndef CONFIG_USER_ONLY
>>>      cpu->as = &address_space_memory;
>>>      cpu->thread_id = qemu_get_thread_id();
>>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>>> index 8d121b3..0ec020b 100644
>>> --- a/include/qom/cpu.h
>>> +++ b/include/qom/cpu.h
>>> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>>>      QTAILQ_ENTRY(CPUWatchpoint) entry;
>>>  } CPUWatchpoint;
>>>
>>> +/* vCPU exit callbacks */
>>> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
>>> +struct CPUExitCBs {
>>> +    QemuMutex mutex;
>>> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
>>> +};
>>> +
>>> +typedef struct CPUExitCB {
>>> +    CPUExitCallback callback;
>>> +    void *opaque;
>>> +
>>> +    QTAILQ_ENTRY(CPUExitCB) entry;
>>> +} CPUExitCB;
>>> +
>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>> +                           void *opaque);
>>> +void cpu_exit_callbacks_call_all(CPUState *cpu);
>>> +
>>>  /* Rendezvous support */
>>>  #define TCG_RDV_POLLING_PERIOD 10
>>>  typedef struct CpuExitRendezvous {
>>> @@ -305,6 +323,8 @@ struct CPUState {
>>>
>>>      void *opaque;
>>>
>>> +    /* One-shot callbacks for stopping requests. */
>>> +    struct CPUExitCBs exit_cbs;
>>>      volatile int pending_rdv;
>>>
>>>      /* In order to avoid passing too many arguments to the MMIO helpers,
>>>
> 
>
fred.konrad@greensocs.com July 10, 2015, 12:16 p.m. UTC | #6
On 10/07/2015 12:24, Paolo Bonzini wrote:
>
> On 10/07/2015 11:47, alvise rigo wrote:
>> I tried to use it, but it would then create a deadlock at a very early
>> stage of the stress test.
>> The problem is likely related to the fact that flush_queued_work
>> happens with the global mutex locked.
> Let's fix that and move the global mutex inside the callbacks.  I can
> take a look.
>
> Paolo

Meanwhile I can add a lock to protect the list as you suggested if I 
remember right.

Fred

>> As Frederick suggested, we can use the newly introduced
>> flush_queued_safe_work for this.
>>
>> Regards,
>> alvise
>>
>> On Fri, Jul 10, 2015 at 11:36 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>>>
>>> On 10/07/2015 10:23, Alvise Rigo wrote:
>>>> In order to perfom "lazy" TLB invalidation requests, introduce a
>>>> queue of callbacks at every vCPU disposal that will be fired just
>>>> before entering the next TB.
>>>>
>>>> Suggested-by: Jani Kokkonen <jani.kokkonen@huawei.com>
>>>> Suggested-by: Claudio Fontana <claudio.fontana@huawei.com>
>>>> Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com>
>>> Why is async_run_on_cpu not enough?
>>>
>>> Paolo
>>>
>>>> ---
>>>>   cpus.c            | 34 ++++++++++++++++++++++++++++++++++
>>>>   exec.c            |  1 +
>>>>   include/qom/cpu.h | 20 ++++++++++++++++++++
>>>>   3 files changed, 55 insertions(+)
>>>>
>>>> diff --git a/cpus.c b/cpus.c
>>>> index f4d938e..b9f0329 100644
>>>> --- a/cpus.c
>>>> +++ b/cpus.c
>>>> @@ -1421,6 +1421,7 @@ static int tcg_cpu_exec(CPUArchState *env)
>>>>           cpu->icount_extra = count;
>>>>       }
>>>>       qemu_mutex_unlock_iothread();
>>>> +    cpu_exit_callbacks_call_all(cpu);
>>>>       ret = cpu_exec(env);
>>>>       cpu->tcg_executing = 0;
>>>>
>>>> @@ -1469,6 +1470,39 @@ static void tcg_exec_all(CPUState *cpu)
>>>>       cpu->exit_request = 0;
>>>>   }
>>>>
>>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>>> +                           void *opaque)
>>>> +{
>>>> +    CPUExitCB *cb;
>>>> +
>>>> +    cb = g_malloc(sizeof(*cb));
>>>> +    cb->callback = callback;
>>>> +    cb->opaque = opaque;
>>>> +
>>>> +    qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>>> +    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>>> +    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>>> +}
>>>> +
>>>> +void cpu_exit_callbacks_call_all(CPUState *cpu)
>>>> +{
>>>> +    CPUExitCB *cb, *next;
>>>> +
>>>> +    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
>>>> +        cb->callback(cpu, cb->opaque);
>>>> +
>>>> +        /* one-shot callbacks, remove it after using it */
>>>> +        qemu_mutex_lock(&cpu->exit_cbs.mutex);
>>>> +        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
>>>> +        g_free(cb);
>>>> +        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
>>>> +    }
>>>> +}
>>>> +
>>>>   void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
>>>>   {
>>>>       /* XXX: implement xxx_cpu_list for targets that still miss it */
>>>> diff --git a/exec.c b/exec.c
>>>> index 51958ed..322f2c6 100644
>>>> --- a/exec.c
>>>> +++ b/exec.c
>>>> @@ -531,6 +531,7 @@ void cpu_exec_init(CPUArchState *env)
>>>>       cpu->numa_node = 0;
>>>>       QTAILQ_INIT(&cpu->breakpoints);
>>>>       QTAILQ_INIT(&cpu->watchpoints);
>>>> +    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
>>>>   #ifndef CONFIG_USER_ONLY
>>>>       cpu->as = &address_space_memory;
>>>>       cpu->thread_id = qemu_get_thread_id();
>>>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>>>> index 8d121b3..0ec020b 100644
>>>> --- a/include/qom/cpu.h
>>>> +++ b/include/qom/cpu.h
>>>> @@ -201,6 +201,24 @@ typedef struct CPUWatchpoint {
>>>>       QTAILQ_ENTRY(CPUWatchpoint) entry;
>>>>   } CPUWatchpoint;
>>>>
>>>> +/* vCPU exit callbacks */
>>>> +typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
>>>> +struct CPUExitCBs {
>>>> +    QemuMutex mutex;
>>>> +    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
>>>> +};
>>>> +
>>>> +typedef struct CPUExitCB {
>>>> +    CPUExitCallback callback;
>>>> +    void *opaque;
>>>> +
>>>> +    QTAILQ_ENTRY(CPUExitCB) entry;
>>>> +} CPUExitCB;
>>>> +
>>>> +void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
>>>> +                           void *opaque);
>>>> +void cpu_exit_callbacks_call_all(CPUState *cpu);
>>>> +
>>>>   /* Rendezvous support */
>>>>   #define TCG_RDV_POLLING_PERIOD 10
>>>>   typedef struct CpuExitRendezvous {
>>>> @@ -305,6 +323,8 @@ struct CPUState {
>>>>
>>>>       void *opaque;
>>>>
>>>> +    /* One-shot callbacks for stopping requests. */
>>>> +    struct CPUExitCBs exit_cbs;
>>>>       volatile int pending_rdv;
>>>>
>>>>       /* In order to avoid passing too many arguments to the MMIO helpers,
>>>>
>>
diff mbox

Patch

diff --git a/cpus.c b/cpus.c
index f4d938e..b9f0329 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1421,6 +1421,7 @@  static int tcg_cpu_exec(CPUArchState *env)
         cpu->icount_extra = count;
     }
     qemu_mutex_unlock_iothread();
+    cpu_exit_callbacks_call_all(cpu);
     ret = cpu_exec(env);
     cpu->tcg_executing = 0;
 
@@ -1469,6 +1470,39 @@  static void tcg_exec_all(CPUState *cpu)
     cpu->exit_request = 0;
 }
 
+void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
+                           void *opaque)
+{
+    CPUExitCB *cb;
+
+    cb = g_malloc(sizeof(*cb));
+    cb->callback = callback;
+    cb->opaque = opaque;
+
+    qemu_mutex_lock(&cpu->exit_cbs.mutex);
+    QTAILQ_INSERT_TAIL(&cpu->exit_cbs.exit_callbacks, cb, entry);
+    qemu_mutex_unlock(&cpu->exit_cbs.mutex);
+}
+
+void cpu_exit_callbacks_call_all(CPUState *cpu)
+{
+    CPUExitCB *cb, *next;
+
+    if (QTAILQ_EMPTY(&cpu->exit_cbs.exit_callbacks)) {
+        return;
+    }
+
+    QTAILQ_FOREACH_SAFE(cb, &cpu->exit_cbs.exit_callbacks, entry, next) {
+        cb->callback(cpu, cb->opaque);
+
+        /* one-shot callbacks, remove it after using it */
+        qemu_mutex_lock(&cpu->exit_cbs.mutex);
+        QTAILQ_REMOVE(&cpu->exit_cbs.exit_callbacks, cb, entry);
+        g_free(cb);
+        qemu_mutex_unlock(&cpu->exit_cbs.mutex);
+    }
+}
+
 void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
 {
     /* XXX: implement xxx_cpu_list for targets that still miss it */
diff --git a/exec.c b/exec.c
index 51958ed..322f2c6 100644
--- a/exec.c
+++ b/exec.c
@@ -531,6 +531,7 @@  void cpu_exec_init(CPUArchState *env)
     cpu->numa_node = 0;
     QTAILQ_INIT(&cpu->breakpoints);
     QTAILQ_INIT(&cpu->watchpoints);
+    QTAILQ_INIT(&cpu->exit_cbs.exit_callbacks);
 #ifndef CONFIG_USER_ONLY
     cpu->as = &address_space_memory;
     cpu->thread_id = qemu_get_thread_id();
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 8d121b3..0ec020b 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -201,6 +201,24 @@  typedef struct CPUWatchpoint {
     QTAILQ_ENTRY(CPUWatchpoint) entry;
 } CPUWatchpoint;
 
+/* vCPU exit callbacks */
+typedef void (*CPUExitCallback)(CPUState *cpu, void *opaque);
+struct CPUExitCBs {
+    QemuMutex mutex;
+    QTAILQ_HEAD(exit_callbacks_head, CPUExitCB) exit_callbacks;
+};
+
+typedef struct CPUExitCB {
+    CPUExitCallback callback;
+    void *opaque;
+
+    QTAILQ_ENTRY(CPUExitCB) entry;
+} CPUExitCB;
+
+void cpu_exit_callback_add(CPUState *cpu, CPUExitCallback callback,
+                           void *opaque);
+void cpu_exit_callbacks_call_all(CPUState *cpu);
+
 /* Rendezvous support */
 #define TCG_RDV_POLLING_PERIOD 10
 typedef struct CpuExitRendezvous {
@@ -305,6 +323,8 @@  struct CPUState {
 
     void *opaque;
 
+    /* One-shot callbacks for stopping requests. */
+    struct CPUExitCBs exit_cbs;
     volatile int pending_rdv;
 
     /* In order to avoid passing too many arguments to the MMIO helpers,