[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 07/15] xen/overlay: Enable device tree overlay assignment to running domains
From: Vikram Garhwal <fnu.vikram@xxxxxxxxxx> Following changes are done to enable dtbo assignment to a running vm with given domain_id: 1. Support for irq map and unmap for running domain. We store the IRQ nums for each overlay and while removing those IRQ nums are removed. 2. Support iommu assign/reassign to running domains. 3. Added "map_nodes" options to support two modes: a. Add the nodes in Xen device tree and map the nodes to given VM b. Just add them in xen device tree without mapping. Change device.c: handle_device() function with input domain_mapping flag. Signed-off-by: Vikram Garhwal <vikram.garhwal@xxxxxxx> Signed-off-by: Michal Orzel <michal.orzel@xxxxxxx> Signed-off-by: Henry Wang <xin.wang2@xxxxxxx> --- xen/arch/arm/device.c | 8 +- xen/arch/arm/domain_build.c | 2 +- xen/arch/arm/include/asm/setup.h | 3 +- xen/common/dt-overlay.c | 218 ++++++++++++++++++++++++++----- xen/include/public/sysctl.h | 4 +- 5 files changed, 199 insertions(+), 36 deletions(-) diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c index 3e02cff008..df5035f9a8 100644 --- a/xen/arch/arm/device.c +++ b/xen/arch/arm/device.c @@ -317,7 +317,8 @@ static int map_device_children(const struct dt_device_node *dev, * - Map the IRQs and iomem regions to DOM0 */ int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt, - struct rangeset *iomem_ranges, struct rangeset *irq_ranges) + struct rangeset *iomem_ranges, struct rangeset *irq_ranges, + bool device_mapping) { unsigned int naddr; unsigned int i; @@ -334,9 +335,10 @@ int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt, struct map_range_data mr_data = { .d = d, .p2mt = p2mt, - .skip_mapping = !own_device || + .skip_mapping = (!own_device || (is_pci_passthrough_enabled() && - (device_get_class(dev) == DEVICE_PCI_HOSTBRIDGE)), + (device_get_class(dev) == DEVICE_PCI_HOSTBRIDGE))) && + !device_mapping, .iomem_ranges = iomem_ranges, .irq_ranges = irq_ranges }; diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c index 54232ed4cb..a525aed175 100644 --- a/xen/arch/arm/domain_build.c +++ b/xen/arch/arm/domain_build.c @@ -1699,7 +1699,7 @@ static int __init handle_node(struct domain *d, struct kernel_info *kinfo, "WARNING: Path %s is reserved, skip the node as we may re-use the path.\n", path); - res = handle_device(d, node, p2mt, NULL, NULL); + res = handle_device(d, node, p2mt, NULL, NULL, false); if ( res) return res; diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index d15a88d2e0..d6d8c28d02 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -172,7 +172,8 @@ u32 device_tree_get_u32(const void *fdt, int node, const char *prop_name, u32 dflt); int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt, - struct rangeset *iomem_ranges, struct rangeset *irq_ranges); + struct rangeset *iomem_ranges, struct rangeset *irq_ranges, + bool device_mapping); int map_device_irqs_to_domain(struct domain *d, struct dt_device_node *dev, bool need_mapping, struct rangeset *irq_ranges); diff --git a/xen/common/dt-overlay.c b/xen/common/dt-overlay.c index 39e4ba59dd..8840ea756b 100644 --- a/xen/common/dt-overlay.c +++ b/xen/common/dt-overlay.c @@ -356,24 +356,133 @@ static int overlay_get_nodes_info(const void *fdto, char **nodes_full_path) return 0; } +static int remove_all_irqs(struct rangeset *irq_ranges, + struct domain *d, bool domain_mapping) +{ + struct range *x; + int rc = 0; + + read_lock(&irq_ranges->lock); + + for ( x = first_range(irq_ranges); x != NULL; + x = next_range(irq_ranges, x) ) + { + /* + * Handle invalid use cases 1: + * Where user assigned the nodes to dom0 along with their irq + * mappings but now just wants to remove the nodes entry from Xen device + * device tree without unmapping the irq. + */ + if ( !domain_mapping && vgic_get_hw_irq_desc(d, NULL, x->s) ) + { + printk(XENLOG_ERR "Removing node from device tree without releasing it's IRQ/IOMMU is not allowed\n"); + rc = -EINVAL; + goto out; + } + + /* + * 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, x->s)) + continue; + /* + * 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, x->s); + if ( rc ) + { + printk(XENLOG_ERR "unable to revoke access for irq %ld\n", x->s); + goto out; + } + + if ( domain_mapping ) + { + rc = release_guest_irq(d, x->s); + if ( rc ) + { + printk(XENLOG_ERR "unable to release irq %ld\n", x->s); + goto out; + } + } + } + +out: + read_unlock(&irq_ranges->lock); + return rc; +} + +static int remove_all_iomems(struct rangeset *iomem_ranges, + struct domain *d, + bool domain_mapping) +{ + struct range *x; + int rc = 0; + p2m_type_t t; + mfn_t mfn; + + read_lock(&iomem_ranges->lock); + + for ( x = first_range(iomem_ranges); x != NULL; + x = next_range(iomem_ranges, x) ) + { + mfn = p2m_lookup(d, _gfn(x->s), &t); + if ( mfn_x(mfn) == 0 || mfn_x(mfn) == ~0UL ) + { + rc = -EINVAL; + goto out; + } + + rc = iomem_deny_access(d, x->s, x->e); + if ( rc ) + { + printk(XENLOG_ERR "Unable to remove dom%d access to %#lx - %#lx\n", + d->domain_id, + x->s, x->e); + goto out; + } + + rc = unmap_mmio_regions(d, _gfn(x->s), x->e - x->s, + _mfn(x->s)); + if ( rc ) + goto out; + } + +out: + read_unlock(&iomem_ranges->lock); + return rc; +} + /* 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, bool domain_mapping) { int rc = 0; unsigned int len; domid_t domid; - domid = dt_device_used_by(device_node); + if ( !d ) + { + domid = dt_device_used_by(device_node); - dt_dprintk("Checking if node %s is used by any domain\n", - device_node->full_name); + /* + * We also check if device is assigned to DOMID_IO as when a domain + * is destroyed device is assigned to DOMID_IO or for the case when + * device was never mapped to a running domain. + */ + if ( domid != hardware_domain->domain_id && 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; + } - /* 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; + /* + * Device is assigned to hardware domain. + */ + d = hardware_domain; } /* Check if iommu property exists. */ @@ -382,6 +491,16 @@ static int remove_node_resources(struct dt_device_node *device_node) if ( dt_device_is_protected(device_node) ) { write_lock(&dt_host_lock); + if ( domain_mapping && !list_empty(&device_node->domain_list) ) + { + rc = iommu_deassign_dt_device(d, device_node); + if ( rc < 0 ) + { + write_unlock(&dt_host_lock); + return rc; + } + } + rc = iommu_remove_dt_device(device_node); if ( rc < 0 ) { @@ -397,7 +516,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, bool domain_mapping) { int rc = 0; struct dt_device_node *child_node; @@ -407,12 +527,13 @@ 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, + domain_mapping); if ( rc ) return rc; } - rc = remove_node_resources(child_node); + rc = remove_node_resources(child_node, d, domain_mapping); if ( rc ) return rc; } @@ -421,12 +542,13 @@ remove_descendant_nodes_resources(const struct dt_device_node *device_node) } /* Remove nodes from dt_host. */ -static int remove_nodes(const struct overlay_track *tracker) +static int remove_nodes(const struct overlay_track *tracker, + struct domain *d) { int rc = 0; struct dt_device_node *overlay_node; unsigned int j; - struct domain *d = hardware_domain; + bool domain_mapping = (d != NULL); for ( j = 0; j < tracker->num_nodes; j++ ) { @@ -434,11 +556,19 @@ static int remove_nodes(const struct overlay_track *tracker) if ( overlay_node == NULL ) return -EINVAL; - rc = remove_descendant_nodes_resources(overlay_node); + rc = remove_descendant_nodes_resources(overlay_node, d, domain_mapping); + if ( rc ) + return rc; + + rc = remove_node_resources(overlay_node, d, domain_mapping); + if ( rc ) + return rc; + + rc = remove_all_irqs(tracker->irq_ranges, d, domain_mapping); if ( rc ) return rc; - rc = remove_node_resources(overlay_node); + rc = remove_all_iomems(tracker->iomem_ranges, d, domain_mapping); if ( rc ) return rc; @@ -481,7 +611,8 @@ static int remove_nodes(const struct overlay_track *tracker) * while destroying the domain. */ static long handle_remove_overlay_nodes(const void *overlay_fdt, - uint32_t overlay_fdt_size) + uint32_t overlay_fdt_size, + struct domain *d) { int rc; struct overlay_track *entry, *temp, *track; @@ -520,7 +651,7 @@ static long handle_remove_overlay_nodes(const void *overlay_fdt, } - rc = remove_nodes(entry); + rc = remove_nodes(entry, d); if ( rc ) { printk(XENLOG_ERR "Removing node failed\n"); @@ -560,12 +691,21 @@ static void free_nodes_full_path(unsigned int num_nodes, char **nodes_full_path) xfree(nodes_full_path); } -static long add_nodes(struct overlay_track *tr, char **nodes_full_path) +static long add_nodes(struct overlay_track *tr, char **nodes_full_path, + struct domain *d) { int rc; unsigned int j; struct dt_device_node *overlay_node; + bool domain_mapping = (d != NULL); + + /* + * If domain is NULL, then we add the devices into hardware domain and skip + * IRQs/IOMMUs mapping. + */ + if ( d == NULL ) + d = hardware_domain; for ( j = 0; j < tr->num_nodes; j++ ) { @@ -609,8 +749,6 @@ static long add_nodes(struct overlay_track *tr, char **nodes_full_path) return rc; } - write_unlock(&dt_host_lock); - prev_node->allnext = next_node; overlay_node = dt_find_node_by_path(overlay_node->full_name); @@ -618,18 +756,23 @@ static long add_nodes(struct overlay_track *tr, char **nodes_full_path) { /* Sanity check. But code will never come here. */ ASSERT_UNREACHABLE(); + write_unlock(&dt_host_lock); return -EFAULT; } - rc = handle_device(hardware_domain, overlay_node, p2m_mmio_direct_c, + rc = handle_device(d, overlay_node, p2m_mmio_direct_c, tr->iomem_ranges, - tr->irq_ranges); + tr->irq_ranges, + domain_mapping); if ( rc ) { printk(XENLOG_ERR "Adding IRQ and IOMMU failed\n"); + write_unlock(&dt_host_lock); return rc; } + write_unlock(&dt_host_lock); + /* Keep overlay_node address in tracker. */ tr->nodes_address[j] = (unsigned long)overlay_node; } @@ -643,7 +786,8 @@ static long add_nodes(struct overlay_track *tr, char **nodes_full_path) * to hardware domain done by handle_node(). */ static long handle_add_overlay_nodes(void *overlay_fdt, - uint32_t overlay_fdt_size) + uint32_t overlay_fdt_size, + struct domain *d) { int rc; unsigned int j; @@ -788,7 +932,7 @@ static long handle_add_overlay_nodes(void *overlay_fdt, goto err; } - rc = add_nodes(tr, nodes_full_path); + rc = add_nodes(tr, nodes_full_path, d); if ( rc ) { printk(XENLOG_ERR "Adding nodes failed. Removing the partially added nodes.\n"); @@ -810,7 +954,7 @@ static long handle_add_overlay_nodes(void *overlay_fdt, */ remove_node: tr->num_nodes = j; - rc = remove_nodes(tr); + rc = remove_nodes(tr, d); if ( rc ) { @@ -855,6 +999,7 @@ long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op) { long ret; void *overlay_fdt; + struct domain *d = NULL; if ( op->overlay_op != XEN_SYSCTL_DT_OVERLAY_ADD && op->overlay_op != XEN_SYSCTL_DT_OVERLAY_REMOVE ) @@ -863,7 +1008,7 @@ long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op) if ( op->overlay_fdt_size == 0 || op->overlay_fdt_size > KB(500) ) return -EINVAL; - if ( op->pad[0] || op->pad[1] || op->pad[2] ) + if ( op->pad[0] || op->pad[1] ) return -EINVAL; overlay_fdt = xmalloc_bytes(op->overlay_fdt_size); @@ -880,13 +1025,26 @@ long dt_overlay_sysctl(struct xen_sysctl_dt_overlay *op) return -EFAULT; } + /* + * If domain_mapping == false, domain_id can be ignored as we don't need to + * map resource to any domain. + * + * If domain_mapping == true, domain_id, get the target domain for the + * mapping. + */ + if ( op->domain_mapping ) + d = rcu_lock_domain_by_id(op->domain_id); + if ( op->overlay_op == XEN_SYSCTL_DT_OVERLAY_REMOVE ) - ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size); + ret = handle_remove_overlay_nodes(overlay_fdt, op->overlay_fdt_size, d); else - ret = handle_add_overlay_nodes(overlay_fdt, op->overlay_fdt_size); + ret = handle_add_overlay_nodes(overlay_fdt, op->overlay_fdt_size, d); xfree(overlay_fdt); + if ( op->domain_mapping ) + rcu_unlock_domain(d); + return ret; } diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index 9b19679cae..f5435f8890 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -1197,7 +1197,9 @@ struct xen_sysctl_dt_overlay { #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. */ + bool domain_mapping; /* IN: True of False. */ + uint8_t pad[2]; /* IN: Must be zero. */ + uint32_t domain_id; }; #endif -- 2.34.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |