All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oleksii Kurochko <oleksii.kurochko@gmail.com>
To: Jan Beulich <jbeulich@suse.com>
Cc: Romain Caritey <Romain.Caritey@microchip.com>,
	Baptiste Le Duc <baptiste.le-duc@vates.tech>,
	Alistair Francis <alistair.francis@wdc.com>,
	Connor Davis <connojdavis@gmail.com>,
	"Daniel P. Smith" <dpsmith@apertussolutions.com>,
	xen-devel@lists.xenproject.org
Subject: Re: [PATCH v4 21/25] xen/riscv: implement IRQ routing for device passthrough
Date: Wed, 1 Jul 2026 16:49:54 +0200	[thread overview]
Message-ID: <6aafbeb5-2f33-49f4-9d14-937b0bc4d15d@gmail.com> (raw)
In-Reply-To: <ac1a3a08-dc39-4794-ba96-dab4b88c27a3@suse.com>



On 6/29/26 5:55 PM, Jan Beulich wrote:
> On 26.06.2026 17:46, Oleksii Kurochko wrote:
>> dom0less device passthrough requires granting guest domains access to
>> device interrupts.  Introduce map_device_irqs_to_domain() to enumerate
>> a DT node's interrupt properties, skipping those not owned by
>> the primary interrupt controller (as at the moment I haven't seen usages
>> of it), and map_irq_to_domain() to grant domain access and configure
>> Xen's interrupt descriptor accordingly. Sharing IRQ between domains is
>> rejected.
>>
>> Both map_irq_to_domain() and map_device_irqs_to_domain() are marked
>> __overlay_init, mirroring Arm: without CONFIG_OVERLAY_DTB this expands to
>> __init, so the functions are init-only and need no XSM check; with
>> CONFIG_OVERLAY_DTB they become runtime-callable, but the only runtime
>> entry point is dt_overlay_domctl(), which performs the XSM checks at the
>> domctl layer.  RISC-V does not wire up DT overlay yet, so today these are
>> strictly __init; if/when overlay support is added, the domctl-level XSM
>> gating must be added together with it, as on Arm.
>>
>> route_irq_to_guest() and release_irq() manage irq_desc ownership for
>> guest-assigned interrupts.  Each assignment carries a small irq_guest
>> structure as irqaction::dev_id, recording the owning domain and virtual
>> IRQ number which is 1:1 mapped to physical IRQ number.  A per-domain
>> vIRQ allocation bitmap (used_irqs in struct vintc), managed by
>> vintc_reserve_virq(), prevents the same vIRQ being claimed twice.
>>
>> Host and guest interrupts may differ in some operations (EOI timing in
>> particular, possibly others): a host IRQ is completed once Xen's handler
>> runs, whereas a passthrough IRQ must defer the physical completion until
>> the guest issues its own EOI, otherwise a still-asserted level line would
>> immediately retrigger and storm.  This affects only the .end callback;
>> the rest of hw_interrupt_type is shared, hence the separate host and
>> guest hw_interrupt_type instances.
>>
>> With APLIC+IMSIC, guest interrupts are delivered directly by hardware
>> through the IMSIC, bypassing do_IRQ(). The _IRQ_GUEST branch in
>> do_IRQ() is therefore left as BUG() until a platform without direct
>> IMSIC delivery is encountered.
> 
> And this is secure, i.e. one guest (by mishandling things, e.g. simply
> never claiming / servicing an interrupt) cannot affect another guest?

It will be just affect this specific interrupt which isn't claimed/serviced.

> 
>> +int __overlay_init map_device_irqs_to_domain(struct domain *d,
>> +                                             struct dt_device_node *dev,
>> +                                             bool need_mapping,
>> +                                             struct rangeset *irq_ranges)
>> +{
>> +    unsigned int i, nirq = dt_number_of_irq(dev);
>> +
>> +    if ( irq_ranges )
>> +        return -EOPNOTSUPP;
>> +
>> +    /* Give permission and map IRQs */
>> +    for ( i = 0; i < nirq; i++ )
>> +    {
>> +        int res, irq;
>> +        struct dt_raw_irq rirq;
>> +
>> +        res = dt_device_get_raw_irq(dev, i, &rirq);
>> +        if ( res )
>> +        {
>> +            printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
>> +                   i, dt_node_full_name(dev));
>> +            return res;
>> +        }
>> +
>> +        /*
>> +         * Don't map IRQs that have no physical meaning
>> +         * ie: IRQs whose controller is not APLIC/IMSIC/PLIC.
>> +         */
>> +        if ( rirq.controller != dt_interrupt_controller )
>> +        {
>> +            dt_dprintk("irq %u not connected to primary controller."
>> +                       "Connected to %s\n", i,
> 
> Nit: By splitting a format string like this, you pretty effectively hide
> that there's a blank missing after the full stop.
> 
> Further after an already wrapped function argument there shouldn't follow
> another one, to maintain visual clarity.

I would be okay to have a format string a little bit long. I would 
change that line to (if you are okay with that):
   ...("irq %u not connected to primary controller. Connected to %s\n",
       i, dt_node_full_name(rirq.controller));

> 
>> @@ -101,12 +119,28 @@ int domain_vintc_init(struct domain *d)
>>           break;
>>       }
>>   
>> +    if ( !ret )
>> +    {
>> +        d->arch.vintc->used_irqs =
>> +            xvzalloc_array(unsigned long, BITS_TO_LONGS(d->arch.vintc->nr_virqs));
> 
> Nit: Overlong line.
> 

I will reformat that in the following way:
         d->arch.vintc->used_irqs =
             xvzalloc_array(unsigned long,
                            BITS_TO_LONGS(d->arch.vintc->nr_virqs));

>> +        if ( !d->arch.vintc->used_irqs )
>> +            ret = -ENOMEM;
>> +    }
>> +
>>       return ret;
>>   }
>>   
>>   void domain_vintc_deinit(struct domain *d)
>>   {
>>       const enum intc_variant variant = intc_hw_ops->info->hw_variant;
>> +    unsigned int virq;
>> +
>> +    if ( !d->arch.vintc )
>> +        return;
> 
> Seeing this and ...
> 
>> +    for ( virq = 0; virq < d->arch.vintc->nr_virqs; virq++ )
>> +        if ( test_bit(virq, d->arch.vintc->used_irqs) )
>> +            release_guest_irq(d, virq);
>>   
>>       switch ( variant )
>>       {
>> @@ -117,4 +151,14 @@ void domain_vintc_deinit(struct domain *d)
>>       default:
>>           break;
>>       }
>> +
>> +    XVFREE(d->arch.vintc->used_irqs);
> 
> ... this, ...
> 
>> +}
> 
> ... where is d->arch.vintc being freed? That would logically look to
> belong into this function.

Right, it is an issue: d->arch.vintc is being NULLed inside 
domain_vaplic_deinit(), which are called from domain_vintc_deinit(), so 
XVFREE(d->arch.vintc->used_irqs) must be moved to just after the for() 
loop (before the switch).

As for d->arch.vintc itself, it should only be NULLed and not freed, 
since it is a pointer to &vaplic->vintc which is embedded in the vaplic 
struct and not separately allocated.

> 
>> --- a/xen/arch/riscv/irq.c
>> +++ b/xen/arch/riscv/irq.c
>> @@ -12,11 +12,20 @@
>>   #include <xen/errno.h>
>>   #include <xen/init.h>
>>   #include <xen/irq.h>
>> +#include <xen/sched.h>
>>   #include <xen/spinlock.h>
>> +#include <xen/xvmalloc.h>
>>   
>>   #include <asm/hardirq.h>
>>   #include <asm/intc.h>
>>   
>> +/* Describe an IRQ assigned to a guest */
>> +struct irq_guest
>> +{
>> +    struct domain *d;
>> +    unsigned int virq;
>> +};
>> +
>>   static irq_desc_t irq_desc[NR_IRQS];
>>   
>>   static bool irq_validate_new_type(unsigned int curr, unsigned int new)
>> @@ -192,6 +201,15 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq)
>>       if ( desc->handler->ack )
>>           desc->handler->ack(desc);
>>   
>> +    if ( desc->status & IRQ_GUEST )
>> +        /*
>> +         * As at the moment APLIC + IMSIC is used for guest interrupts will
>> +         * be directly passed to guest. But if/when IMSIC won't be available
>> +         * all interrupts will go through Xenand here an irq injection
>> +         * will be necessary to do.
>> +         */
>> +        panic("unimplemented");
> 
> The first comment sentence doesn't parse for me. In the 2nd there's a blank
> missing between "Xen" and "and".

I will rephrase it for clarity:

/*
  * With APLIC + IMSIC, guest interrupts bypass Xen and are delivered
  * directly to the guest. Without IMSIC, interrupts would be trapped
  * by Xen and would need injecting into the guest here.
  */

It looks more clearer to me.


> 
>> @@ -221,3 +239,215 @@ void do_IRQ(struct cpu_user_regs *regs, unsigned int irq)
>>       spin_unlock(&desc->lock);
>>       irq_exit();
>>   }
>> +
>> +static inline struct irq_guest *irq_get_guest_info(struct irq_desc *desc)
>> +{
>> +    ASSERT(spin_is_locked(&desc->lock));
>> +    ASSERT(test_bit(_IRQ_GUEST, &desc->status));
>> +    ASSERT(desc->action != NULL);
>> +
>> +    return desc->action->dev_id;
>> +}
>> +
>> +static inline struct domain *irq_get_domain(struct irq_desc *desc)
>> +{
>> +    return irq_get_guest_info(desc)->d;
>> +}
> 
> Does this really need a separate helper? (You effectively open-code it
> anyway in release_guest_irq().)

Considering that I used irq_get_domain() once at all (even in 
downstream) I will drop that.

> 
> For both functions: "inline" generally wants limiting to header files.

Regarding inline I think that I don't understand, it isn't in the header 
file as irq_get_guest_info() is used only in this file.

Do I understand you correctly and it is needed just to drop "inline" for
irq_get_guest_info()?

> 
>> +int release_guest_irq(struct domain *d, unsigned int virq)
>> +{
>> +    struct irq_desc *desc = irq_to_desc(virq);
>> +    struct irq_guest *info;
>> +    unsigned long flags;
>> +
>> +    spin_lock_irqsave(&desc->lock, flags);
>> +
>> +    if ( !test_bit(_IRQ_GUEST, &desc->status) )
>> +        goto unlock_err;
>> +
>> +    info = irq_get_guest_info(desc);
>> +    if ( d != info->d )
>> +        goto unlock_err;
>> +
>> +    /*
>> +     * Live IRQ unrouting from a running domain is not supported: the tear-down
>> +     * drops desc->lock across release_irq()/xvfree() and relies on no
>> +     * concurrent route_irq_to_guest() being issued for this domain. Only permit
>> +     * it for a dying domain, where assignment is frozen and no new routes can
>> +     * appear.
>> +     */
>> +    if ( !d->is_dying )
>> +    {
>> +        spin_unlock_irqrestore(&desc->lock, flags);
>> +        return -EBUSY;
>> +    }
> 
> Yet route_irq_to_guest() looks to happily act on dying guests. IOW assignment
> doesn't look to be frozen, despite the comment saying so.

Right, it is needed to add:
     if ( d->is_dying )
         return -EINVAL;
at the top of route_irq_to_guest().


> 
>> +    /*
>> +     * Clear _IRQ_GUEST while still holding the lock so that a concurrent
>> +     * release_guest_irq() for the same IRQ observes it and bails out, rather
>> +     * than capturing the same 'info' and double-freeing it below.
>> +     */
>> +    clear_bit(_IRQ_GUEST, &desc->status);
> 
> You use __set_bit() / __clear_bit() elsewhere - why not here?

As it is under spinlock it could __clear_bit here.

> 
>> +    spin_unlock_irqrestore(&desc->lock, flags);
>> +
>> +    release_irq(desc->irq, info);
>> +    xvfree(info);
> 
> If, in release_irq(), action isn't freed, it's ->dev_id field will now have
> a dangling pointer. (I think I did point this out before.)

It should freed in release_irq() as route_irq_to_guest() always set 
action->free_on_release = true;

Thanks.

~ Oleksii


  reply	other threads:[~2026-07-01 14:50 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26 15:46 [PATCH v4 00/25] Introduce enablemenant of dom0less Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 01/25] xen/dom0less: turn max_init_domid into a common variable Oleksii Kurochko
2026-06-29 14:34   ` Jan Beulich
2026-06-26 15:46 ` [PATCH v4 02/25] xen: arm: move declaration of map_device_irqs_to_domain() to common header Oleksii Kurochko
2026-06-29  7:10   ` Orzel, Michal
2026-06-26 15:46 ` [PATCH v4 03/25] xen: arm: update p2m_set_allocation() prototype Oleksii Kurochko
2026-06-29  7:14   ` Orzel, Michal
2026-06-26 15:46 ` [PATCH v4 04/25] xen/Kconfig: introduce HAS_STATIC_MEMORY Oleksii Kurochko
2026-06-29  7:05   ` Jan Beulich
2026-06-29  7:24   ` Orzel, Michal
2026-06-26 15:46 ` [PATCH v4 05/25] xen/riscv: Implement ARCH_PAGING_MEMPOOL Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 06/25] xen/riscv: Implement construct_domain() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 07/25] xen/riscv: implement prerequisites for domain_create() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 08/25] xen/riscv: introduce guest riscv,isa string Oleksii Kurochko
2026-06-29 14:46   ` Jan Beulich
2026-06-30 16:06     ` Oleksii Kurochko
2026-07-01  6:22       ` Jan Beulich
2026-07-01  7:44         ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 09/25] xen/riscv: implement make_cpus_node() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 10/25] xen/riscv: implement make_timer_node() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 11/25] xen/riscv: implement make_arch_nodes() Oleksii Kurochko
2026-06-29  7:07   ` Jan Beulich
2026-06-26 15:46 ` [PATCH v4 12/25] xen/riscv: introduce init interrupt controller operations Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 13/25] xen/riscv: implement make_intc_domU_node() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 14/25] xen/riscv: introduce aia_init() and aia_usable() Oleksii Kurochko
2026-06-29  9:54   ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 15/25] xen/riscv: introduce per-vCPU IMSIC state Oleksii Kurochko
2026-06-29 14:54   ` Jan Beulich
2026-06-26 15:46 ` [PATCH v4 16/25] xen/riscv: introduce minimal virtual APLIC (vAPLIC) infrastructure Oleksii Kurochko
2026-06-29 15:02   ` Jan Beulich
2026-07-01 10:24     ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 17/25] xen/riscv: rename enum intc_version to intc_variant Oleksii Kurochko
2026-06-29 15:04   ` Jan Beulich
2026-07-01 10:32     ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 18/25] xen/riscv: introduce (de)initialization helpers for vINTC Oleksii Kurochko
2026-06-29 15:07   ` Jan Beulich
2026-07-01 10:52     ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 19/25] xen/riscv: generate IMSIC DT node for guest domains Oleksii Kurochko
2026-06-29 15:19   ` Jan Beulich
2026-07-01 11:21     ` Oleksii Kurochko
2026-07-01 11:55       ` Jan Beulich
2026-07-01 12:04         ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 20/25] xen/riscv: create APLIC " Oleksii Kurochko
2026-06-29 15:26   ` Jan Beulich
2026-07-01 11:55     ` Oleksii Kurochko
2026-07-01 11:58       ` Jan Beulich
2026-06-26 15:46 ` [PATCH v4 21/25] xen/riscv: implement IRQ routing for device passthrough Oleksii Kurochko
2026-06-29 15:55   ` Jan Beulich
2026-07-01 14:49     ` Oleksii Kurochko [this message]
2026-07-02  6:38       ` Jan Beulich
2026-07-02  9:33         ` Oleksii Kurochko
2026-07-02 14:32           ` Jan Beulich
2026-07-02 16:04             ` Oleksii Kurochko
2026-07-03  7:21               ` Jan Beulich
2026-07-03 10:34                 ` Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 22/25] xen/riscv: implement init_intc_phandle() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 23/25] xen/riscv: initialize RCU, scheduler, and system domains in start_xen() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 24/25] xen/riscv: provide init_vuart() Oleksii Kurochko
2026-06-26 15:46 ` [PATCH v4 25/25] xen/riscv: add initial dom0less infrastructure support Oleksii Kurochko
2026-06-30  7:28   ` Jan Beulich
2026-07-01 15:24     ` Oleksii Kurochko
2026-07-02  6:41       ` Jan Beulich
2026-07-02  9:48         ` Oleksii Kurochko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6aafbeb5-2f33-49f4-9d14-937b0bc4d15d@gmail.com \
    --to=oleksii.kurochko@gmail.com \
    --cc=Romain.Caritey@microchip.com \
    --cc=alistair.francis@wdc.com \
    --cc=baptiste.le-duc@vates.tech \
    --cc=connojdavis@gmail.com \
    --cc=dpsmith@apertussolutions.com \
    --cc=jbeulich@suse.com \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.