On Mon, 03 Jul 2017 04:13:34 PDT (-0700), t...@linutronix.de wrote:
> On Thu, 29 Jun 2017, Palmer Dabbelt wrote:
>> On Wed, 28 Jun 2017 13:47:40 PDT (-0700), t...@linutronix.de wrote:
>> In this case the software interrupt is to handle IPIs, so it doesn't really
>> make any sense to handle one without SMP.  I'm OK with just warning, though, 
>> as
>> the IPIs are just for TLB shootdowns so skipping one on a non-SMP system 
>> seems
>> safe.
>
> Indeed.
>
>> >> +static void riscv_irq_mask(struct irq_data *d)
>> >> +{
>> >> + struct riscv_irq_data *data = irq_data_get_irq_chip_data(d);
>> >> +
>> >> + BUG_ON(smp_processor_id() != data->hart);
>> >
>> > Crashing the machine is the last resort if there is no chance to handle a
>> > situation gracefully. Something like
>> >
>> >    if (WARN_ON_ONCE(smp_processor_id() != data->hart))
>> >            do_something_sensible();
>> >    else
>> >            .....
>> >
>> > might at least keep the machine halfways functional for debugging.
>>
>> In this case I think BUG_ON is the only sane thing to do.  I've gone and 
>> added
>> a comment that explains what's going on here
>>
>>   diff --git a/drivers/irqchip/irq-riscv-intc.c 
>> b/drivers/irqchip/irq-riscv-intc.c
>>   index 7e55fe57e95f..3dd421ade128 100644
>>   --- a/drivers/irqchip/irq-riscv-intc.c
>>   +++ b/drivers/irqchip/irq-riscv-intc.c
>>   @@ -97,6 +97,14 @@ static const struct irq_domain_ops riscv_irqdomain_ops 
>> = {
>>           .xlate  = irq_domain_xlate_onecell,
>>    };
>>
>>   +/*
>>   + * On RISC-V systems local interrupts are masked or unmasked by writing 
>> the SIE
>>   + * (Supervisor Interrupt Enable) CSR.  As CSRs can only be written on the 
>> local
>>   + * hart, these functions can only be called on the hart that cooresponds 
>> to the
>>   + * IRQ chip.  They are only called internally to this module, so they 
>> BUG_ON if
>>   + * this condition is violated rather than attempting to handle the error 
>> by
>>   + * forwarding to the target hart, as that's already expected to have been 
>> done.
>>   + */
>>    static void riscv_irq_mask(struct irq_data *d)
>>    {
>>           struct riscv_irq_data *data = irq_data_get_irq_chip_data(d);
>>
>> I think there's three options here:
>>
>>  * BUG_ON like we do.
>>  * Attempt to jump to the correct hart to set the CSR, but since we just did
>>    that I don't really see why it would work the second time.
>>  * Provide a warning and then ignore {un,}masking the IRQ, but that seems
>>    dangerous.
>>
>> I can change it to a warning if you think it's better.
>
> With a coherent explanation why a BUG_ON() is the right thing to do, the
> BUG_ON can stay.
>
>>   diff --git a/drivers/irqchip/irq-riscv-intc.c 
>> b/drivers/irqchip/irq-riscv-intc.c
>>   index 3dd421ade128..b2643f7131ff 100644
>>   --- a/drivers/irqchip/irq-riscv-intc.c
>>   +++ b/drivers/irqchip/irq-riscv-intc.c
>>   @@ -137,9 +137,13 @@ static void riscv_irq_enable(struct irq_data *d)
>>           struct riscv_irq_data *data = irq_data_get_irq_chip_data(d);
>>
>>           /*
>>   -        * There are two phases to setting up an interrupt: first we set a 
>> bit
>>   -        * in this bookkeeping structure, which is used by trap_init to
>>   -        * initialize SIE for each hart as it comes up.
>>   +        * When booting a RISC-V system, procesors can come online at any 
>> time.
>
> Not so much at any time. The kernel orchestrates that.
>
>>   +        * Interrupts can only be enabled or disabled by writing a CSR on 
>> the
>>   +        * hart that cooresponds to that interrupt controller, but CSRs can
>>   +        * only be written locally.  In order to avoid waiting a long time 
>> for
>>   +        * a hart to boot, we instead collect the interrupts to be enabled 
>> upon
>>   +        * booting a hart in this bookkeeping structure, which is used by
>>   +        * trap_init to initialize SIE for each hart as it comes up.
>>            */
>>           atomic_long_or((1 << (long)d->hwirq),
>>                          &per_cpu(riscv_early_sie, data->hart));
>
> That still does not answer the question WHY an interrupt for a not
> available CPU would be enabled in the first place.
>
>> >> +static int riscv_intc_init(struct device_node *node, struct device_node 
>> >> *parent)
>> >> +{
>> >> + int hart;
>> >> + struct riscv_irq_data *data;
>> >> +
>> >> + if (parent)
>> >> +         return 0;
>> >> +
>> >> + hart = riscv_of_processor_hart(node->parent);
>> >> + if (hart < 0) {
>> >> +         /* If a hart is disabled, create a no-op irq domain.  Devices
>> >> +          * may still have interrupts connected to those harts.  This is
>> >> +          * not wrong... unless they actually load a driver that needs
>> >> +          * it!
>> >> +          */
>> >> +         irq_domain_add_linear(
>> >> +                 node,
>> >> +                 8*sizeof(uintptr_t),
>> >> +                 &riscv_irqdomain_ops_noop,
>> >> +                 node->parent);
>> >> +         return 0;
>> >
>> > I have a hard time to understand the logic here. Why do you need an
>> > interrupt domain for something which does not exist? That does not make any
>> > sense.
>>
>> I think this is best explained as a comment, which I've added
>>
>>   diff --git a/drivers/irqchip/irq-riscv-intc.c 
>> b/drivers/irqchip/irq-riscv-intc.c
>>   index a62a1f04198e..52db58a94bdc 100644
>>   --- a/drivers/irqchip/irq-riscv-intc.c
>>   +++ b/drivers/irqchip/irq-riscv-intc.c
>>   @@ -178,6 +178,32 @@ static void riscv_irq_disable(struct irq_data *d)
>>                                            true);
>>    }
>>
>>   +/*
>>   + * This "no op" interrupt handler deals with harts that for some reason 
>> exist
>>   + * but can't actually run Linux.  Examples of these sorts of harts 
>> include:
>>   + * - Harts that don't report an "okay" status, presumably because of a 
>> hardware
>>   + *   fault.
>>   + * - Harts with an ID larger than NR_CPUS.
>>   + * - Harts with an ISA we don't understand.
>>   + *
>>   + * Interrupts targeted to these harts won't even actually be seen by 
>> Linux, as
>>   + * there's no code running to ever receive the interrupt.  If this is an
>>   + * invalid system configuration then there's nothing we can really do 
>> about it,
>>   + * but we expect these configurations to usually be valid.
>
> But what would enable such interrupts in the first place? The device whose
> interrupt is routed into nirwana is going to be non-functional. See below.
>
>>   + * For example, we generally allow the PLIC to route interrupts to every 
>> hart
>>   + * in the system via the local interrupt controller on every hart.
>
> That's fine, but you really want to control that via the interrupt affinity
> mechanism and not by blindly routing every interrupt to every possible CPU
> in the system. That mechanism is there for a reason.
>
>>   + * When Linux
>>   + * is built without CONFIG_SMP this still allows for a working system, as 
>> the
>>   + * single enabled hart can handle every device interrupt in the system.
>
> Well, that's how it should be. But that still does not require to handle
> anything which is not usable.
>
>>   + * It is
>>   + * possible to build a PLIC that doesn't allow interrupts to be routed to 
>> the
>>   + * first hart to boot.  If for some reason the PLIC was instantiated in 
>> this
>>   + * manner then whatever devices could not be directed to the first hart 
>> to boot
>>   + * would be unusable on non-CONFIG_SMP kernels, but as we can't change 
>> the PLIC
>>   + * hardware there's nothing we can do about that.
>>   + *
>>   + * In order to avoid the complexity of having unmapped IRQ domains, we 
>> instead
>>   + * just install the noop IRQ handler in domains we can't handle.
>
> What's complex about that? If a device driver tries to request an unmapped
> irq then the request will fail and the driver can act accordingly.
>
> Silently pretending success and then letting the user bang his head against
> the wall is definitely the worst thing you can do.

OK, we'll do it that way.  I'll submit another patch set soon.

Thanks!

Reply via email to