[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.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |