|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [Patch v5 2/5] x86/hpet: Use singe apic vector rather than irq_descs for HPET interrupts
On 22/11/13 15:45, Jan Beulich wrote:
>>>> On 14.11.13 at 17:01, Andrew Cooper <andrew.cooper3@xxxxxxxxxx> wrote:
>> The new logic is as follows:
>> * A single high priority vector is allocated and uses on all cpus.
> Does this really need to be a high priority one? I'd think we'd be
> fine with the lowest priority one we can get, as we only need the
> wakeup here if nothing else gets a CPU to wake up.
Yes - absolutely. We cannot have an HPET interrupt lower priority than
a guest line level interrupt.
Another cpu could be registered with our HPET channel to be worken up,
and we need to service them in a timely fashon.
>
>> +/* Wake up all cpus in the channel mask. Lock should be held. */
>> +static void hpet_wake_cpus(struct hpet_event_channel *ch)
>> {
>> - unsigned int cpu = smp_processor_id();
>> + cpuidle_wakeup_mwait(ch->cpumask);
>>
>> - if ( cpumask_test_and_clear_cpu(cpu, mask) )
>> - raise_softirq(TIMER_SOFTIRQ);
>> -
>> - cpuidle_wakeup_mwait(mask);
>> -
>> - if ( !cpumask_empty(mask) )
>> - cpumask_raise_softirq(mask, TIMER_SOFTIRQ);
>> + if ( !cpumask_empty(ch->cpumask) )
>> + cpumask_raise_softirq(ch->cpumask, TIMER_SOFTIRQ);
> Looks like the cpumask_empty() check isn't really needed here?
cpuidle_wakeup_mwait(mask) will only wake cpus who have set their bit in
cpuidle_mwait_flags.
There might be cpus who have registered for waking up who have not yet
set their bit in cpuidle_mwait_flags.
Out of caution, I did by best to avoid playing with the guts of the
timing code, as it seems quite fragile.
>
>> +/* HPET interrupt entry. This is set up as a high priority vector. */
>> +static void do_hpet_irq(struct cpu_user_regs *regs)
>> {
>> - struct hpet_event_channel *ch = (struct hpet_event_channel *)data;
>> -
>> - this_cpu(irq_count)--;
>> + unsigned int cpu = smp_processor_id();
> This is being used just once, and hence rather pointless to have.
Fair point - this was left over from a previous version of the function
which did use cpu twice.
>
>> - desc->handler = &hpet_msi_type;
>> - ret = request_irq(ch->msi.irq, hpet_interrupt_handler, "HPET", ch);
>> - if ( ret >= 0 )
>> - ret = __hpet_setup_msi_irq(desc);
>> + ret = hpet_setup_msi(ch);
> Why did you keep this? With that function now being called from
> hpet_broadcast_enter() I don't why you'd need this here (just
> like you're removing it from hpet_broadcast_resume() without
> replacement).
Because I optimised functions in the wrong order to obviously spot
this. As we leave the MSI masked, this call to setup can be dropped.
I would however prefer not to manually inline hpet_setup_msi() into
hpet_broadcast_enter(). The compiler can do that for me, and it saves
making hpet_broadcast_enter() even more complicated.
>
>> @@ -574,6 +384,7 @@ void __init hpet_broadcast_init(void)
>> /* Stop HPET legacy interrupts */
>> cfg &= ~HPET_CFG_LEGACY;
>> n = num_hpets_used;
>> + free_channels = (1U << n) - 1;
> This is undefined when n == 32. Since n > 0, I'd suggest
>
> free_channels = (u32)~0 >> (32 - n);
Ok
>
>> + ch = hpet_get_free_channel();
>> +
>> + if ( ch )
>> + {
>> + spin_lock(&ch->lock);
>> +
>> + /* This really should be an MSI channel by this point */
>> + ASSERT(!(ch->flags & HPET_EVT_LEGACY));
>> +
>> + hpet_msi_mask(ch);
>> +
>> + this_cpu(hpet_channel) = ch;
>> + ch->cpu = cpu;
> I think even if benign, you should set ->cpu before storing into
> hpet_channel.
Ok
>
>> + cpumask_set_cpu(cpu, ch->cpumask);
>> +
>> + hpet_setup_msi(ch);
>> + hpet_program_time(ch, deadline, NOW(), 1);
>> + hpet_msi_unmask(ch);
>> +
>> + spin_unlock(&ch->lock);
>> + }
>> + else
>> + {
>> + /* TODO - this seems very ugly */
> And you nevertheless want it committed this way?
Probably best the comment gets dropped.
>
>> + s_time_t fallback_deadline = STIME_MAX;
>> + unsigned int i, fallback_idx = -1;
>> +
>> + for ( i = 0; i < num_hpets_used; ++i )
>> + {
>> + ch = &hpet_events[i];
>> + spin_lock(&ch->lock);
>> +
>> + if ( ch->cpu == -1 )
>> + goto continue_search;
>> +
>> + /* This channel is going to expire far too early */
>> + if ( ch->next_event < deadline - MICROSECS(50) )
>> + goto continue_search;
> So this is going to make us wake early. You remember that all this
> code exists solely for power saving purposes? Iiuc the CPU will
> then basically spin entering an idle state, until its actual wakeup
> time is reached.
What would you suggest instead? We dont really want to be waking up late.
>
>> +
>> + /* We can deal with being woken with 50us to spare */
>> + if ( ch->next_event <= deadline )
>> + break;
>> +
>> + /* Otherwise record our best HPET to borrow. */
>> + if ( ch->next_event <= fallback_deadline )
>> + {
>> + fallback_idx = i;
>> + fallback_deadline = ch->next_event;
>> + }
>> +
>> + continue_search:
>> + spin_unlock(&ch->lock);
>> + ch = NULL;
>> + }
>> +
>> + if ( ch )
>> + {
>> + /* Found HPET with an appropriate time. Request to be woken up
>> */
>> + cpumask_set_cpu(cpu, ch->cpumask);
>> + this_cpu(hpet_channel) = ch;
>> + spin_unlock(&ch->lock);
>> + }
>> + else if ( fallback_deadline < STIME_MAX && fallback_deadline != -1 )
>> + {
>> + /*
>> + * Else we want to reprogram the fallback HPET sooner if
>> possible,
>> + * but with all the spinlocking, it just might have passed.
>> + */
>> + ch = &hpet_events[fallback_idx];
>> +
>> + spin_lock(&ch->lock);
>>
>> - if ( !(ch->flags & HPET_EVT_LEGACY) )
>> - hpet_attach_channel(cpu, ch);
>> + if ( ch->cpu != -1 && ch->next_event == fallback_deadline )
> Can't this be "<= deadline", being quite a bit more flexible?
This is a test for whether ch is the one I identified as the best inside
the loop. There should be sufficient flexibility inside the loop.
>
>> + {
>> + cpumask_set_cpu(cpu, ch->cpumask);
>> + hpet_program_time(ch, deadline, NOW(), 1);
>> + }
>> + else
>> + /* All else has failed. Wander the idle loop again */
>> + this_cpu(timer_deadline) = NOW() - 1;
>> +
>> + spin_unlock(&ch->lock);
>> + }
>> + else
>> + /* All HPETs were too soon. Wander the idle loop again */
>> + this_cpu(timer_deadline) = NOW() - 1;
> Here and above - what good will this do? Is this just in the
> expectation that the next time round a free channel will likely be
> found? If so, why can't you just go back to the start of the
> function.
Or a different HPET programmed to a different time which is now
compatible with our wakeup timeframe.
We cannot spin in this function, as we have interrupts disabled.
>
> Further - how do you see this going back to the idle loop? The
> way this is called from acpi/cpu_idle.c, you'll end up entering
> C3 anyway, with nothing going to wake you up. By proceeding to
> the end of the function, you even manage to disable the LAPIC
> timer.
Hmm - I will need to revisit this logic then.
>
>> + /* If we own the channel, detach it */
>> + if ( ch->cpu == cpu )
>> + {
>> + hpet_msi_mask(ch);
>> + hpet_wake_cpus(ch);
>> + ch->cpu = -1;
>> + set_bit(ch->idx, &free_channels);
> Wouldn't there be less cache line bouncing if the "free" flag was just
> one of the channel flags?
>
> Jan
Yes, but then finding a free channel would involve searching each
hpet_channel rather than searching free_channels with ffs().
~Andrew
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |