All of lore.kernel.org
 help / color / mirror / Atom feed
From: Julien Grall <julien@xen.org>
To: Henry Wang <xin.wang2@amd.com>, xen-devel@lists.xenproject.org
Cc: Stefano Stabellini <sstabellini@kernel.org>,
	Andrew Cooper <andrew.cooper3@citrix.com>,
	George Dunlap <george.dunlap@citrix.com>,
	Jan Beulich <jbeulich@suse.com>
Subject: Re: [PATCH v4 7/9] xen/arm: Support device detachment from domains
Date: Thu, 23 May 2024 23:42:16 +0100	[thread overview]
Message-ID: <a9033982-9c7b-4b16-b2c0-e47a3f73e452@xen.org> (raw)
In-Reply-To: <20240523074040.1611264-8-xin.wang2@amd.com>

Hi Henry,

On 23/05/2024 08:40, Henry Wang wrote:
> Similarly as the device attachment from DT overlay to domain, this
> commit implements the device detachment from domain. The DOMCTL
> XEN_DOMCTL_dt_overlay op is extended to have the operation
> XEN_DOMCTL_DT_OVERLAY_DETACH. The detachment of the device is
> implemented by unmapping the IRQ and IOMMU resources. Note that with
> these changes, the device de-registration from the IOMMU driver should
> only happen at the time when the DT overlay is removed from the Xen
> device tree.
> 
> Signed-off-by: Henry Wang <xin.wang2@amd.com>
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
> v4:
> - Split the original patch, only do device detachment from domain.
> ---
>   xen/common/dt-overlay.c     | 243 ++++++++++++++++++++++++++++--------
>   xen/include/public/domctl.h |   3 +-
>   2 files changed, 194 insertions(+), 52 deletions(-)
> 
> diff --git a/xen/common/dt-overlay.c b/xen/common/dt-overlay.c
> index 1087f9b502..693b6e4777 100644
> --- a/xen/common/dt-overlay.c
> +++ b/xen/common/dt-overlay.c
> @@ -392,24 +392,100 @@ find_track_entry_from_tracker(const void *overlay_fdt,
>       return entry;
>   }
>   
> +static int remove_irq(unsigned long s, unsigned long e, void *data)
> +{
> +    struct domain *d = data;
> +    int rc = 0;
> +
> +    /*
> +     * IRQ should always have access unless there are duplication of
> +     * of irqs in device tree. There are few cases of xen device tree
> +     * where there are duplicate interrupts for the same node.
> +     */
> +    if (!irq_access_permitted(d, s))

Because of this check, it means that ...

> +        return 0;
> +    /*
> +     * TODO: We don't handle shared IRQs for now. So, it is assumed that
> +     * the IRQs was not shared with another domain.
> +     */
> +    rc = irq_deny_access(d, s);
> +    if ( rc )
> +    {
> +        printk(XENLOG_ERR "unable to revoke access for irq %ld\n", s);
> +        return rc;
> +    }
> +
> +    rc = release_guest_irq(d, s);

... release_guest_irq() fails on the next retry it will pass. I don't 
think this is what we want.

Instead, we probably want to re-order the call.

> +    if ( rc )
> +    {
> +        printk(XENLOG_ERR "unable to release irq %ld\n", s);
> +        return rc;
> +    }
> +
> +    return rc;
> +}
> +
> +static int remove_all_irqs(struct rangeset *irq_ranges, struct domain *d)
> +{
> +    return rangeset_report_ranges(irq_ranges, 0, ~0UL, remove_irq, d);
> +}
> +
> +static int remove_iomem(unsigned long s, unsigned long e, void *data)
> +{
> +    struct domain *d = data;
> +    int rc = 0;
> +    p2m_type_t t;
> +    mfn_t mfn;
> +
> +    mfn = p2m_lookup(d, _gfn(s), &t);

What are you trying to addres with this check? For instance, the fact 
that the first MFN is mapped, doesn't guarantee the rest is.

> +    if ( mfn_x(mfn) == 0 || mfn_x(mfn) == ~0UL )

I don't understand why we are checking for 0 here. In theory, it is 
valid MFN. Also, the second part wants to be INVALID_MFN.

> +        return -EINVAL;
> +
> +    rc = iomem_deny_access(d, s, e);

iomem_deny_access() works on MFN but here you pass an MFN. Are you 
assuming the GFN == MFN? How would that work for domains that are not 
direct mapped?

> +    if ( rc )
> +    {
> +        printk(XENLOG_ERR "Unable to remove %pd access to %#lx - %#lx\n",
> +               d, s, e);
> +        return rc;
> +    }
> +
> +    rc = unmap_mmio_regions(d, _gfn(s), e - s, _mfn(s));
> +    if ( rc )
> +        return rc;
> +
> +    return rc;
> +}
> +
> +static int remove_all_iomems(struct rangeset *iomem_ranges, struct domain *d)
> +{
> +    return rangeset_report_ranges(iomem_ranges, 0, ~0UL, remove_iomem, d);
> +}
> +
>   /* Check if node itself can be removed and remove node from IOMMU. */
> -static int remove_node_resources(struct dt_device_node *device_node)
> +static int remove_node_resources(struct dt_device_node *device_node,
> +                                 struct domain *d)
>   {
>       int rc = 0;
>       unsigned int len;
>       domid_t domid;
>   
> -    domid = dt_device_used_by(device_node);
> +    if ( !d )

I looked at the code, I am a bit unsure how "d" can be NULL. Do you have 
any pointer?

> +    {
> +        domid = dt_device_used_by(device_node);
>   
> -    dt_dprintk("Checking if node %s is used by any domain\n",
> -               device_node->full_name);
> +        dt_dprintk("Checking if node %s is used by any domain\n",
> +                   device_node->full_name);
>   
> -    /* Remove the node if only it's assigned to hardware domain or domain io. */
> -    if ( domid != hardware_domain->domain_id && domid != DOMID_IO )
> -    {
> -        printk(XENLOG_ERR "Device %s is being used by domain %u. Removing nodes failed\n",
> -               device_node->full_name, domid);
> -        return -EINVAL;
> +        /*
> +         * We also check if device is assigned to DOMID_IO as when a domain
> +         * is destroyed device is assigned to DOMID_IO.
> +         */
> +        if ( domid != DOMID_IO )
> +        {
> +            printk(XENLOG_ERR "Device %s is being assigned to %u. Device is assigned to %d\n",
> +                   device_node->full_name, DOMID_IO, domid);
> +            return -EINVAL;
> +        }
>       }
>   
>       /* Check if iommu property exists. */
> @@ -417,9 +493,12 @@ static int remove_node_resources(struct dt_device_node *device_node)
>       {
>           if ( dt_device_is_protected(device_node) )
>           {
> -            rc = iommu_remove_dt_device(device_node);
> -            if ( rc < 0 )
> -                return rc;
> +            if ( !list_empty(&device_node->domain_list) )
> +            {
> +                rc = iommu_deassign_dt_device(d, device_node);
> +                if ( rc < 0 )
> +                    return rc;
> +            }
>           }
>       }
>   
> @@ -428,7 +507,8 @@ static int remove_node_resources(struct dt_device_node *device_node)
>   
>   /* Remove all descendants from IOMMU. */
>   static int
> -remove_descendant_nodes_resources(const struct dt_device_node *device_node)
> +remove_descendant_nodes_resources(const struct dt_device_node *device_node,
> +                                  struct domain *d)
>   {
>       int rc = 0;
>       struct dt_device_node *child_node;
> @@ -438,12 +518,12 @@ remove_descendant_nodes_resources(const struct dt_device_node *device_node)
>       {
>           if ( child_node->child )
>           {
> -            rc = remove_descendant_nodes_resources(child_node);
> +            rc = remove_descendant_nodes_resources(child_node, d);
>               if ( rc )
>                   return rc;
>           }
>   
> -        rc = remove_node_resources(child_node);
> +        rc = remove_node_resources(child_node, d);
>           if ( rc )
>               return rc;
>       }
> @@ -456,8 +536,7 @@ static int remove_nodes(const struct overlay_track *tracker)
>   {
>       int rc = 0;
>       struct dt_device_node *overlay_node;
> -    unsigned int j;
> -    struct domain *d = hardware_domain;
> +    unsigned int j, len;
>   
>       for ( j = 0; j < tracker->num_nodes; j++ )
>       {
> @@ -467,18 +546,15 @@ static int remove_nodes(const struct overlay_track *tracker)
>   
>           write_lock(&dt_host_lock);
>   
> -        rc = remove_descendant_nodes_resources(overlay_node);
> -        if ( rc )
> +        /* Check if iommu property exists. */
> +        if ( dt_get_property(overlay_node, "iommus", &len) )

Why do we need to check for the property in the "iommus" rather than ...

>           {
> -            write_unlock(&dt_host_lock);
> -            return rc;
> -        }
> -
> -        rc = remove_node_resources(overlay_node);
> -        if ( rc )
> -        {
> -            write_unlock(&dt_host_lock);
> -            return rc;
> +            if ( dt_device_is_protected(overlay_node) )

... relying on dt_device_is_protected()?

> +            {
> +                rc = iommu_remove_dt_device(overlay_node);
> +                if ( rc < 0 )
> +                    return rc;
> +            }
>           }
>   
>           dt_dprintk("Removing node: %s\n", overlay_node->full_name);
> @@ -493,22 +569,6 @@ static int remove_nodes(const struct overlay_track *tracker)
>           write_unlock(&dt_host_lock);
>       }
>   
> -    /* Remove IRQ access. */
> -    if ( tracker->irq_ranges )
> -    {
> -        rc = rangeset_consume_ranges(tracker->irq_ranges, irq_remove_cb, d);
> -        if ( rc )
> -            return rc;
> -    }
> -
> -   /* Remove mmio access. */
> -    if ( tracker->iomem_ranges )
> -    {
> -        rc = rangeset_consume_ranges(tracker->iomem_ranges, iomem_remove_cb, d);
> -        if ( rc )
> -            return rc;
> -    }
> -
>       return rc;
>   }
>   
> @@ -534,7 +594,6 @@ static long handle_remove_overlay_nodes(const void *overlay_fdt,
>       {
>           rc = -EINVAL;
>           goto out;
> -

Valid but unrelated change?

>       }
>   
>       rc = remove_nodes(entry);
> @@ -552,9 +611,6 @@ static long handle_remove_overlay_nodes(const void *overlay_fdt,
>   
>       xfree(entry->nodes_address);
>   
> -    rangeset_destroy(entry->irq_ranges);
> -    rangeset_destroy(entry->iomem_ranges);
> -
>       xfree(entry);
>   
>    out:
> @@ -840,6 +896,88 @@ static long handle_add_overlay_nodes(void *overlay_fdt,
>       return rc;
>   }
>   
> +static long handle_detach_overlay_nodes(struct domain *d,
> +                                        const void *overlay_fdt,
> +                                        uint32_t overlay_fdt_size)
> +{
> +    int rc;
> +    unsigned int j;
> +    struct overlay_track *entry;
> +
> +    rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
> +    if ( rc )
> +        return rc;
> +
> +    spin_lock(&overlay_lock);
> +
> +    entry = find_track_entry_from_tracker(overlay_fdt, overlay_fdt_size);
> +    if ( entry == NULL )
> +    {
> +        rc = -EINVAL;
> +        goto out;
> +    }
> +
> +    for ( j = 0; j < entry->num_nodes; j++ )
> +    {
> +        struct dt_device_node *overlay_node;
> +
> +        overlay_node = (struct dt_device_node *)entry->nodes_address[j];

Can you remind me why nodes_address is storing unsigned long rather 
struct dt_device_node?

> +        if ( overlay_node == NULL )
> +        {
> +            rc = -EINVAL;
> +            goto out;
> +        }
> +
> +        write_lock(&dt_host_lock);
> +        rc = remove_descendant_nodes_resources(overlay_node, d);
> +        if ( rc )
> +        {
> +            write_unlock(&dt_host_lock);
> +            goto out;
> +        }
> +
> +        rc = remove_node_resources(overlay_node, d);
> +        if ( rc )
> +        {
> +            write_unlock(&dt_host_lock);
> +            goto out;
> +        }
> +        write_unlock(&dt_host_lock);
> +
> +        rc = remove_all_irqs(entry->irq_ranges, d);

I am really confused. This is called in the for loop but entry doesn't 
seem to change. So why do we need to call it for every iteration? Also...


> +        if ( rc )
> +            goto out;
> +
> +        rc = remove_all_iomems(entry->iomem_ranges, d);
> +        if ( rc )
> +            goto out;
> +    }
> +
> +    /* Remove IRQ access. */
> +    if ( entry->irq_ranges )
> +    {
> +        rc = rangeset_consume_ranges(entry->irq_ranges, irq_remove_cb, d);

... remove_all_irqs() will revert the permission. So why do we need to 
do it again?

> +        if ( rc )
> +            goto out;
> +    }
> +
> +    /* Remove mmio access. */
> +    if ( entry->iomem_ranges )
> +    {
> +        rc = rangeset_consume_ranges(entry->iomem_ranges, iomem_remove_cb, d);

Same question here.

> +        if ( rc )
> +            goto out;
> +    }
> +
> +    rangeset_destroy(entry->irq_ranges);
> +    rangeset_destroy(entry->iomem_ranges);
> +
> + out:
> +    spin_unlock(&overlay_lock);
> +
> +    return rc;
> +}
> +
>   static long handle_attach_overlay_nodes(struct domain *d,
>                                           const void *overlay_fdt,
>                                           uint32_t overlay_fdt_size)
> @@ -956,7 +1094,8 @@ long dt_overlay_domctl(struct domain *d, struct xen_domctl_dt_overlay *op)
>       long ret;
>       void *overlay_fdt;
>   
> -    if ( op->overlay_op != XEN_DOMCTL_DT_OVERLAY_ATTACH )
> +    if ( op->overlay_op != XEN_DOMCTL_DT_OVERLAY_ATTACH &&
> +         op->overlay_op != XEN_DOMCTL_DT_OVERLAY_DETACH )
>           return -EOPNOTSUPP;
>   
>       if ( op->overlay_fdt_size == 0 || op->overlay_fdt_size > KB(500) )
> @@ -979,7 +1118,9 @@ long dt_overlay_domctl(struct domain *d, struct xen_domctl_dt_overlay *op)
>           return -EFAULT;
>       }
>   
> -    if ( op->overlay_op == XEN_DOMCTL_DT_OVERLAY_ATTACH )
> +    if ( op->overlay_op == XEN_DOMCTL_DT_OVERLAY_DETACH )
> +        ret = handle_detach_overlay_nodes(d, overlay_fdt, op->overlay_fdt_size);
> +    else
>           ret = handle_attach_overlay_nodes(d, overlay_fdt, op->overlay_fdt_size);
>   
>       xfree(overlay_fdt);
> diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
> index ac3c2a7c4c..e91f743fa4 100644
> --- a/xen/include/public/domctl.h
> +++ b/xen/include/public/domctl.h
> @@ -1195,7 +1195,8 @@ struct xen_domctl_dt_overlay {
>       XEN_GUEST_HANDLE_64(const_void) overlay_fdt;  /* IN: overlay fdt. */
>       uint32_t overlay_fdt_size;              /* IN: Overlay dtb size. */
>   #define XEN_DOMCTL_DT_OVERLAY_ATTACH                1
> -    uint8_t overlay_op;                     /* IN: Attach. */
> +#define XEN_DOMCTL_DT_OVERLAY_DETACH                2
> +    uint8_t overlay_op;                     /* IN: Attach/Detach. */

Changing the comment for every operation we add is not really scalable 
:). If you want an explanation, can it be generic?

>       uint8_t pad[3];                         /* IN: Must be zero. */
>   };
>   #endif

Cheers,

-- 
Julien Grall


  reply	other threads:[~2024-05-23 22:42 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-23  7:40 [PATCH v4 0/9] Remaining patches for dynamic node programming using overlay dtbo Henry Wang
2024-05-23  7:40 ` [PATCH v4 1/9] tools/xl: Correct the help information and exit code of the dt-overlay command Henry Wang
2024-05-24  0:55   ` Stefano Stabellini
2024-05-23  7:40 ` [PATCH v4 2/9] xen/arm, doc: Add a DT property to specify IOMMU for Dom0less domUs Henry Wang
2024-05-23 21:23   ` Julien Grall
2024-05-23  7:40 ` [PATCH v4 3/9] tools/arm: Introduce the "nr_spis" xl config entry Henry Wang
2024-05-23 23:05   ` Julien Grall
2024-05-24  2:19     ` Stefano Stabellini
2024-05-23  7:40 ` [PATCH v4 4/9] xen/arm/gic: Allow adding interrupt to running VMs Henry Wang
2024-05-23 21:27   ` Julien Grall
2024-05-23  7:40 ` [PATCH v4 5/9] xen/arm: Add XEN_DOMCTL_dt_overlay and device attachment to domains Henry Wang
2024-05-23 21:55   ` Julien Grall
2024-05-24  2:19     ` Stefano Stabellini
2024-05-23  7:40 ` [PATCH v4 6/9] xen/arm/gic: Allow removing interrupt to running VMs Henry Wang
2024-05-23  7:40 ` [PATCH v4 7/9] xen/arm: Support device detachment from domains Henry Wang
2024-05-23 22:42   ` Julien Grall [this message]
2024-05-23  7:40 ` [PATCH v4 8/9] tools: Introduce the "xl dt-overlay {attach,detach}" commands Henry Wang
2024-05-23 23:11   ` Julien Grall
2024-05-24  2:19     ` Stefano Stabellini
2024-05-23  7:40 ` [PATCH v4 9/9] docs: Add device tree overlay documentation Henry Wang
2024-05-23 22:48   ` Julien Grall
2024-05-24  2:19     ` Stefano Stabellini
2024-05-23 21:29 ` [PATCH v4 0/9] Remaining patches for dynamic node programming using overlay dtbo Julien Grall

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=a9033982-9c7b-4b16-b2c0-e47a3f73e452@xen.org \
    --to=julien@xen.org \
    --cc=andrew.cooper3@citrix.com \
    --cc=george.dunlap@citrix.com \
    --cc=jbeulich@suse.com \
    --cc=sstabellini@kernel.org \
    --cc=xen-devel@lists.xenproject.org \
    --cc=xin.wang2@amd.com \
    /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.