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

Re: [Xen-devel] [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping



Hi,

On 06/04/17 16:34, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:19, 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.
>> Because device IDs are per ITS, we need to identify a virtual ITS. We
>> use the doorbell address for that purpose, as it is a nice architectural
>> MSI property and spares us handling with opaque pointer or break
>> the VGIC abstraction.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
>> ---
>>  xen/arch/arm/gic-v3-its.c        | 263
>> +++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic-v3-its.c       |   3 +
>>  xen/include/asm-arm/domain.h     |   3 +
>>  xen/include/asm-arm/gic_v3_its.h |  13 ++
>>  4 files changed, 282 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index eb47c9d..45bbfa7 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>
>> @@ -36,6 +38,26 @@
>>   */
>>  LIST_HEAD(host_its_list);
>>
>> +/*
>> + * Describes a device which is using the ITS and is used by a guest.
>> + * Since device IDs are per ITS (in contrast to vLPIs, which are per
>> + * guest), we have to differentiate between different virtual ITSes.
>> + * We use the doorbell address here, since this is a nice architectural
>> + * property of MSIs in general and we can easily get to the base address
>> + * of the ITS and look that up.
>> + */
>> +struct its_devices {
> 
> Again, why its_devices? You only store the information for one device.

You are right, and actually I fixed that already an hour ago.

>> +    struct rb_node rbnode;
>> +    struct host_its *hw_its;
>> +    void *itt_addr;
>> +    paddr_t guest_doorbell;             /* Identifies the virtual ITS */
>> +    uint32_t host_devid;
>> +    uint32_t guest_devid;
>> +    uint32_t eventids;                  /* Number of event IDs (MSIs) */
>> +    uint32_t *host_lpi_blocks;          /* Which LPIs are used on the
>> host */
>> +    struct pending_irq *pend_irqs;      /* One struct per event */
>> +};
> 
> [...]
> 
>> +static int remove_mapped_guest_device(struct its_devices *dev)
>> +{
>> +    int ret = 0;
>> +    unsigned int i;
>> +
>> +    if ( dev->hw_its )
>> +        /* MAPD also discards all events with this device ID. */
>> +        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0,
>> false);
>> +
>> +    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
>> +        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
>> +
>> +    /* Make sure the MAPD command above is really executed. */
>> +    if ( !ret )
>> +        ret = gicv3_its_wait_commands(dev->hw_its);
>> +
>> +    /* This should never happen, but just in case ... */
>> +    if ( ret )
>> +        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
>> +               dev->host_devid);
> 
> I think we want to ratelimit this.

OK.

>> +
>> +    xfree(dev->itt_addr);
>> +    xfree(dev->pend_irqs);
>> +    xfree(dev->host_lpi_blocks);
>> +    xfree(dev);
>> +
>> +    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;
>> +    struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent
>> = NULL;
>> +    unsigned int i;
>> +    int ret = -ENOENT;
>> +
>> +    hw_its = gicv3_its_find_by_doorbell(host_doorbell);
>> +    if ( !hw_its )
>> +        return ret;
>> +
>> +    /* Sanitise the provided hardware values against the host ITS. */
>> +    if ( host_devid >= BIT(hw_its->devid_bits) )
>> +        return -EINVAL;
>> +
>> +    /* We allocate events and LPIs in chunks of LPI_BLOCK (=32). */
>> +    nr_events = ROUNDUP(nr_events, LPI_BLOCK);
> 
> I think it is still not enough. The MAPD command will use the number of
> bits, so you want to make sure you cover all the events in the table.
> 
> For instance, if nr_events = 66, you will round up to 96. My
> understanding, is you will call mapd with 7 bits rather 8 bits.
> 
> So you have to round up to the next power of two. Whether you want to
> allocate a power of two of LPIs is another question. But the ITT should
> be configured correctly.

Again good catch.

> 
> [...]
> 
>> +    if ( ret )
>> +    {
>> +        do {
>> +            i--;
>> +            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
>> +            if ( i == 0 )
>> +                break;
>> +        } while (1);
> 
> This could be  } while ( i > 0 ) saving 3 lines.

Argh, I rewrote this sucker three times because "i" is unsigned and I
apparently missed the easiest solution ;-)
And still there is a bug in there (if the first allocation fails already
above). Do you mind if I revert "i" back to a signed int? That would
allow me to do write "while ( i >= 0 )" here or a for loop.

>> +
>> +        /* Unmapping the device will discard all LPIs mapped so far. */
>> +        its_send_cmd_mapd(hw_its, host_devid, 1, 0, false);
> 
> You likely need to check the return of its_send_mapd...
> 
> Also why 1 for the 3rd argument?

Good catch, I thought I changed this to 0 after I applied your changes
to the wrapper function ...

>> +
>> +        goto out;
>> +    }
>> +
>> +    return 0;
>> +
>> +out_unlock:
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +out:
>> +    if ( dev )
>> +    {
>> +        xfree(dev->pend_irqs);
>> +        xfree(dev->host_lpi_blocks);
>> +    }
>> +    xfree(itt_addr);
>> +    xfree(dev);
>> +
>> +    return ret;
>> +}
>> +
> 
> Cheers,
> 
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

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