On 05/08/2016 09:14 PM, Shanker Donthineni wrote:
> Since device IDs are extremely sparse, the single, a.k.a flat table is
> not sufficient for the following two reasons.
>
> 1) According to ARM-GIC spec, ITS hw can access maximum of 256(pages)*
>    64K(pageszie) bytes. In the best case, it supports upto DEVid=21
>    sparse with minimum device table entry size 8bytes.
>
> 2) The maximum memory size that is possible without memblock depends on
>    MAX_ORDER. 4MB on 4K page size kernel with default MAX_ORDER, so it
>    supports DEVid range 19bits.
>
> The two-level device table feature brings us two advantages, the first
> is a very high possibility of supporting upto 32bit sparse, and the
> second one is the best utilization of memory allocation.
>
> The feature is enabled automatically during driver probe if a single
> ITS page is not adequate for flat table and the hardware is capable
> of two-level table walk.
>
> Signed-off-by: Shanker Donthineni <shank...@codeaurora.org>
> ---
>
> This patch is based on Marc Zyngier's branch 
> https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/log/?h=irq/irqchip-4.7
>
> I have tested the Indirection feature on Qualcomm Technologies QDF2XXX server 
> platform.
>
> Changes since v1:
>   Most of this patch has been rewritten after refactoring its_alloc_tables().
>   Always enable device two-level if the memory requirement is more than 
> PAGE_SIZE.
>   Fixed the coding bug that breaks on the BE machine.
>   Edited the commit text.
>
>  drivers/irqchip/irq-gic-v3-its.c | 100 
> ++++++++++++++++++++++++++++++++-------
>  1 file changed, 83 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3-its.c 
> b/drivers/irqchip/irq-gic-v3-its.c
> index b23e00c..27be792 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -938,6 +938,18 @@ retry_baser:
>       return 0;
>  }
>  
> +/**
> + * Find out whether an implemented baser register supports a single, flat 
> table
> + * or a two-level table by reading bit offset at '62' after writing '1' to 
> it.
> + */
> +static u64 its_baser_check_indirect(struct its_baser *baser)
> +{
> +     u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb;
> +
> +     writeq_relaxed(val | GITS_BASER_INDIRECT, baser->hwreg);
> +     return (readq_relaxed(baser->hwreg) & GITS_BASER_INDIRECT);
> +}
> +
>  static int its_alloc_tables(const char *node_name, struct its_node *its)
>  {
>       u64 typer = readq_relaxed(its->base + GITS_TYPER);
> @@ -964,6 +976,7 @@ static int its_alloc_tables(const char *node_name, struct 
> its_node *its)
>               u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
>               int order = get_order(psz);
>               struct its_baser *baser = its->tables + i;
> +             u64 indirect = 0;
>  
>               if (type == GITS_BASER_TYPE_NONE)
>                       continue;
> @@ -977,17 +990,27 @@ static int its_alloc_tables(const char *node_name, 
> struct its_node *its)
>                * Allocate as many entries as required to fit the
>                * range of device IDs that the ITS can grok... The ID
>                * space being incredibly sparse, this results in a
> -              * massive waste of memory.
> +              * massive waste of memory if two-level device table
> +              * feature is not supported by hardware.
>                *
>                * For other tables, only allocate a single page.
>                */
>               if (type == GITS_BASER_TYPE_DEVICE) {
> -                     /*
> -                      * 'order' was initialized earlier to the default page
> -                      * granule of the the ITS.  We can't have an allocation
> -                      * smaller than that.  If the requested allocation
> -                      * is smaller, round up to the default page granule.
> -                      */
> +                     if ((entry_size << ids) > psz)
> +                             indirect = its_baser_check_indirect(baser);
> +
> +                     if (indirect) {
> +                             /*
> +                              * The size of the lvl2 table is equal to ITS
> +                              * page size which is 'psz'. For computing lvl1
> +                              * table size, subtract ID bits that sparse
> +                              * lvl2 table from 'ids' which is reported by
> +                              * ITS hardware times lvl1 table entry size.
> +                              */
> +                             ids -= ilog2(psz / entry_size);
> +                             entry_size = GITS_LVL1_ENTRY_SIZE;
> +                     }
> +
>                       order = max(get_order(entry_size << ids), order);
>                       if (order >= MAX_ORDER) {
>                               order = MAX_ORDER - 1;
> @@ -997,7 +1020,7 @@ static int its_alloc_tables(const char *node_name, 
> struct its_node *its)
>                       }
>               }
>  
> -             err = its_baser_setup(its, baser, order, 0);
> +             err = its_baser_setup(its, baser, order, indirect);
>               if (err < 0) {
>                       its_free_tables(its);
>                       return err;
> @@ -1187,10 +1210,60 @@ static struct its_baser *its_get_baser(struct 
> its_node *its, u32 type)
>       return NULL;
>  }
>  
> +static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
> +{
> +     struct its_baser *baser;
> +     struct page *page;
> +     u32 entry_size, idx;
> +     u64 *table;
> +
> +     baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
> +
> +     /* Don't allow 'dev_id' that exceeds ITS hardware limit */
> +     if (!baser && (ilog2(dev_id) >= its->device_ids))
> +             return false;
> +
I have missed this change, will be fixed in v3 patchset.

    if (!baser) {
        if (ilog2(dev_id) >= its->device_ids))
            return false;
        return true;
    }

> +     /* Don't allow 'dev_id' that exceeds single, flat table limit */
> +     entry_size = GITS_BASER_ENTRY_SIZE(baser->val);
> +     if (!(baser->val & GITS_BASER_INDIRECT)) {
> +             if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) / entry_size))
> +                     return false;
> +             return true;
> +     }
> +
> +     /* Compute Level1 table index & check if that exceeds table range */
> +     idx = dev_id >> ilog2(baser->psz / entry_size);
> +     if (idx >= (PAGE_ORDER_TO_SIZE(baser->order) / GITS_LVL1_ENTRY_SIZE))
> +             return false;
> +
> +     table = baser->base;
> +
> +     /* Allocate memory for Level2 table */
> +     if (!table[idx]) {
> +             page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 
> get_order(baser->psz));
> +             if (!page)
> +                     return false;
> +
> +             /* Flush memory to PoC if hardware doesn't support coherency */
> +             if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
> +                     __flush_dcache_area(page_address(page), baser->psz);
> +
> +             table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID);
> +
> +             /* Flush memory to PoC if hardware doesn't support coherency */
> +             if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
> +                     __flush_dcache_area(table + idx, GITS_LVL1_ENTRY_SIZE);
> +
> +             /* Ensure updated table contents are visible to ITS hardware */
> +             dsb(sy);
> +     }
> +
> +     return true;
> +}
> +
>  static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
>                                           int nvecs)
>  {
> -     struct its_baser *baser;
>       struct its_device *dev;
>       unsigned long *lpi_map;
>       unsigned long flags;
> @@ -1201,14 +1274,7 @@ static struct its_device *its_create_device(struct 
> its_node *its, u32 dev_id,
>       int nr_ites;
>       int sz;
>  
> -     baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
> -
> -     /* Don't allow 'dev_id' that exceeds single, flat table limit */
> -     if (baser) {
> -             if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) /
> -                           GITS_BASER_ENTRY_SIZE(baser->val)))
> -                     return NULL;
> -     } else if (ilog2(dev_id) >= its->device_ids)
> +     if (!its_alloc_device_table(its, dev_id))
>               return NULL;
>  
>       dev = kzalloc(sizeof(*dev), GFP_KERNEL);

-- 
Shanker Donthineni
Qualcomm Technologies, Inc. on behalf of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux 
Foundation Collaborative Project

Reply via email to