[PATCH] irq: fix the interrupt trigger type override issue

Richard Clark richard.xnu.clark at gmail.com
Tue Sep 3 00:55:16 PDT 2024


Hi Thomas,

On Mon, Sep 02, 2024 at 04:39:33PM +0200, Thomas Gleixner wrote:
> Richard!
> 
> On Mon, Sep 02 2024 at 20:50, richard clark wrote:
> > On Mon, Sep 2, 2024 at 5:51 PM Thomas Gleixner <tglx at linutronix.de> wrote:
> >> >> 2) rmmod()
> >> >>      tears down mapping
> >> >>
> >> > This just tears down the action allocated and installed by
> >> > request_irq(...), but does not teardown the irq's node inserted in the
> >> > revmap_tree.
> >>
> >> So what creates the mapping? If the driver creates it then why doesn't
> >> it unmap it when it exits?
> >>
> > Kernel allocates an irq and creates the mapping during its
> > initialization when parsing the device's interrupt firmware, not the
> > driver does that.
> 
> So the mapping and the interrupt allocation persist even if nothing uses
> them. What a waste.
>
I checked the code and found that it's not the kernel to create the mapping,
it's by the driver calling platform_get_irq(...)/of_irq_get(...) to create. 
> 
> >> > The logic is if the trigger type specified by request_irq(...) is not
> >> > consistent with the firmware one, the request_irq will override the
> >> > FW. We need to keep this logic the same as when we insmod the same
> >> > kmod next time -- override the FW's too instead of returning a
> >> > mismatch type error.
> >>
> >> I can see how that can happen, but what's missing is the information why
> >> this mapping persists and why it's tried to be set up again.
> >>
> > As I mentioned, it doesn't try to set up again. It will just lookup
> > the mapping from the tree for the persistent reason when driver try to
> > request the irq...
> 
> Fair enough. Though the logic in that map code is convoluted as hell and
> making it more convoluted does not really make it better.
> 
> So let's look at how this works (or not):
> 
> 1)
>    map()
>    allocate_irq();
>    set_trigger_type(FW);
> 
> 2)
>    request_irq(type = DRV);
>    set_trigger_type(DRV);
> 
> 3)
>    free_irq();
>    // type is not reset to FW
> 
> 4)
>    map()
>      irq_trigger_type() != NONE && != FW
>         -> fail
> 
> This results in a pile of questions:
> 
>   Why does #2 override the firmware, if the firmware defines a trigger
>   type != NONE?
> 
>   Isn't the whole point of firmware defining the type that the driver
>   does not need to care?
> 
>   If the firmware cannot does not know what the right thing is then it
>   should say so by using type NONE and the type is using the hardware
>   or interrupt driver default.
> 
> That aside. What you are trying to do only works when #2 and #4 are
> coming from the exactly same context.
> 
> What it does not catch is when the interrupt line is shared and there
> are two drivers where the first one fiddles with type on request_irq()
> and the second one relies on the firmware to do the right thing.
> 
> Same problem if you unload the driver and remove the type from
> request_irq() flags because you figured out that this was bogus. Then
> you end up with the old setting when you load the recompiled driver
> again.
> 
> That's all inconsistent. The proper solution would be to restore the
> firmware setting in free_irq() when the last action goes away.
> 
> The question is whether it's worth the trouble. If not then we can just
> make all of this simple, trivial and incomplete instead of making it
> more complex and differently incomplete.
>

Ah, the mapping is created from of_irq_get(...) by driver, the kernel also
provides the mapping teardown interface - irq_dispose_mapping.
IMO, the right way for the driver is:
	1) driver calls of_irq_get() to get the irq and create the mapping
	2) driver *should* call irq_dispose_mapping() as the teardown of step 1.
	3) free_irq is the teardown of the request_irq to free the irq and
	   its action.
So the original issue should be the bug of the driver not calling the
irq_dispose_mapping to release the mapping(and being persistent there). 
The error message will not show if irq_dispose_mapping(...) called by
the driver.
Looks like the current implementation is correct, but I don't know if it's true
for the shared irq...

Thanks!

> 
> Thanks,
> 
>         tglx
> ---
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -895,32 +895,14 @@ unsigned int irq_create_fwspec_mapping(s
>  	 */
>  	virq = irq_find_mapping(domain, hwirq);
>  	if (virq) {
> -		/*
> -		 * If the trigger type is not specified or matches the
> -		 * current trigger type then we are done so return the
> -		 * interrupt number.
> -		 */
> -		if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
> -			goto out;
> -
> -		/*
> -		 * If the trigger type has not been set yet, then set
> -		 * it now and return the interrupt number.
> -		 */
> -		if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
> +		/* Preserve the trigger type set by request_irq() */
> +		if (type != IRQ_TYPE_NONE && irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
>  			irq_data = irq_get_irq_data(virq);
> -			if (!irq_data) {
> +			if (irq_data)
> +				irqd_set_trigger_type(irq_data, type);
> +			else
>  				virq = 0;
> -				goto out;
> -			}
> -
> -			irqd_set_trigger_type(irq_data, type);
> -			goto out;
>  		}
> -
> -		pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
> -			hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
> -		virq = 0;
>  		goto out;
>  	}
>  
> 
>    



More information about the linux-arm-kernel mailing list