[Qemu-ppc] pseries on qemu-system-ppc64le crashes in doorbell_core_ipi()

Peter Zijlstra peterz at infradead.org
Mon Apr 1 19:38:27 AEDT 2019


+ fweisbec, who did the remote bits

On Sat, Mar 30, 2019 at 01:10:28PM +1000, Nicholas Piggin wrote:
> Something like this?
> 
> kernel/irq_work: Do not raise an IPI when queueing work on the local CPU
> 
> The QEMU powerpc/pseries machine model was not expecting a self-IPI,
> and it may be a bit surprising thing to do, so have irq_work_queue_on
> do local queueing when target is current CPU.

This seems OK to me.

> Suggested-by: Steven Rostedt <rostedt at goodmis.org>
> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>

Acked-by: Peter Zijlstra (Intel) <peterz at infradead.org>

> ---
>  kernel/irq_work.c | 78 ++++++++++++++++++++++++++---------------------
>  1 file changed, 43 insertions(+), 35 deletions(-)
> 
> diff --git a/kernel/irq_work.c b/kernel/irq_work.c
> index 6b7cdf17ccf8..f0e539d0f879 100644
> --- a/kernel/irq_work.c
> +++ b/kernel/irq_work.c
> @@ -56,61 +56,69 @@ void __weak arch_irq_work_raise(void)
>  	 */
>  }
>  
> -/*
> - * Enqueue the irq_work @work on @cpu unless it's already pending
> - * somewhere.
> - *
> - * Can be re-enqueued while the callback is still in progress.
> - */
> -bool irq_work_queue_on(struct irq_work *work, int cpu)
> +/* Enqueue on current CPU, work must already be claimed and preempt disabled */
> +static void __irq_work_queue(struct irq_work *work)
>  {
> -	/* All work should have been flushed before going offline */
> -	WARN_ON_ONCE(cpu_is_offline(cpu));
> -
> -#ifdef CONFIG_SMP
> -
> -	/* Arch remote IPI send/receive backend aren't NMI safe */
> -	WARN_ON_ONCE(in_nmi());
> +	/* If the work is "lazy", handle it from next tick if any */
> +	if (work->flags & IRQ_WORK_LAZY) {
> +		if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) &&
> +		    tick_nohz_tick_stopped())
> +			arch_irq_work_raise();
> +	} else {
> +		if (llist_add(&work->llnode, this_cpu_ptr(&raised_list)))
> +			arch_irq_work_raise();
> +	}
> +}
>  
> +/* Enqueue the irq work @work on the current CPU */
> +bool irq_work_queue(struct irq_work *work)
> +{
>  	/* Only queue if not already pending */
>  	if (!irq_work_claim(work))
>  		return false;
>  
> -	if (llist_add(&work->llnode, &per_cpu(raised_list, cpu)))
> -		arch_send_call_function_single_ipi(cpu);
> -
> -#else /* #ifdef CONFIG_SMP */
> -	irq_work_queue(work);
> -#endif /* #else #ifdef CONFIG_SMP */
> +	/* Queue the entry and raise the IPI if needed. */
> +	preempt_disable();
> +	__irq_work_queue(work);
> +	preempt_enable();
>  
>  	return true;
>  }
> +EXPORT_SYMBOL_GPL(irq_work_queue);
>  
> -/* Enqueue the irq work @work on the current CPU */
> -bool irq_work_queue(struct irq_work *work)
> +/*
> + * Enqueue the irq_work @work on @cpu unless it's already pending
> + * somewhere.
> + *
> + * Can be re-enqueued while the callback is still in progress.
> + */
> +bool irq_work_queue_on(struct irq_work *work, int cpu)
>  {
> +#ifndef CONFIG_SMP
> +	return irq_work_queue(work);
> +
> +#else /* #ifndef CONFIG_SMP */
> +	/* All work should have been flushed before going offline */
> +	WARN_ON_ONCE(cpu_is_offline(cpu));
> +
>  	/* Only queue if not already pending */
>  	if (!irq_work_claim(work))
>  		return false;
>  
> -	/* Queue the entry and raise the IPI if needed. */
>  	preempt_disable();
> -
> -	/* If the work is "lazy", handle it from next tick if any */
> -	if (work->flags & IRQ_WORK_LAZY) {
> -		if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) &&
> -		    tick_nohz_tick_stopped())
> -			arch_irq_work_raise();
> -	} else {
> -		if (llist_add(&work->llnode, this_cpu_ptr(&raised_list)))
> -			arch_irq_work_raise();
> -	}
> -
> +	if (cpu != smp_processor_id()) {
> +		/* Arch remote IPI send/receive backend aren't NMI safe */
> +		WARN_ON_ONCE(in_nmi());
> +		if (llist_add(&work->llnode, &per_cpu(raised_list, cpu)))
> +			arch_send_call_function_single_ipi(cpu);
> +	} else
> +		__irq_work_queue(work);
>  	preempt_enable();
>  
>  	return true;
> +#endif /* #else #ifndef CONFIG_SMP */
>  }
> -EXPORT_SYMBOL_GPL(irq_work_queue);
> +
>  
>  bool irq_work_needs_cpu(void)
>  {
> -- 
> 2.20.1
> 


More information about the Linuxppc-dev mailing list