All of lore.kernel.org
 help / color / mirror / Atom feed
From: Julien Grall <julien.grall@arm.com>
To: Andre Przywara <andre.przywara@arm.com>,
	Stefano Stabellini <sstabellini@kernel.org>
Cc: xen-devel@lists.xenproject.org
Subject: Re: [PATCH v4 08/27] ARM: GICv3 ITS: introduce device mapping
Date: Tue, 4 Apr 2017 10:03:00 +0100	[thread overview]
Message-ID: <9d7d3413-2b81-36cc-d983-e0abde117225@arm.com> (raw)
In-Reply-To: <20170403202829.7278-9-andre.przywara@arm.com>

Hi Andre,

On 04/03/2017 09:28 PM, 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@arm.com>
> ---
>  xen/arch/arm/gic-v3-its.c        | 266 +++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c           |   4 +
>  xen/include/asm-arm/domain.h     |   3 +
>  xen/include/asm-arm/gic_v3_its.h |  17 +++
>  4 files changed, 290 insertions(+)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index bd189e8..c7c32b9 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 {
> +    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 */
> +};
> +
>  bool gicv3_its_host_has_its(void)
>  {
>      return !list_empty(&host_its_list);
> @@ -170,6 +192,26 @@ 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 <= its->evid_bits);

Because of the -1 below, you expect size_bits to always be > 0. So I 
would add another check here.

> +        ASSERT(!(itt_addr & ~GENMASK_ULL(51, 8)));
> +    }
> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
> +    cmd[1] = size_bits - 1;

I am sorry, I know I suggested this. How about moving size_bits - 1 
under the if ( valid ) above?

This would avoid the 1 in remove_mapped_guest_device.

> +    cmd[2] = itt_addr;
> +    if ( valid )
> +        cmd[2] |= GITS_VALID_BIT;
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
>  static int its_send_cmd_inv(struct host_its *its,
>                              uint32_t deviceid, uint32_t eventid)
>  {
> @@ -464,6 +506,64 @@ int gicv3_its_init(void)
>      return 0;
>  }
>
> +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, 1, 0, false);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(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);
> +
> +    /* We can't free the ITT memory if the MAPD(V=0) failed for any reason. */
> +    if ( !ret )
> +        xfree(dev->itt_addr);

That's a leak. How likely this can happen? How do you plan to address this?

> +
> +    xfree(dev->pend_irqs);
> +    xfree(dev->host_lpi_blocks);
> +    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)
> +    {
> +        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;
> +}
> +
>  /*
>   * On the host ITS @its, map @nr_events consecutive LPIs.
>   * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
> @@ -495,6 +595,172 @@ static int gicv3_its_map_host_events(struct host_its *its,
>      return gicv3_its_wait_commands(its);
>  }
>
> +/*
> + * 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;

You likely need to sanitize host_devid, nr_events against the hardware 
values.

> +
> +    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 )
> +    {
> +        struct its_devices *temp;
> +        int cmp;
> +
> +        temp = rb_entry(*new, struct its_devices, rbnode);
> +
> +        parent = *new;
> +        cmp = compare_its_guest_devices(temp, guest_doorbell, guest_devid);
> +        if ( !cmp )
> +        {
> +            if ( !valid )
> +                rb_erase(&temp->rbnode, &d->arch.vgic.its_devices);
> +
> +            spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +            if ( valid )
> +            {
> +                gprintk(XENLOG_DEBUG, "d%d: tried to remap guest ITS device 0x%x to host device 0x% x\n",
> +                        d->domain_id, guest_devid, host_devid);

Please use printk(XENLOG_GUEST XENLOG_DEBUG ...) as you don't care about 
the domain that calls the function.

> +                return -EBUSY;
> +            }
> +
> +            return remove_mapped_guest_device(temp);
> +        }
> +
> +        if ( cmp > 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);

The MAPD command will use the number of event bits, so you need to 
allocate (1 << N) * hw_its->itte_size

When you do the computation, the number of events has to be aligned to 
32 to prevent any memory corruption.

This is because a guest MAPD could contain Size = 1 (i.e 2 events) but 
you will further down align to a minimum of 32.

Note this is the bug reported by Vijay.

> +    if ( !itt_addr )
> +        goto out_unlock;
> +
> +    dev = xzalloc(struct its_devices);
> +    if ( !dev )
> +        goto out_unlock;
> +
> +    /*
> +     * Allocate the pending_irqs for each virtual LPI. They will be put
> +     * into the domain's radix tree upon the guest's MAPTI command.
> +     */
> +    dev->pend_irqs = xzalloc_array(struct pending_irq, nr_events);
> +    if ( !dev->pend_irqs )
> +        goto out_unlock;
> +
> +    dev->host_lpi_blocks = xzalloc_array(uint32_t,
> +                                         DIV_ROUND_UP(nr_events, LPI_BLOCK));

I still think it would make easier to round_up the number of events at 
the top of the function.

> +    if ( !dev->host_lpi_blocks )
> +        goto out_unlock;
> +
> +    ret = its_send_cmd_mapd(hw_its, host_devid,
> +                            fls(ROUNDUP(nr_events, LPI_BLOCK) - 1),

See my comment above about the number of event bits.

> +                            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);
> +
> +    /*
> +     * Map all host LPIs within this device already. We can't afford to queue
> +     * any host ITS commands later on during the guest's runtime.
> +     */
> +    for ( i = 0; i < DIV_ROUND_UP(nr_events, LPI_BLOCK); i++ )

It is the second time in this function you do DIV_ROUND_UP. Please 
introduce a variable to make easier to read and avoid potential mistake.

> +    {
> +        ret = gicv3_allocate_host_lpi_block(d, &dev->host_lpi_blocks[i]);
> +        if ( ret < 0 )
> +            goto out;
> +
> +        ret = gicv3_its_map_host_events(hw_its, host_devid, i * LPI_BLOCK,
> +                                        dev->host_lpi_blocks[i], LPI_BLOCK);
> +        if ( ret < 0 )
> +            goto out;
> +    }
> +
> +    return 0;
> +
> +out_unlock:
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +out:

There are a lot of things missing in the error path:
	- Free LPI block allocated
	- Send MAPD (V=0) command.

> +    if ( dev )
> +    {
> +        xfree(dev->pend_irqs);
> +        xfree(dev->host_lpi_blocks);
> +    }
> +    xfree(itt_addr);
> +    xfree(dev);

NIT: newline here.

> +    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, but sub-optimal 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.
> +     */

I still maintain my point on this function. I would expect an upper 
layer to remove the devices on by one during the relinquish period (see 
domain_relinquish_resources). So we can report error back and allow 
preemption.

> +    do {
> +        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);

You likely need to propagate the return here.

> +        }
> +    } while ( victim );
> +
> +    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)
>  {
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 0679e76..3c7161b 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -1449,6 +1449,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;

I continue to maintain this is the wrong place for those 2 variables. 
ITS should be separated from the rest of the GICv3.

> +
>      /*
>       * Domain 0 gets the hardware address.
>       * Guests get the virtual platform layout.
> @@ -1521,6 +1524,7 @@ static int vgic_v3_domain_init(struct domain *d)
>
>  static void vgic_v3_domain_free(struct domain *d)
>  {
> +    gicv3_its_unmap_all_devices(d);

This is likely the wrong place to call as you would need to report back 
error or potential preemption. This would have to be called in 
domain_relinquish_resources.

This is also a call for another gic_ops callback.

Cheers,

-- 
Julien Grall

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

  reply	other threads:[~2017-04-04  9:03 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-03 20:28 [PATCH v4 00/27] arm64: Dom0 ITS emulation Andre Przywara
2017-04-03 20:28 ` [PATCH v4 01/27] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
2017-04-05  0:40   ` Stefano Stabellini
2017-04-03 20:28 ` [PATCH v4 02/27] ARM: GICv3 ITS: initialize host ITS Andre Przywara
2017-04-03 21:03   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 03/27] ARM: GICv3: allocate LPI pending and property table Andre Przywara
2017-04-03 21:47   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 04/27] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
2017-04-05  0:56   ` Stefano Stabellini
2017-04-03 20:28 ` [PATCH v4 05/27] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
2017-04-03 21:56   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 06/27] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
2017-04-03 22:39   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 07/27] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
2017-04-03 23:07   ` Julien Grall
2017-04-04 10:40     ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 08/27] ARM: GICv3 ITS: introduce device mapping Andre Przywara
2017-04-04  9:03   ` Julien Grall [this message]
2017-04-04 16:13   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 09/27] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
2017-04-04 11:43   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 10/27] ARM: GICv3: forward pending LPIs to guests Andre Przywara
2017-04-04 11:55   ` Julien Grall
2017-04-04 15:36   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 11/27] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
2017-04-03 20:28 ` [PATCH v4 12/27] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
2017-04-04 13:01   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 13/27] ARM: vGICv3: Handle disabled LPIs Andre Przywara
2017-04-03 20:28 ` [PATCH v4 14/27] ARM: vGICv3: introduce basic ITS emulation bits Andre Przywara
2017-04-04 13:35   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 15/27] ARM: vITS: introduce translation table walks Andre Przywara
2017-04-04 15:59   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 16/27] ARM: vITS: handle CLEAR command Andre Przywara
2017-04-04 16:03   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 17/27] ARM: vITS: handle INT command Andre Przywara
2017-04-04 16:05   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 18/27] ARM: vITS: handle MAPC command Andre Przywara
2017-04-04 16:08   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 19/27] ARM: vITS: handle MAPD command Andre Przywara
2017-04-04 16:09   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 20/27] ARM: vITS: handle MAPTI command Andre Przywara
2017-04-04 16:22   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 21/27] ARM: vITS: handle MOVI command Andre Przywara
2017-04-04 16:37   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 22/27] ARM: vITS: handle DISCARD command Andre Przywara
2017-04-04 16:40   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 23/27] ARM: vITS: handle INV command Andre Przywara
2017-04-04 16:51   ` Julien Grall
2017-04-05 23:21     ` André Przywara
2017-04-03 20:28 ` [PATCH v4 24/27] ARM: vITS: handle INVALL command Andre Przywara
2017-04-04 17:00   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 25/27] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
2017-04-04 17:03   ` Julien Grall
2017-04-03 20:28 ` [PATCH v4 26/27] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
2017-04-03 20:28 ` [PATCH v4 27/27] ARM: vGIC: advertise LPI support Andre Przywara
2017-04-05 13:06   ` Julien Grall
2017-04-04 12:36 ` [PATCH v4 00/27] arm64: Dom0 ITS emulation Julien Grall

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9d7d3413-2b81-36cc-d983-e0abde117225@arm.com \
    --to=julien.grall@arm.com \
    --cc=andre.przywara@arm.com \
    --cc=sstabellini@kernel.org \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.