[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [XEN][RFC PATCH v2 08/12] xen/arm: Implement device tree node removal functionalities



On Wed, 22 Dec 2021, Julien Grall wrote:
> Hi,
> 
> On 09/11/2021 08:02, Vikram Garhwal wrote:
> > Introduce sysctl XEN_SYSCTL_overlay to remove device-tree nodes added using
> 
> I agree with Jan about the name being too generic. I will comment on this a
> bit further down.
> 
> > device tree overlay.
> > 
> > xl 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 domus.
> > If
> >      even one of the node in dtbo is not available for removal i.e. either
> > not
> >      there in dt_host or currently used by any domain, in that case we don't
> >      remove any node in the given dtbo.
> > 
> > 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 node. When a node is removed, we also free the
> > memory
> > used by overlay_track for the particular overlay node.
> > 
> > Signed-off-by: Vikram Garhwal <fnu.vikram@xxxxxxxxxx>
> > ---
> >   xen/common/device_tree.c      |  53 ++++++
> >   xen/common/sysctl.c           | 372
> > ++++++++++++++++++++++++++++++++++++++++++
> >   xen/include/public/sysctl.h   |  23 +++
> >   xen/include/xen/device_tree.h |   4 +
> >   4 files changed, 452 insertions(+)
> > 
> > diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
> > index 26d2e28..19320e1 100644
> > --- a/xen/common/device_tree.c
> > +++ b/xen/common/device_tree.c
> > @@ -385,6 +385,59 @@ void dt_print_node_names(struct dt_device_node *dt)
> >       return;
> >   }
> >   +#if defined (CONFIG_OVERLAY_DTB)
> > +int overlay_remove_node(struct dt_device_node *device_node)
> 
> This want to be prefixed with dt_* so it is clear which components it is
> touching. But I think all the DT overlay code (including sysctl) should be
> moved in a new file (maybe dt_overlay.c) to spreading the overlay code and
> growing
> the size of sysctl.c.
> 
> > +{
> > +    struct dt_device_node *np;
> > +    struct dt_device_node *parent_node;
> > +    struct dt_device_node *current_node;
> > +
> > +    parent_node = device_node->parent;
> > +
> > +    current_node = parent_node;
> > +
> > +    if ( parent_node == NULL )
> > +    {
> > +        dt_dprintk("%s's parent node not found\n", device_node->name);
> > +        return -EFAULT;
> > +    }
> > +
> > +    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 ( np->name == device_node->name )
> 
> Why are you checking the equality between the fields name rather than the node
> itself?
> 
> If it is intended, then I think this wants to be explained because often
> people wants to check the names are equal (e.g str*cmp()) rather than the
> pointer where the names are stored.
> 
> > +    {
> > +        current_node->allnext = np->next;
> > +        return 0;
> > +    }
> > +
> > +    for ( np = parent_node->child; np->sibling != NULL; np = np->sibling )
> > +    {
> > +        current_node = np;
> > +        if ( np->sibling->name == device_node->name )
> 
> Same question.
> 
> > +        {
> > +            /* Found the node. Now we remove it. */
> > +            current_node->allnext = np->allnext->allnext;
> > +
> > +            if ( np->sibling->sibling )
> > +                current_node->sibling = np->sibling->sibling;
> > +            else
> > +                current_node->sibling = NULL;
> > +
> > +            break;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +#endif
> > +
> >   int dt_find_node_by_gpath(XEN_GUEST_HANDLE(char) u_path, uint32_t u_plen,
> >                             struct dt_device_node **node)
> >   {
> > diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c
> > index f2dab72..fca47f5 100644
> > --- a/xen/common/sysctl.c
> > +++ b/xen/common/sysctl.c
> > @@ -28,6 +28,311 @@
> >   #include <xen/livepatch.h>
> >   #include <xen/coverage.h>
> >   +#if defined (CONFIG_OVERLAY_DTB)
> 
> This can be shortend to #ifdef CONFIG_<...>.
> 
> > +#include <xen/list.h>
> > +#include <xen/libfdt/libfdt.h>
> > +#include <xen/xmalloc.h>
> > +#include <xen/device_tree.h>
> > +#include <asm/domain_build.h>
> > +#endif
> > +
> > +#if defined (CONFIG_OVERLAY_DTB)
> 
> Same here.
> 
> > +static LIST_HEAD(overlay_tracker);
> > +static DEFINE_SPINLOCK(overlay_lock);
> > +
> > +/*
> > + * overlay_node_track describes information about added nodes through dtbo.
> > + * @dt_host_new: Pointer to the updated dt_host_new unflattened 'updated
> > fdt'.
> > + * @node_fullname: Store the name of nodes.
> > + * @entry: List pointer.
> > + */
> > +struct overlay_track {
> > +    struct list_head entry;
> > +    struct dt_device_node *dt_host_new;
> > +    char **node_fullname;
> > +    uint8_t num_nodes;
> 
> Any reason to restrict to 256 nodes?
> 
> > +};
> > +
> > +/* Basic sanity check for the dtbo tool stack provided to Xen. */
> > +static int check_overlay_fdt(void *overlay_fdt, uint32_t overlay_fdt_size)
> This function doesn't modify overlay_fdt. So I think it should be const if
> fdt_total_size() and fdt_check_header() allows it.
> 
> > +{
> > +    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 overlay_node_count(void *fdto)
> > +{
> > +    int num_overlay_nodes = 0;
> 
> The name suggests this should be an unsiged int.
> 
> > +    int fragment;
> > +
> > +    fdt_for_each_subnode(fragment, fdto, 0)
> > +    {
> > +
> > +        int subnode;
> > +        int overlay;
> > +
> > +        overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
> 
> This may return < 0 if __overlay__ is not found. From my understanding,
> fdt_for_each_subnode() would end up to start from 0.
> 
> So I think you want to add a check here.
> 
> > +
> > +        fdt_for_each_subnode(subnode, fdto, overlay)
> > +        {
> > +            num_overlay_nodes++;
> > +        }
> > +    }
> > +
> > +    return num_overlay_nodes;
> > +}
> > +
> > +/*
> > + * overlay_get_node_info will get the all node's full name with path. This
> > is
> > + * useful when checking node for duplication i.e. dtbo tries to add nodes
> > which
> > + * already exists in device tree.
> > + */
> > +static void overlay_get_node_info(void *fdto, char ***node_full_path,
> 
> You will retrieve mutiple nodes. So I think the function wants to be named
> 'overlay_get_nodes_info()'. Same for node_full_path.
> 
> Furthermore, you could drop one * if you return the list of paths. This will
> also allow you to return an error when xmalloc fails (see below)
> 
> Lastly, AFAICT, fdto can be const.
> 
> > +                                  int num_overlay_nodes)
> 
> This coud be unsigned int.
> 
> > +{
> > +    int fragment;
> > +    int node_num = 0;
> 
> This could be unsigned int.
> 
> > +
> > +    *node_full_path = xmalloc_bytes(num_overlay_nodes * sizeof(char *));
> 
> Memory allocation can fail.
> 
> > +
> > +    fdt_for_each_subnode(fragment, fdto, 0)
> > +    {
> > +        int target;
> > +        int overlay;
> > +        int subnode;
> > +        const char *target_path;
> > +
> > +        target = fdt_overlay_get_target(device_tree_flattened, fdto,
> > fragment,
> > +                                    &target_path);
> 
> fdt_overlay_get_target() can fail. Also, the indentation looks strange.
> 
> > +        overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
> 
> fdt_subnode_offset can fail.
> 
> > +
> > +        fdt_for_each_subnode(subnode, fdto, overlay)
> > +        {
> > +            const char *node_name = fdt_get_name(fdto, subnode, NULL);
> 
> AFAIU, fdt_get_name() can return NULL;
> 
> > +            int node_name_len = strlen(node_name);
> 
> fdt_get_name() can already provide the len for you. Can you re-use it?
> 
> 
> 
> > +            int target_path_len = strlen(target_path);
> > +            int node_full_name_len = target_path_len + node_name_len + 2;
> 
> node_name_len, target_path_len, node_full_name_len can be unsigned. Also, I
> would add a comment explain what '2' refers to.
> 
> > +
> > +            (*node_full_path)[node_num] =
> > xmalloc_bytes(node_full_name_len);
> 
> xmalloc_bytes() can fail.
> 
> > +
> > +            memcpy((*node_full_path)[node_num], target_path,
> > target_path_len);
> > +
> > +            (*node_full_path)[node_num][target_path_len] = '/';
> > +
> > +            memcpy((*node_full_path)[node_num] + target_path_len + 1,
> > node_name,
> > +                   node_name_len);
> > +
> > +            (*node_full_path)[node_num][node_full_name_len - 1] = '\0';
> > +
> > +            node_num++;
> > +        }
> > +    }
> > +}
> > +
> > +/*
> > + * Checks if all the devices node listed are present in dt_host and used by
> > any
> > + * domain.
> > + */
> 
> Why do we need to handle all the nodes together? I think the code would end up
> to be simpler if we were handling node by node.
> 
> > +static int check_nodes(char **full_dt_node_path, uint32_t num_nodes)
> > +{
> > +    int rc = 0;
> > +    unsigned int i;
> > +    struct dt_device_node *overlay_node;
> > +    uint32_t ret = 0;
> 
> AFAICT, you are storing a domid here, so this wants to be a domid_t and
> possible renamed to domid.
> 
> > +
> > +    for ( i = 0; i < num_nodes; i++ ) {
> > +        dt_dprintk("Finding node %s in the dt_host\n",
> > full_dt_node_path[i]);
> > +
> > +        overlay_node = dt_find_node_by_path(full_dt_node_path[i]);
> > +
> > +        if ( overlay_node == NULL )
> > +        {
> > +            rc = -EINVAL;
> > +
> > +            printk(XENLOG_G_ERR "Device %s is not present in the tree."
> 
> You seem to use a mix of XENLOG_G_ERR and XENLOG_ERR. However, it is not
> entirely clear some messages are more critical than the other. Could you
> clarify?
> 
> > +                   " Removing nodes failed\n", full_dt_node_path[i]);
> 
> Coding style: We don't split error message even if they are more than 80
> lines. This helps grepping them in the log.
> 
> > +            return rc;
> > +        }
> > +
> > +        ret = dt_device_used_by(overlay_node);
> > +
> > +        dt_dprintk("Checking if node %s is used by any domain\n",
> > +                   full_dt_node_path[i]);
> > +
> > +        if ( ret != 0 && ret != DOMID_IO )
> 
> In the commit message you wrote:
> 
> "The nodes get removed only if it is not used by any of dom0 or domus"
> 
> This implies that this should return -EINVAL for domid as well. Can you make
> sure the two matches? (I am not sure which one you want).
> 
> Also, what does prevent the device to not be assigned for the check?
> 
> > +        {
> > +            rc = -EINVAL;
> 
> NIT: This is a bit pointless to set rc when it is just used 2 lines below in
> the return. The alternative is to replace the return with a break.
> 
> > +
> > +            printk(XENLOG_G_ERR "Device %s as it is being used by domain
> > %d."
> > +                   " Removing nodes failed\n", full_dt_node_path[i], ret);
> 
> Coding style: Same about the error message.
> 
> > +            return rc;
> > +        }
> > +    }
> > +
> > +    return rc;
> > +}
> > +
> > +/* Remove nodes from dt_host. */
> > +static int remove_nodes(char **full_dt_node_path, uint32_t num_nodes)
> 
> In the commit message, you said you wanted to remove all the nodes together.
> But this function could possibly fail leaving some nodes present or not.
> 
> As I wrote above, I think handling node by node would be fine. However, we
> need to make sure that the remove operation can be called again to clean-up
> the rest of the nodes.
> 
> > +{
> > +    struct domain *d = hardware_domain;
> > +    int rc = 0;
> > +    struct dt_device_node *overlay_node;
> > +    unsigned int naddr;
> > +    unsigned int i, j, nirq;
> > +    struct dt_raw_irq rirq;
> > +    u64 addr, size;
> > +
> > +    for ( j = 0; j < num_nodes; j++ ) {
> > +        dt_dprintk("Removing node: %s\n", full_dt_node_path[j]);
> > +
> > +        overlay_node = dt_find_node_by_path(full_dt_node_path[j]);
> > +
> > +        nirq = dt_number_of_irq(overlay_node);
> > +
> > +        /* Remove IRQ permission */
> > +        for ( i = 0; i < nirq; i++ )
> > +        {
> > +            rc = dt_device_get_raw_irq(overlay_node, i, &rirq);
> > +            if ( rc )
> > +            {
> > +                printk(XENLOG_ERR "Unable to retrieve irq %u for %s\n",
> > +                       i, dt_node_full_name(overlay_node));
> > +                return rc;
> > +            }
> 
> The interrupt may not be routed to the GIC, in which case we want to skip
> them. So you want to check the controller is equal to dt_interrupt_controller.
> 
> The code is also quite similar to handle_device_interrupts(). So I would
> abstract the code to avoid duplication.
> 
> That said, I find a bit odd to have to parse the overlay again on remove given
> you expect the same to be passed.  I think it might be better to use rangeset
> to keep track of the interrupts added with that specific overlay.
> 
> This will reduce the possibility that remove can go wrong.
> 
> > +
> > +            rc = platform_get_irq(overlay_node, i);
> > +            if ( rc < 0 )
> > +            {
> > +                printk(XENLOG_ERR "Unable to get irq %u for %s\n",
> > +                       i, dt_node_full_name(overlay_node));
> > +                return rc;
> > +            }
> > +
> > +            rc = irq_deny_access(d, rc);
> 
> A few things:
> 
>   1) IRQs can be assigned to another domain without the device being assigned.
> So this want to be checked.
>   2) This is assuming the IRQ was not shared. I am not entirely sure how to
> solve this one. Maybe a TODO and note in the documentation will do for now.
> Stefano?

I think it is fine if we don't handle shared IRQs for now. A note in the
documentation and a TODO is OK.



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.