|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v2 06/27] ARM: GICv3 ITS: introduce device mapping
Hi,
On 22/03/17 22:45, Stefano Stabellini wrote:
> On Thu, 16 Mar 2017, Andre Przywara wrote:
>> The ITS uses device IDs to map LPIs to a device. Dom0 will later use
>> those IDs, which we directly pass on to the host.
>> For this we have to map each device that Dom0 may request to a host
>> ITS device with the same identifier.
>> Allocate the respective memory and enter each device into an rbtree to
>> later be able to iterate over it or to easily teardown guests.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
>> ---
>> xen/arch/arm/gic-v3-its.c | 207
>> +++++++++++++++++++++++++++++++++++++++
>> xen/arch/arm/vgic-v3.c | 3 +
>> xen/include/asm-arm/domain.h | 3 +
>> xen/include/asm-arm/gic_v3_its.h | 18 ++++
>> 4 files changed, 231 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index 5c11b0d..60b15b5 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -21,6 +21,8 @@
>> #include <xen/lib.h>
>> #include <xen/delay.h>
>> #include <xen/mm.h>
>> +#include <xen/rbtree.h>
>> +#include <xen/sched.h>
>> #include <xen/sizes.h>
>> #include <asm/gic.h>
>> #include <asm/gic_v3_defs.h>
>> @@ -32,6 +34,17 @@
>>
>> LIST_HEAD(host_its_list);
>>
>> +struct its_devices {
>> + struct rb_node rbnode;
>> + struct host_its *hw_its;
>> + void *itt_addr;
>> + paddr_t guest_doorbell;
>> + uint32_t host_devid;
>> + uint32_t guest_devid;
>> + uint32_t eventids;
>> + uint32_t *host_lpis;
>> +};
>> +
>> bool gicv3_its_host_has_its(void)
>> {
>> return !list_empty(&host_its_list);
>> @@ -149,6 +162,24 @@ static int its_send_cmd_mapc(struct host_its *its,
>> uint32_t collection_id,
>> return its_send_command(its, cmd);
>> }
>>
>> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
>> + uint8_t size_bits, paddr_t itt_addr, bool
>> valid)
>> +{
>> + uint64_t cmd[4];
>> +
>> + if ( valid )
>> + {
>> + ASSERT(size_bits < 32);
>> + ASSERT(!(itt_addr & ~GENMASK(51, 8)));
>> + }
>> + cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
>> + cmd[1] = valid ? size_bits : 0x00;
>> + cmd[2] = valid ? (itt_addr | GITS_VALID_BIT) : 0x00;
>> + cmd[3] = 0x00;
>> +
>> + return its_send_command(its, cmd);
>> +}
>> +
>> /* Set up the (1:1) collection mapping for the given host CPU. */
>> int gicv3_its_setup_collection(unsigned int cpu)
>> {
>> @@ -379,6 +410,7 @@ static int gicv3_its_init_single_its(struct host_its
>> *hw_its)
>> devid_bits = min(devid_bits, max_its_device_bits);
>> if ( reg & GITS_TYPER_PTA )
>> hw_its->flags |= HOST_ITS_USES_PTA;
>> + hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
>>
>> for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>> {
>> @@ -428,6 +460,180 @@ int gicv3_its_init(void)
>> return 0;
>> }
>>
>> +static int remove_mapped_guest_device(struct its_devices *dev)
>> +{
>> + int ret;
>> +
>> + if ( dev->hw_its )
>> + {
>> + int ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0,
>> false);
>> + if ( ret )
>> + return ret;
>> + }
>> +
>> + ret = gicv3_its_wait_commands(dev->hw_its);
>> + if ( ret )
>> + return ret;
>> +
>> + xfree(dev->itt_addr);
>> + xfree(dev);
>> +
>> + return 0;
>> +}
>> +
>> +static struct host_its *gicv3_its_find_by_doorbell(paddr_t doorbell_address)
>> +{
>> + struct host_its *hw_its;
>> +
>> + list_for_each_entry(hw_its, &host_its_list, entry)
>
> Does this need to take a spinlock to protect host_its_list? I guess not
> because the list is not modified after boot?
Exactly, I added a comment in v4 explaining this.
>> + {
>> + if ( hw_its->addr + ITS_DOORBELL_OFFSET == doorbell_address )
>> + return hw_its;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static int compare_its_guest_devices(struct its_devices *dev,
>> + paddr_t doorbell, uint32_t devid)
>> +{
>> + if ( dev->guest_doorbell < doorbell )
>> + return -1;
>> +
>> + if ( dev->guest_doorbell > doorbell )
>> + return 1;
>> +
>> + if ( dev->guest_devid < devid )
>> + return -1;
>> +
>> + if ( dev->guest_devid > devid )
>> + return 1;
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * Map a hardware device, identified by a certain host ITS and its device ID
>> + * to domain d, a guest ITS (identified by its doorbell address) and device
>> ID.
>> + * Also provide the number of events (MSIs) needed for that device.
>> + * This does not check if this particular hardware device is already mapped
>> + * at another domain, it is expected that this would be done by the caller.
>> + */
>> +int gicv3_its_map_guest_device(struct domain *d,
>> + paddr_t host_doorbell, uint32_t host_devid,
>> + paddr_t guest_doorbell, uint32_t guest_devid,
>> + uint32_t nr_events, bool valid)
>> +{
>> + void *itt_addr = NULL;
>> + struct host_its *hw_its;
>> + struct its_devices *dev = NULL, *temp;
>> + struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent =
>> NULL;
>> + int ret = -ENOENT;
>> +
>> + hw_its = gicv3_its_find_by_doorbell(host_doorbell);
>> + if ( !hw_its )
>> + return ret;
>> +
>> + /* check for already existing mappings */
>> + spin_lock(&d->arch.vgic.its_devices_lock);
>> + while ( *new )
>> + {
>> + temp = rb_entry(*new, struct its_devices, rbnode);
>> +
>> + parent = *new;
>> + if ( !compare_its_guest_devices(temp, guest_doorbell, guest_devid) )
>> + {
>> + if ( !valid )
>> + rb_erase(&temp->rbnode, &d->arch.vgic.its_devices);
>> +
>> + spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> + if ( valid )
>> + return -EBUSY;
>> +
>> + return remove_mapped_guest_device(temp);
>> + }
>> +
>
> Plese don't run compare_its_guest_devices twice with the same arguments,
> just store the return value.
Fixed in v3.
>> + if ( compare_its_guest_devices(temp, guest_doorbell, guest_devid) >
>> 0 )
>> + new = &((*new)->rb_left);
>> + else
>> + new = &((*new)->rb_right);
>> + }
>> +
>> + if ( !valid )
>> + goto out_unlock;
>> +
>> + ret = -ENOMEM;
>> +
>> + /* An Interrupt Translation Table needs to be 256-byte aligned. */
>> + itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
>> + if ( !itt_addr )
>> + goto out_unlock;
>> +
>> + dev = xzalloc(struct its_devices);
>> + if ( !dev )
>> + goto out_unlock;
>> +
>> + ret = its_send_cmd_mapd(hw_its, host_devid, fls(nr_events - 1) - 1,
>> + virt_to_maddr(itt_addr), true);
>> + if ( ret )
>> + goto out_unlock;
>> +
>> + dev->itt_addr = itt_addr;
>> + dev->hw_its = hw_its;
>> + dev->guest_doorbell = guest_doorbell;
>> + dev->guest_devid = guest_devid;
>> + dev->host_devid = host_devid;
>> + dev->eventids = nr_events;
>> +
>> + rb_link_node(&dev->rbnode, parent, new);
>> + rb_insert_color(&dev->rbnode, &d->arch.vgic.its_devices);
>> +
>> + spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> + return 0;
>> +
>> +out_unlock:
>> + spin_unlock(&d->arch.vgic.its_devices_lock);
>> + if ( dev )
>> + xfree(dev->host_lpis);
>
> Where is host_lpis allocated? Why is it freed here?
So I swapped this patch with the next one in v4, because this solves
this issue.
>> + xfree(itt_addr);
>> + xfree(dev);
>> + return ret;
>> +}
>> +
>> +/* Removing any connections a domain had to any ITS in the system. */
>> +void gicv3_its_unmap_all_devices(struct domain *d)
>> +{
>> + struct rb_node *victim;
>> + struct its_devices *dev;
>> +
>> + /*
>> + * This is an easily readable, yet inefficient implementation.
>> + * It uses the provided iteration wrapper and erases each node, which
>> + * possibly triggers rebalancing.
>> + * This seems overkill since we are going to abolish the whole tree, but
>> + * avoids an open-coded re-implementation of the traversal functions
>> with
>> + * some recursive function calls.
>> + * Performance does not matter here, since we are destroying a domain.
>> + */
>> +restart:
>> + spin_lock(&d->arch.vgic.its_devices_lock);
>> + if ( (victim = rb_first(&d->arch.vgic.its_devices)) )
>> + {
>> + dev = rb_entry(victim, struct its_devices, rbnode);
>> + rb_erase(victim, &d->arch.vgic.its_devices);
>> +
>> + spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> + remove_mapped_guest_device(dev);
>> +
>> + goto restart;
>> + }
>
> There is no need to use goto here, a simple while loop should suffice.
Fixed in v4.
>> + spin_unlock(&d->arch.vgic.its_devices_lock);
>> +}
>> +
>> /* Scan the DT for any ITS nodes and create a list of host ITSes out of it.
>> */
>> void gicv3_its_dt_init(const struct dt_device_node *node)
>> {
>> @@ -455,6 +661,7 @@ void gicv3_its_dt_init(const struct dt_device_node *node)
>> its_data->addr = addr;
>> its_data->size = size;
>> its_data->dt_node = its;
>> + spin_lock_init(&its_data->cmd_lock);
>
> This change should be in the previous patch
Fixed in v4.
>> printk("GICv3: Found ITS @0x%lx\n", addr);
>>
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index d61479d..1fadb00 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -1450,6 +1450,9 @@ static int vgic_v3_domain_init(struct domain *d)
>> d->arch.vgic.nr_regions = rdist_count;
>> d->arch.vgic.rdist_regions = rdist_regions;
>>
>> + spin_lock_init(&d->arch.vgic.its_devices_lock);
>> + d->arch.vgic.its_devices = RB_ROOT;
>> +
>> /*
>> * Domain 0 gets the hardware address.
>> * Guests get the virtual platform layout.
>> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
>> index 2d6fbb1..00b9c1a 100644
>> --- a/xen/include/asm-arm/domain.h
>> +++ b/xen/include/asm-arm/domain.h
>> @@ -11,6 +11,7 @@
>> #include <asm/gic.h>
>> #include <public/hvm/params.h>
>> #include <xen/serial.h>
>> +#include <xen/rbtree.h>
>>
>> struct hvm_domain
>> {
>> @@ -109,6 +110,8 @@ struct arch_domain
>> } *rdist_regions;
>> int nr_regions; /* Number of rdist regions */
>> uint32_t rdist_stride; /* Re-Distributor stride */
>> + struct rb_root its_devices; /* devices mapped to an ITS */
>> + spinlock_t its_devices_lock; /* protects the its_devices
>> tree */
>> #endif
>> } vgic;
>>
>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>> b/xen/include/asm-arm/gic_v3_its.h
>> index 8b493fb..3421ea0 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -48,6 +48,10 @@
>> #define GITS_TYPER_DEVICE_ID_BITS(r) (((r & GITS_TYPER_DEVIDS_MASK) >> \
>> GITS_TYPER_DEVIDS_SHIFT) + 1)
>> #define GITS_TYPER_IDBITS_SHIFT 8
>> +#define GITS_TYPER_ITT_SIZE_SHIFT 4
>> +#define GITS_TYPER_ITT_SIZE_MASK (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
>> +#define GITS_TYPER_ITT_SIZE(r) ((((r) & GITS_TYPER_ITT_SIZE_MASK)
>> >> \
>> + GITS_TYPER_ITT_SIZE_SHIFT)
>> + 1)
>>
>> #define GITS_IIDR_VALUE 0x34c
>>
>> @@ -94,7 +98,10 @@
>> #define GITS_CMD_MOVALL 0x0e
>> #define GITS_CMD_DISCARD 0x0f
>>
>> +#define ITS_DOORBELL_OFFSET 0x10040
>> +
>> #include <xen/device_tree.h>
>> +#include <xen/rbtree.h>
>>
>> #define HOST_ITS_FLUSH_CMD_QUEUE (1U << 0)
>> #define HOST_ITS_USES_PTA (1U << 1)
>> @@ -108,6 +115,7 @@ struct host_its {
>> void __iomem *its_base;
>> spinlock_t cmd_lock;
>> void *cmd_buf;
>> + unsigned int itte_size;
>> unsigned int flags;
>> };
>
> I would move itte_size and its initialization to the patch that
> introduced struct host_its.
Puh, OK. This wasn't as innocent as it looks like on the first glance,
so I created a new patch to introduce the host ITS init early and
separate this from the ITS table init.
Cheers,
Andre.
>> @@ -134,6 +142,16 @@ uint64_t gicv3_get_redist_address(unsigned int cpu,
>> bool use_pta);
>> /* Map a collection for this host CPU to each host ITS. */
>> int gicv3_its_setup_collection(unsigned int cpu);
>>
>> +/*
>> + * Map a device on the host by allocating an ITT on the host (ITS).
>> + * "nr_event" specifies how many events (interrupts) this device will need.
>> + * Setting "valid" to false deallocates the device.
>> + */
>> +int gicv3_its_map_guest_device(struct domain *d,
>> + paddr_t host_doorbell, uint32_t host_devid,
>> + paddr_t guest_doorbell, uint32_t guest_devid,
>> + uint32_t nr_events, bool valid);
>> +
>> #else
>>
>> static LIST_HEAD(host_its_list);
>> --
>> 2.9.0
>>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |