|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [XEN][PATCH v10 16/20] xen/arm: Implement device tree node removal functionalities
On Fri, 25 Aug 2023, Vikram Garhwal wrote:
> Introduce sysctl XEN_SYSCTL_dt_overlay to remove device-tree nodes added using
> device tree overlay.
>
> xl dt-overlay remove file.dtbo:
> Removes all the nodes in a given dtbo.
> First, removes IRQ permissions and MMIO accesses. Next, it finds the nodes
> in dt_host and delete the device node entries from dt_host.
>
> The nodes get removed only if it is not used by any of dom0 or domio.
>
> Also, added overlay_track struct to keep the track of added node through
> device
> tree overlay. overlay_track has dt_host_new which is unflattened form of
> updated
> fdt and name of overlay nodes. When a node is removed, we also free the memory
> used by overlay_track for the particular overlay node.
>
> Nested overlay removal is supported in sequential manner only i.e. if
> overlay_child nests under overlay_parent, it is assumed that user first
> removes
> overlay_child and then removes overlay_parent.
> Also, this is an experimental feature so it is expected from user to make sure
> correct device tree overlays are used when adding nodes and making sure
> devices
> are not being used by other domain before removing them from Xen tree.
> Partially added/removed i.e. failures while removing the overlay may cause
> other
> failures and might need a system reboot.
>
> Signed-off-by: Vikram Garhwal <vikram.garhwal@xxxxxxx>
>
> ---
> Changes from v9:
> Remove iommu and IRQ routing as this will not be done while adding the
> nodes.
I understand about IRQ routing. But I am confused by the removal of the
call to iommu_remove_dt_device.
handle_device (called by add_nodes) calls iommu_add_dt_device when
adding the device for dom0 (this is the "own_device" case)
So here we need to remove it from the iommu? That seems like a mistake?
> Changes from v8:
> Remove IRQs and IOMMU entries using rangesets instead of parsing each
> node.
> Changes from v7:
> Add dt-overlay.c in MAINTAINERS.
> Add comments for dt_overlay_remove_node.
> Rename handle_remove_irq_iommu() to remove_resources().
> Add comment regarding false mapping flag for reason behind not removing
> the
> mapping..
> Remove irq_access_premitted() check.
> Add error handling for remove_all_descendant_nodes
> Change read_lock with write_lock.
> Remove check_overlay_fdt() call from handle_remove_overlay_nodes().
> Re-organize dt_sysctl and reutnr -EOPNOSTSUPP for error cases. Also,
> renamed
> this function to dt_overlay_sysctl.
> Remove unnecessary header includes in dt-overlay.h
> Correct indentation and make func tion inputs const wherever possible.
> Make overlay_fdt const_void inside xen_sysctl_dt_overlay{}.
> Add comment regarding why we not removing IRQ and MMIO mappings.
> Move overlay_node_count() out of this patch as it's not being used here
> anymore.
> Changes from v6:
> Add explicit padding for xen_system_dt_overlay{}
> Update license.
> Rearrange xfree in dt_sysctl()
> Update overlay_track struct comment with relevant message.
> Fix missing xen/errno.h for builds without CONFIG_OVERLAY_DTB cases.
> Fix header formatting.
> ---
> ---
> MAINTAINERS | 1 +
> xen/arch/arm/sysctl.c | 16 +-
> xen/common/Makefile | 1 +
> xen/common/dt-overlay.c | 392 +++++++++++++++++++++++++++++++++++
> xen/include/public/sysctl.h | 24 +++
> xen/include/xen/dt-overlay.h | 63 ++++++
> 6 files changed, 496 insertions(+), 1 deletion(-)
> create mode 100644 xen/common/dt-overlay.c
> create mode 100644 xen/include/xen/dt-overlay.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a0805d35cd..c41a7c5440 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -301,6 +301,7 @@ M: Julien Grall <julien@xxxxxxx>
> S: Supported
> F: xen/common/libfdt/
> F: xen/common/device_tree.c
> +F: xen/common/dt-overlay.c
> F: xen/include/xen/libfdt/
> F: xen/include/xen/device_tree.h
> F: xen/drivers/passthrough/device_tree.c
> diff --git a/xen/arch/arm/sysctl.c b/xen/arch/arm/sysctl.c
> index e9a0661146..5cda0dc674 100644
> --- a/xen/arch/arm/sysctl.c
> +++ b/xen/arch/arm/sysctl.c
> @@ -9,6 +9,7 @@
>
> #include <xen/types.h>
> #include <xen/lib.h>
> +#include <xen/dt-overlay.h>
> #include <xen/errno.h>
> #include <xen/hypercall.h>
> #include <asm/arm64/sve.h>
> @@ -25,7 +26,20 @@ void arch_do_physinfo(struct xen_sysctl_physinfo *pi)
> long arch_do_sysctl(struct xen_sysctl *sysctl,
> XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl)
> {
> - return -ENOSYS;
> + long ret;
> +
> + switch ( sysctl->cmd )
> + {
> + case XEN_SYSCTL_dt_overlay:
> + ret = dt_overlay_sysctl(&sysctl->u.dt_overlay);
> + break;
> +
> + default:
> + ret = -ENOSYS;
> + break;
> + }
> +
> + return ret;
> }
>
> /*
> diff --git a/xen/common/Makefile b/xen/common/Makefile
> index 46049eac35..e7e96b1087 100644
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_DEBUG_TRACE) += debugtrace.o
> obj-$(CONFIG_HAS_DEVICE_TREE) += device_tree.o
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.o
> +obj-$(CONFIG_OVERLAY_DTB) += dt-overlay.o
> obj-y += event_2l.o
> obj-y += event_channel.o
> obj-y += event_fifo.o
> diff --git a/xen/common/dt-overlay.c b/xen/common/dt-overlay.c
> new file mode 100644
> index 0000000000..12a3029fee
> --- /dev/null
> +++ b/xen/common/dt-overlay.c
> @@ -0,0 +1,392 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * xen/common/dt-overlay.c
> + *
> + * Device tree overlay support in Xen.
> + *
> + * Copyright (C) 2023, Advanced Micro Devices, Inc. All Rights Reserved.
> + * Written by Vikram Garhwal <vikram.garhwal@xxxxxxx>
> + *
> + */
> +#include <asm/domain_build.h>
> +#include <xen/dt-overlay.h>
> +#include <xen/guest_access.h>
> +#include <xen/iocap.h>
> +#include <xen/libfdt/libfdt.h>
> +#include <xen/xmalloc.h>
> +
> +static LIST_HEAD(overlay_tracker);
> +static DEFINE_SPINLOCK(overlay_lock);
> +
> +/* Find last descendants of the device_node. */
> +static struct dt_device_node *
> +find_last_descendants_node(const struct dt_device_node *device_node)
> +{
> + struct dt_device_node *child_node;
> +
> + for ( child_node = device_node->child; child_node->sibling != NULL;
> + child_node = child_node->sibling );
> +
> + /* If last child_node also have children. */
> + if ( child_node->child )
> + child_node = find_last_descendants_node(child_node);
> +
> + return child_node;
> +}
> +
> +static int dt_overlay_remove_node(struct dt_device_node *device_node)
> +{
> + struct dt_device_node *np;
> + struct dt_device_node *parent_node;
> + struct dt_device_node *last_descendant = device_node->child;
> +
> + parent_node = device_node->parent;
> +
> + /* Check if we are trying to remove "/" i.e. root node. */
> + if ( parent_node == NULL )
> + {
> + dt_dprintk("%s's parent node not found\n", device_node->name);
> + return -EFAULT;
> + }
> +
> + /* Sanity check for linking between parent and child node. */
> + np = parent_node->child;
> + if ( np == NULL )
> + {
> + dt_dprintk("parent node %s's not found\n", parent_node->name);
> + return -EFAULT;
> + }
> +
> + /* If node to be removed is only child node or first child. */
> + if ( !dt_node_cmp(np->full_name, device_node->full_name) )
> + {
> + parent_node->child = np->sibling;
> +
> + /*
> + * Iterate over all child nodes of device_node. Given that we are
> + * removing a node, we need to remove all it's descendants too.
> + * Reason behind finding last_descendant:
> + * If device_node has multiple children, device_node->allnext will
> point
> + * to first_child and first_child->allnext will be a sibling. When
> the
> + * device_node and it's all children are removed,
> parent_node->allnext
> + * should point to node next to last children.
> + */
> + if ( last_descendant )
> + {
> + last_descendant = find_last_descendants_node(device_node);
> + parent_node->allnext = last_descendant->allnext;
> + }
> + else
> + parent_node->allnext = np->allnext;
> +
> + return 0;
> + }
> +
> + for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
> + {
> + if ( !dt_node_cmp(np->sibling->full_name, device_node->full_name) )
> + {
> + /* Found the node. Now we remove it. */
> + np->sibling = np->sibling->sibling;
> +
> + if ( np->child )
> + np = find_last_descendants_node(np);
> +
> + /*
> + * Iterate over all child nodes of device_node. Given that we are
> + * removing parent node, we need to remove all it's descendants
> too.
> + */
> + if ( last_descendant )
> + last_descendant = find_last_descendants_node(device_node);
> +
> + if ( last_descendant )
> + np->allnext = last_descendant->allnext;
> + else
> + np->allnext = np->allnext->allnext;
> +
> + break;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/* Basic sanity check for the dtbo tool stack provided to Xen. */
> +static int check_overlay_fdt(const void *overlay_fdt, uint32_t
> overlay_fdt_size)
> +{
> + if ( (fdt_totalsize(overlay_fdt) != overlay_fdt_size) ||
> + fdt_check_header(overlay_fdt) )
> + {
> + printk(XENLOG_ERR "The overlay FDT is not a valid Flat Device
> Tree\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int irq_remove_cb(unsigned long s, unsigned long e, void *dom,
> + unsigned long *c)
> +{
> + int rc;
> + struct domain *d = dom;
> +
> + /*
> + * TODO: We don't handle shared IRQs for now. So, it is assumed that
> + * the IRQs was not shared with another devices.
> + * TODO: Undo the IRQ routing.
> + */
> + rc = irq_deny_access(d, s);
> + if ( rc )
> + {
> + printk(XENLOG_ERR "unable to revoke access for irq %lu\n", s);
> + }
> + else
> + *c += e - s + 1;
> +
> + return rc;
> +
> +}
> +
> +static int iomem_remove_cb(unsigned long s, unsigned long e, void *dom,
> + unsigned long *c)
> +{
> + int rc;
> + struct domain *d = dom;
> +
> + /*
> + * Remove mmio access.
> + * TODO: Support for remove/add the mapping in P2M.
> + */
> + rc = iomem_deny_access(d, s, e);
> + if ( rc )
> + {
> + printk(XENLOG_ERR "Unable to remove dom%d access to"
> + " 0x%"PRIx64" - 0x%"PRIx64"\n",
> + d->domain_id,
> + s & PAGE_MASK, PAGE_ALIGN(e) - 1);
> + }
> + else
> + *c += e - s + 1;
> +
> + return rc;
> +}
> +
> +/* Check if node itself can be removed. */
> +static bool check_node_removable(struct dt_device_node *device_node)
> +{
> + domid_t domid;
> +
> + domid = dt_device_used_by(device_node);
> +
> + 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 false;
> + }
> +
> +
> + return true;
> +}
> +
> +/* Check if all descendants of the given node are removable. */
> +static bool
> +check_descendant_nodes_removable(const struct dt_device_node *device_node)
> +{
> + bool rc = true;
> + struct dt_device_node *child_node;
> +
> + for ( child_node = device_node->child; child_node != NULL;
> + child_node = child_node->sibling )
> + {
> + if ( child_node->child )
> + {
> + rc = check_descendant_nodes_removable(child_node);
> + if ( !rc )
> + return rc;
> + }
> +
> + rc = check_node_removable(child_node);
> + if ( !rc )
> + return rc;
> + }
> +
> + return rc;
> +}
> +
> +/* Remove nodes from dt_host. */
> +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;
> +
> + for ( j = 0; j < tracker->num_nodes; j++ )
> + {
> + overlay_node = (struct dt_device_node *)tracker->nodes_address[j];
> + if ( overlay_node == NULL )
> + {
> + printk(XENLOG_ERR "Device %s is not present in the tree.
> Removing nodes failed\n",
> + overlay_node->full_name);
> + return -EINVAL;
> + }
> +
> + if ( !check_descendant_nodes_removable(overlay_node) )
> + return -EINVAL;
> +
> + if ( !check_node_removable(overlay_node) )
> + return -EINVAL;
> +
> + dt_dprintk("Removing node: %s\n", overlay_node->full_name);
> +
> + write_lock(&dt_host_lock);
> +
> + rc = dt_overlay_remove_node(overlay_node);
> + if ( rc )
> + {
> + write_unlock(&dt_host_lock);
> + return rc;
> + }
> +
> + 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;
> +}
> +
> +/*
> + * First finds the device node to remove. Check if the device is being used
> by
> + * any dom and finally remove it from dt_host. IOMMU is already being taken
> care
> + * while destroying the domain.
> + */
> +static long handle_remove_overlay_nodes(const void *overlay_fdt,
> + uint32_t overlay_fdt_size)
> +{
> + int rc;
> + struct overlay_track *entry, *temp, *track;
> + bool found_entry = false;
> +
> + rc = check_overlay_fdt(overlay_fdt, overlay_fdt_size);
> + if ( rc )
> + return rc;
> +
> + spin_lock(&overlay_lock);
> +
> + /*
> + * First check if dtbo is correct i.e. it should one of the dtbo which
> was
> + * used when dynamically adding the node.
> + * Limitation: Cases with same node names but different property are not
> + * supported currently. We are relying on user to provide the same dtbo
> + * as it was used when adding the nodes.
> + */
> + list_for_each_entry_safe( entry, temp, &overlay_tracker, entry )
> + {
> + if ( memcmp(entry->overlay_fdt, overlay_fdt, overlay_fdt_size) == 0 )
> + {
> + track = entry;
> + found_entry = true;
> + break;
> + }
> + }
> +
> + if ( !found_entry )
> + {
> + rc = -EINVAL;
> +
> + printk(XENLOG_ERR "Cannot find any matching tracker with input dtbo."
> + " Removing nodes is supported only for prior added dtbo.\n");
> + goto out;
> +
> + }
> +
> + rc = remove_nodes(entry);
> + if ( rc )
> + {
> + printk(XENLOG_ERR "Removing node failed\n");
> + goto out;
> + }
> +
> + list_del(&entry->entry);
> +
> + xfree(entry->dt_host_new);
> + xfree(entry->fdt);
> + xfree(entry->overlay_fdt);
> +
> + xfree(entry->nodes_address);
> +
> + rangeset_destroy(entry->irq_ranges);
> + rangeset_destroy(entry->iomem_ranges);
> +
> + xfree(entry);
> +
> + out:
> + spin_unlock(&overlay_lock);
> + return rc;
> +}
> +
> +long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op)
> +{
> + long ret;
> + void *overlay_fdt;
> +
> + if ( op->overlay_op != XEN_SYSCTL_DT_OVERLAY_ADD &&
> + op->overlay_op != XEN_SYSCTL_DT_OVERLAY_REMOVE )
> + return -EOPNOTSUPP;
> +
> + if ( op->overlay_fdt_size == 0 || op->overlay_fdt_size > KB(500) )
> + return -EINVAL;
> +
> + if ( op->pad[0] || op->pad[1] || op->pad[2] )
> + return -EINVAL;
> +
> + overlay_fdt = xmalloc_bytes(op->overlay_fdt_size);
> +
> + if ( overlay_fdt == NULL )
> + return -ENOMEM;
> +
> + ret = copy_from_guest(overlay_fdt, op->overlay_fdt,
> op->overlay_fdt_size);
> + if ( ret )
> + {
> + gprintk(XENLOG_ERR, "copy from guest failed\n");
> + xfree(overlay_fdt);
> +
> + return -EFAULT;
> + }
> +
> + if ( op->overlay_op == XEN_SYSCTL_DT_OVERLAY_REMOVE )
> + ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size);
> +
> + xfree(overlay_fdt);
> +
> + return ret;
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
> index fa7147de47..900239133a 100644
> --- a/xen/include/public/sysctl.h
> +++ b/xen/include/public/sysctl.h
> @@ -1059,6 +1059,25 @@ typedef struct xen_sysctl_cpu_policy
> xen_sysctl_cpu_policy_t;
> DEFINE_XEN_GUEST_HANDLE(xen_sysctl_cpu_policy_t);
> #endif
>
> +#if defined(__arm__) || defined (__aarch64__)
> +/*
> + * XEN_SYSCTL_dt_overlay
> + * Performs addition/removal of device tree nodes under parent node using
> dtbo.
> + * This does in three steps:
> + * - Adds/Removes the nodes from dt_host.
> + * - Adds/Removes IRQ permission for the nodes.
> + * - Adds/Removes MMIO accesses.
> + */
> +struct xen_sysctl_dt_overlay {
> + XEN_GUEST_HANDLE_64(const_void) overlay_fdt; /* IN: overlay fdt. */
> + uint32_t overlay_fdt_size; /* IN: Overlay dtb size. */
> +#define XEN_SYSCTL_DT_OVERLAY_ADD 1
> +#define XEN_SYSCTL_DT_OVERLAY_REMOVE 2
> + uint8_t overlay_op; /* IN: Add or remove. */
> + uint8_t pad[3]; /* IN: Must be zero. */
> +};
> +#endif
> +
> struct xen_sysctl {
> uint32_t cmd;
> #define XEN_SYSCTL_readconsole 1
> @@ -1089,6 +1108,7 @@ struct xen_sysctl {
> #define XEN_SYSCTL_livepatch_op 27
> /* #define XEN_SYSCTL_set_parameter 28 */
> #define XEN_SYSCTL_get_cpu_policy 29
> +#define XEN_SYSCTL_dt_overlay 30
> uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
> union {
> struct xen_sysctl_readconsole readconsole;
> @@ -1119,6 +1139,10 @@ struct xen_sysctl {
> #if defined(__i386__) || defined(__x86_64__)
> struct xen_sysctl_cpu_policy cpu_policy;
> #endif
> +
> +#if defined(__arm__) || defined (__aarch64__)
> + struct xen_sysctl_dt_overlay dt_overlay;
> +#endif
> uint8_t pad[128];
> } u;
> };
> diff --git a/xen/include/xen/dt-overlay.h b/xen/include/xen/dt-overlay.h
> new file mode 100644
> index 0000000000..c0567741ee
> --- /dev/null
> +++ b/xen/include/xen/dt-overlay.h
> @@ -0,0 +1,63 @@
> + /* SPDX-License-Identifier: GPL-2.0-only */
> + /*
> + * xen/dt-overlay.h
> + *
> + * Device tree overlay support in Xen.
> + *
> + * Copyright (C) 2023, Advanced Micro Devices, Inc. All Rights Reserved.
> + * Written by Vikram Garhwal <vikram.garhwal@xxxxxxx>
> + *
> + */
> +#ifndef __XEN_DT_OVERLAY_H__
> +#define __XEN_DT_OVERLAY_H__
> +
> +#include <xen/device_tree.h>
> +#include <xen/list.h>
> +#include <xen/rangeset.h>
> +
> +/*
> + * overlay_track describes information about added nodes through dtbo.
> + * @entry: List pointer.
> + * @dt_host_new: Pointer to the updated dt_host_new which is unflattened from
> + the 'updated fdt'.
> + * @fdt: Stores the fdt.
> + * @overlay_fdt: Stores a copy of input overlay_fdt.
> + * @nodes_address: Stores each overlay_node's address.
> + * @num_nodes: Total number of nodes in overlay dtb.
> + * @iomem_ranges: Range set to keep track of all IOMEMs.
> + * @irq_ranges: Range set to keep track of all added IRQs.
> + */
> +struct overlay_track {
> + struct list_head entry;
> + struct dt_device_node *dt_host_new;
> + void *fdt;
> + void *overlay_fdt;
> + unsigned long *nodes_address;
> + unsigned int num_nodes;
> + struct rangeset *iomem_ranges;
> + struct rangeset *irq_ranges;
> +};
> +
> +struct xen_sysctl_dt_overlay;
> +
> +#ifdef CONFIG_OVERLAY_DTB
> +long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op);
> +#else
> +#include <xen/errno.h>
> +static inline long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op)
> +{
> + return -EOPNOTSUPP;
> +}
> +#endif
> +
> +#endif /* __XEN_DT_OVERLAY_H__ */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> --
> 2.17.1
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |