[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH RFC DO-NOT-APPLY] x86/IRQ: don't pass offline CPU(s) to on_selected_cpus()
on_selected_cpus() asserts that it's only passed online CPUs. Between __cpu_disable() removing a CPU from the online map and fixup_eoi() cleaning up for the CPU being offlined there's a window, though, where the CPU in question can still appear in an action's cpu_eoi_map. Remove offline CPUs, as they're (supposed to be) taken care of by fixup_eoi(). Move the entire tail of desc_guest_eoi() into a new helper, thus streamlining processinf also for other call sites when the sole remaining CPU is the local one. Move and split the assertion, to be a functionally equivalent replacement for the earlier BUG_ON() in __pirq_guest_unbind(). Note that we can't use scratch_cpumask there, in particular in the context of a timer handler and with data held there needing to stay intact across process_pending_softirqs(). Reported-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> --- While this builds okay, for now I didn't even consider testing it: Both aspects below (plus the ugly locking arrangement) speak against dealing with the issue this way, imo. Cc-ing REST rather than just x86 for this reason. RFC: Don't we need {get,put}_cpu_maps() around quite a few more uses of cpu_online_map (e.g. _clear_irq_vector() when called from destroy_irq(), or about every caller of smp_call_function())? Roger suggests using stop-machine context for CPU {on,off}lining, but I seem to vaguely recall that there was a reason not to do so at least for the offlining part. RFC: I doubt process_pending_softirqs() is okay to use from a timer handler. (Callers of, in particular, {desc,pirq}_guest_eoi() would also need checking for process_pending_softirqs() to be okay to use in their contexts.) --- unstable.orig/xen/arch/x86/irq.c 2021-04-08 11:10:47.000000000 +0200 +++ unstable/xen/arch/x86/irq.c 2025-02-11 09:54:38.532567287 +0100 @@ -6,6 +6,7 @@ */ #include <xen/init.h> +#include <xen/cpu.h> #include <xen/delay.h> #include <xen/errno.h> #include <xen/event.h> @@ -1183,7 +1184,7 @@ static inline void clear_pirq_eoi(struct } } -static void cf_check set_eoi_ready(void *data); +static bool invoke_set_eoi_ready(struct irq_desc *desc, bool wait); static void cf_check irq_guest_eoi_timer_fn(void *data) { @@ -1224,18 +1225,13 @@ static void cf_check irq_guest_eoi_timer switch ( action->ack_type ) { - cpumask_t *cpu_eoi_map; - case ACKTYPE_UNMASK: if ( desc->handler->end ) desc->handler->end(desc, 0); break; case ACKTYPE_EOI: - cpu_eoi_map = this_cpu(scratch_cpumask); - cpumask_copy(cpu_eoi_map, action->cpu_eoi_map); - spin_unlock_irq(&desc->lock); - on_selected_cpus(cpu_eoi_map, set_eoi_ready, desc, 0); + invoke_set_eoi_ready(desc, false); return; } @@ -1468,6 +1464,49 @@ static void cf_check set_eoi_ready(void flush_ready_eoi(); } +/* + * Locking is somewhat special here: In all cases the function is invoked with + * desc's lock held. When @wait is true, the function tries to avoid unlocking. + * It always returns whether the lock is still held. + */ +static bool invoke_set_eoi_ready(struct irq_desc *desc, bool wait) +{ + const irq_guest_action_t *action = guest_action(desc); + cpumask_t cpu_eoi_map; + + cpumask_copy(&cpu_eoi_map, action->cpu_eoi_map); + + if ( __cpumask_test_and_clear_cpu(smp_processor_id(), &cpu_eoi_map) ) + { + ASSERT(action->ack_type == ACKTYPE_EOI); + __set_eoi_ready(desc); + spin_unlock(&desc->lock); + flush_ready_eoi(); + local_irq_enable(); + } + else if ( wait && cpumask_empty(&cpu_eoi_map) ) + return true; + else + { + ASSERT(action->ack_type == ACKTYPE_EOI); + spin_unlock_irq(&desc->lock); + } + + if ( cpumask_empty(&cpu_eoi_map) ) + return false; + + while ( !get_cpu_maps() ) + process_pending_softirqs(); + + cpumask_and(&cpu_eoi_map, &cpu_eoi_map, &cpu_online_map); + if ( !cpumask_empty(&cpu_eoi_map) ) + on_selected_cpus(&cpu_eoi_map, set_eoi_ready, desc, wait); + + put_cpu_maps(); + + return false; +} + void pirq_guest_eoi(struct pirq *pirq) { struct irq_desc *desc; @@ -1481,7 +1520,6 @@ void pirq_guest_eoi(struct pirq *pirq) void desc_guest_eoi(struct irq_desc *desc, struct pirq *pirq) { irq_guest_action_t *action = guest_action(desc); - cpumask_t cpu_eoi_map; if ( unlikely(!action) || unlikely(!test_and_clear_bool(pirq->masked)) || @@ -1502,24 +1540,7 @@ void desc_guest_eoi(struct irq_desc *des return; } - ASSERT(action->ack_type == ACKTYPE_EOI); - - cpumask_copy(&cpu_eoi_map, action->cpu_eoi_map); - - if ( __cpumask_test_and_clear_cpu(smp_processor_id(), &cpu_eoi_map) ) - { - __set_eoi_ready(desc); - spin_unlock(&desc->lock); - flush_ready_eoi(); - local_irq_enable(); - } - else - { - spin_unlock_irq(&desc->lock); - } - - if ( !cpumask_empty(&cpu_eoi_map) ) - on_selected_cpus(&cpu_eoi_map, set_eoi_ready, desc, 0); + invoke_set_eoi_ready(desc, false); } int pirq_guest_unmask(struct domain *d) @@ -1734,7 +1755,6 @@ static irq_guest_action_t *__pirq_guest_ struct domain *d, struct pirq *pirq, struct irq_desc *desc) { irq_guest_action_t *action = guest_action(desc); - cpumask_t cpu_eoi_map; int i; if ( unlikely(action == NULL) ) @@ -1765,12 +1785,7 @@ static irq_guest_action_t *__pirq_guest_ if ( test_and_clear_bool(pirq->masked) && (--action->in_flight == 0) && (action->nr_guests != 0) ) - { - cpumask_copy(&cpu_eoi_map, action->cpu_eoi_map); - spin_unlock_irq(&desc->lock); - on_selected_cpus(&cpu_eoi_map, set_eoi_ready, desc, 0); - spin_lock_irq(&desc->lock); - } + invoke_set_eoi_ready(desc, false); break; } @@ -1796,14 +1811,8 @@ static irq_guest_action_t *__pirq_guest_ * would need to flush all ready EOIs before returning as otherwise the * desc->handler could change and we would call the wrong 'end' hook. */ - cpumask_copy(&cpu_eoi_map, action->cpu_eoi_map); - if ( !cpumask_empty(&cpu_eoi_map) ) - { - BUG_ON(action->ack_type != ACKTYPE_EOI); - spin_unlock_irq(&desc->lock); - on_selected_cpus(&cpu_eoi_map, set_eoi_ready, desc, 1); + if ( !invoke_set_eoi_ready(desc, true) ) spin_lock_irq(&desc->lock); - } BUG_ON(!cpumask_empty(action->cpu_eoi_map));
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |