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

Re: [PATCH v2 8/8] xen/arm: Add support for SMMUv3 driver


  • To: Stefano Stabellini <sstabellini@xxxxxxxxxx>
  • From: Rahul Singh <Rahul.Singh@xxxxxxx>
  • Date: Wed, 2 Dec 2020 19:26:48 +0000
  • Accept-language: en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=jiEzk72JjC318bRSvD79N8SqTxrEm5R+ax7gAYFg3B0=; b=bgJUDkgCF0q2buJGvSJYHL314A3swssmUbJbz+dEUOlDPWAZirYWC5LpjeHG+cDKYjttLBLBMiRMrlPam6OFiPAzugc4whikQli/ycBQeu+TTfudOhZv2G3njBggz/COXAsBF9d2ybAgZMt/KnxuWE/mF8KAdaIONVbxZjj6iA07PkyUkT7V+X66XEw8sVk6MIJVb7zlh6PQcmUH9B1VnoAftijtL1kyOy3kBtkUeF7BytC0lWkF1xxBL+TpyrcbD2QLjenkXCabgmr3Pkcak9di2O0RnJv4qv/iVGlCqKxHSfnv+FFRE+MjSUl2irE11giPLkkiSBL/rdg+WqPcbw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=S7qunehnMJX49K+aC1JCi+iqvorJueVRR4NVywkJtQNHAKR69yXerWP6Ju9A487A4zIOyEBM2ozczX1RkRJpjLof0OQttIKaw5Sr7r5VaxJrm+Qo3i/R3b4xjdKTFxwAFGePu137xgzrEgJEJAxK7LWieaNGHZg1t740d5WKhoPAJhYyn/Id4Nn3ktjluycfmHNuLn3cbc51Jdj5z4cq4RkO+MDQRZGhEb2TVH87EcIl3HENqzR2O/2gSUyvzSVzhEPpyJzY4fhEg1mbUl8xyjoep9aeFByuxwWRQx4sjDkMHw86t/525zk0R28chisR2EV9oEKHbm3+I7LtGrILJA==
  • Authentication-results-original: kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=none action=none header.from=arm.com;
  • Cc: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Bertrand Marquis <Bertrand.Marquis@xxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, George Dunlap <george.dunlap@xxxxxxxxxx>, Ian Jackson <iwj@xxxxxxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Julien Grall <julien@xxxxxxx>, Wei Liu <wl@xxxxxxx>, Paul Durrant <paul@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>
  • Delivery-date: Wed, 02 Dec 2020 19:27:30 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Nodisclaimer: true
  • Original-authentication-results: kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=none action=none header.from=arm.com;
  • Thread-index: AQHWxBX/ttp8YMNqT0KunkbXuSldmKnjI/iAgADj3wCAADIkAA==
  • Thread-topic: [PATCH v2 8/8] xen/arm: Add support for SMMUv3 driver

Hi Stefano,

> On 2 Dec 2020, at 4:27 pm, Rahul Singh <Rahul.Singh@xxxxxxx> wrote:
> 
> Hello Stefano,
> 
>> On 2 Dec 2020, at 2:51 am, Stefano Stabellini <sstabellini@xxxxxxxxxx> wrote:
>> 
>> On Thu, 26 Nov 2020, Rahul Singh wrote:
>>> Add support for ARM architected SMMUv3 implementation. It is based on
>>> the Linux SMMUv3 driver.
>>> 
>>> Major differences with regard to Linux driver are as follows:
>>> 1. Only Stage-2 translation is supported as compared to the Linux driver
>>>  that supports both Stage-1 and Stage-2 translations.
>>> 2. Use P2M  page table instead of creating one as SMMUv3 has the
>>>  capability to share the page tables with the CPU.
>>> 3. Tasklets are used in place of threaded IRQ's in Linux for event queue
>>>  and priority queue IRQ handling.
>>> 4. Latest version of the Linux SMMUv3 code implements the commands queue
>>>  access functions based on atomic operations implemented in Linux.
>>>  Atomic functions used by the commands queue access functions are not
>>>  implemented in XEN therefore we decided to port the earlier version
>>>  of the code. Once the proper atomic operations will be available in
>>>  XEN the driver can be updated.
>>> 5. Driver is currently supported as Tech Preview.
>> 
>> This patch is big and was difficult to review, nonetheless I tried :-)
> 
> Thanks again for reviewing the code.
>> 
>> That said, the code is self-contained, marked as Tech Preview, and not
>> at risk of making other things unstable, so it is low risk to accept it.
>> 
>> Comments below.
>> 
>> 
>>> Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
>>> ---
>>> MAINTAINERS                           |   6 +
>>> SUPPORT.md                            |   1 +
>>> xen/drivers/passthrough/Kconfig       |  10 +
>>> xen/drivers/passthrough/arm/Makefile  |   1 +
>>> xen/drivers/passthrough/arm/smmu-v3.c | 986 +++++++++++++++++++++-----
>>> 5 files changed, 814 insertions(+), 190 deletions(-)
>>> 
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index dab38a6a14..1d63489eec 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -249,6 +249,12 @@ F:     xen/include/asm-arm/
>>> F:  xen/include/public/arch-arm/
>>> F:  xen/include/public/arch-arm.h
>>> 
>>> +ARM SMMUv3
>>> +M: Bertrand Marquis <bertrand.marquis@xxxxxxx>
>>> +M: Rahul Singh <rahul.singh@xxxxxxx>
>>> +S: Supported
>>> +F: xen/drivers/passthrough/arm/smmu-v3.c
>>> +
>>> Change Log
>>> M:  Paul Durrant <paul@xxxxxxx>
>>> R:  Community Manager <community.manager@xxxxxxxxxxxxxx>
>>> diff --git a/SUPPORT.md b/SUPPORT.md
>>> index ab02aca5f4..e402c7202d 100644
>>> --- a/SUPPORT.md
>>> +++ b/SUPPORT.md
>>> @@ -68,6 +68,7 @@ For the Cortex A57 r0p0 - r1p1, see Errata 832075.
>>>    Status, ARM SMMUv1: Supported, not security supported
>>>    Status, ARM SMMUv2: Supported, not security supported
>>>    Status, Renesas IPMMU-VMSA: Supported, not security supported
>>> +    Status, ARM SMMUv3: Tech Preview
>>> 
>>> ### ARM/GICv3 ITS
>>> 
>>> diff --git a/xen/drivers/passthrough/Kconfig 
>>> b/xen/drivers/passthrough/Kconfig
>>> index 0036007ec4..5b71c59f47 100644
>>> --- a/xen/drivers/passthrough/Kconfig
>>> +++ b/xen/drivers/passthrough/Kconfig
>>> @@ -13,6 +13,16 @@ config ARM_SMMU
>>>       Say Y here if your SoC includes an IOMMU device implementing the
>>>       ARM SMMU architecture.
>>> 
>>> +config ARM_SMMU_V3
>>> +   bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support" if EXPERT
>>> +   depends on ARM_64
>>> +   ---help---
>>> +    Support for implementations of the ARM System MMU architecture
>>> +    version 3.
>>> +
>>> +    Say Y here if your system includes an IOMMU device implementing
>>> +    the ARM SMMUv3 architecture.
>>> +
>>> config IPMMU_VMSA
>>>     bool "Renesas IPMMU-VMSA found in R-Car Gen3 SoCs"
>>>     depends on ARM_64
>>> diff --git a/xen/drivers/passthrough/arm/Makefile 
>>> b/xen/drivers/passthrough/arm/Makefile
>>> index fcd918ea3e..c5fb3b58a5 100644
>>> --- a/xen/drivers/passthrough/arm/Makefile
>>> +++ b/xen/drivers/passthrough/arm/Makefile
>>> @@ -1,3 +1,4 @@
>>> obj-y += iommu.o iommu_helpers.o iommu_fwspec.o
>>> obj-$(CONFIG_ARM_SMMU) += smmu.o
>>> obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
>>> +obj-$(CONFIG_ARM_SMMU_V3) += smmu-v3.o
>>> diff --git a/xen/drivers/passthrough/arm/smmu-v3.c 
>>> b/xen/drivers/passthrough/arm/smmu-v3.c
>>> index 55d1cba194..8f2337e7f2 100644
>>> --- a/xen/drivers/passthrough/arm/smmu-v3.c
>>> +++ b/xen/drivers/passthrough/arm/smmu-v3.c
>>> @@ -2,36 +2,280 @@
>>> /*
>>> * IOMMU API for ARM architected SMMUv3 implementations.
>>> *
>>> - * Copyright (C) 2015 ARM Limited
>>> + * Based on Linux's SMMUv3 driver:
>>> + *    drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
>>> + *    commit: 951cbbc386ff01b50da4f46387e994e81d9ab431
>>> + * and Xen's SMMU driver:
>>> + *    xen/drivers/passthrough/arm/smmu.c
>>> *
>>> - * Author: Will Deacon <will.deacon@xxxxxxx>
>>> + * Copyright (C) 2015 ARM Limited Will Deacon <will.deacon@xxxxxxx>
>>> *
>>> - * This driver is powered by bad coffee and bombay mix.
>>> + * Copyright (C) 2020 Arm Ltd.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + *
>>> + */
>>> +
>>> +#include <xen/acpi.h>
>>> +#include <xen/config.h>
>>> +#include <xen/delay.h>
>>> +#include <xen/errno.h>
>>> +#include <xen/err.h>
>>> +#include <xen/irq.h>
>>> +#include <xen/lib.h>
>>> +#include <xen/list.h>
>>> +#include <xen/mm.h>
>>> +#include <xen/rbtree.h>
>>> +#include <xen/sched.h>
>>> +#include <xen/sizes.h>
>>> +#include <xen/vmap.h>
>>> +#include <asm/atomic.h>
>>> +#include <asm/device.h>
>>> +#include <asm/io.h>
>>> +#include <asm/platform.h>
>>> +#include <asm/iommu_fwspec.h>
>>> +
>>> +/* Linux compatibility functions. */
>>> +typedef paddr_t dma_addr_t;
>>> +typedef unsigned int gfp_t;
>>> +
>>> +#define platform_device device
>>> +
>>> +#define GFP_KERNEL 0
>>> +
>>> +/* Alias to Xen device tree helpers */
>>> +#define device_node dt_device_node
>>> +#define of_phandle_args dt_phandle_args
>>> +#define of_device_id dt_device_match
>>> +#define of_match_node dt_match_node
>>> +#define of_property_read_u32(np, pname, out) (!dt_property_read_u32(np, 
>>> pname, out))
>>> +#define of_property_read_bool dt_property_read_bool
>>> +#define of_parse_phandle_with_args dt_parse_phandle_with_args
>> 
>> Given all the changes to the file by the previous patches we are
>> basically fully (or almost fully) adapting this code to Xen.
>> 
>> So at that point I wonder if we should just as well make these changes
>> (e.g. s/of_phandle_args/dt_phandle_args/g) to the code too.
> 
> Yes, you are right that is why in the first version of the patch I modifying 
> the code to make it fully XEN compatible. But in this patch series I use the 
> Linux compatibility function to have Linux code unmodified.I also prefer to 
> make changes (s/of_phandle_args/dt_phandle_args/g).
> 
>> 
>> Julien, Rahul, what do you guys think? 
>> 
>> 
>>> +/* Alias to Xen lock functions */
>> 
>> I think this deserves at least one statement to explain why it is OK.
> 
> Ack. I Will add the comment.
>> 
>> Also, a similar comment to the one above: maybe we should add a couple
>> of more preparation patches to s/mutex/spinlock/g and change the time
>> and allocation functions too.
>> 
>> 
>>> +#define mutex spinlock
>>> +#define mutex_init spin_lock_init
>>> +#define mutex_lock spin_lock
>>> +#define mutex_unlock spin_unlock
>>> +
>>> +/* Alias to Xen time functions */
>>> +#define ktime_t s_time_t
>>> +#define ktime_get()             (NOW())
>>> +#define ktime_add_us(t,i)       (t + MICROSECS(i))
>>> +#define ktime_compare(t,i)      (t > (i))
>>> +
>>> +/* Alias to Xen allocation helpers */
>>> +#define kzalloc(size, flags)    _xzalloc(size, sizeof(void *))
>>> +#define kfree xfree
>>> +#define devm_kzalloc(dev, size, flags)  _xzalloc(size, sizeof(void *))
>>> +
>>> +/* Device logger functions */
>>> +#define dev_name(dev) dt_node_full_name(dev->of_node)
>>> +#define dev_dbg(dev, fmt, ...)      \
>>> +    printk(XENLOG_DEBUG "SMMUv3: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
>>> +#define dev_notice(dev, fmt, ...)   \
>>> +    printk(XENLOG_INFO "SMMUv3: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
>>> +#define dev_warn(dev, fmt, ...)     \
>>> +    printk(XENLOG_WARNING "SMMUv3: %s: " fmt, dev_name(dev), ## 
>>> __VA_ARGS__)
>>> +#define dev_err(dev, fmt, ...)      \
>>> +    printk(XENLOG_ERR "SMMUv3: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
>>> +#define dev_info(dev, fmt, ...)     \
>>> +    printk(XENLOG_INFO "SMMUv3: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
>>> +#define dev_err_ratelimited(dev, fmt, ...)      \
>>> +    printk(XENLOG_ERR "SMMUv3: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
>>> +
>>> +/*
>>> + * Periodically poll an address and wait between reads in us until a
>>> + * condition is met or a timeout occurs.
>> 
>> It would be good to add a statement to point out what the return value
>> is going to be.
> 
> Ack. 
>> 
>> 
>>> + */
>>> +#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \
>>> +({ \
>>> +     s_time_t deadline = NOW() + MICROSECS(timeout_us); \
>>> +     for (;;) { \
>>> +        (val) = op(addr); \
>>> +        if (cond) \
>>> +            break; \
>>> +        if (NOW() > deadline) { \
>>> +            (val) = op(addr); \
>>> +            break; \
>>> +        } \
>>> +        udelay(sleep_us); \
>>> +     } \
>>> +     (cond) ? 0 : -ETIMEDOUT; \
>>> +})
>>> +
>>> +#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
>>> +    readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, 
>>> timeout_us)
>>> +
>>> +#define FIELD_PREP(_mask, _val)         \
>>> +    (((typeof(_mask))(_val) << (__builtin_ffsll(_mask) - 1)) & (_mask))
>> 
>> Let's add the definition of ffsll to bitops.h
> 
> Ok. 
>> 
>> 
>>> +#define FIELD_GET(_mask, _reg)          \
>>> +    (typeof(_mask))(((_reg) & (_mask)) >> (__builtin_ffsll(_mask) - 1))
>>> +
>>> +#define WRITE_ONCE(x, val)                  \
>>> +do {                                        \
>>> +    *(volatile typeof(x) *)&(x) = (val);    \
>>> +} while (0)
>> 
>> maybe we should define this in xen/include/xen/lib.h
> 
> Ok. 
>> 
>> 
>>> +
>>> +/* Xen: Stub out DMA domain related functions */
>>> +#define iommu_get_dma_cookie(dom) 0
>>> +#define iommu_put_dma_cookie(dom)
>> 
>> Shouldn't we remove any call to iommu_get_dma_cookie and
>> iommu_put_dma_cookie in one of the previous patches?
> 
> Ack. 
>> 
>> 
>>> +/*
>>> + * Helpers for DMA allocation. Just the function name is reused for
>>> + * porting code, these allocation are not managed allocations
>>> */
>>> +static void *dmam_alloc_coherent(struct device *dev, size_t size,
>>> +                                 paddr_t *dma_handle, gfp_t gfp)
>>> +{
>>> +    void *vaddr;
>>> +    unsigned long alignment = size;
>>> +
>>> +    /*
>>> +     * _xzalloc requires that the (align & (align -1)) = 0. Most of the
>>> +     * allocations in SMMU code should send the right value for size. In
>>> +     * case this is not true print a warning and align to the size of a
>>> +     * (void *)
>>> +     */
>>> +    if ( size & (size - 1) )
>>> +    {
>>> +        printk(XENLOG_WARNING "SMMUv3: Fixing alignment for the DMA 
>>> buffer\n");
>>> +        alignment = sizeof(void *);
>>> +    }
>>> +
>>> +    vaddr = _xzalloc(size, alignment);
>>> +    if ( !vaddr )
>>> +    {
>>> +        printk(XENLOG_ERR "SMMUv3: DMA allocation failed\n");
>>> +        return NULL;
>>> +    }
>>> +
>>> +    *dma_handle = virt_to_maddr(vaddr);
>>> +
>>> +    return vaddr;
>>> +}
>>> +
>>> +/* Xen: Type definitions for iommu_domain */
>>> +#define IOMMU_DOMAIN_UNMANAGED 0
>>> +#define IOMMU_DOMAIN_DMA 1
>>> +#define IOMMU_DOMAIN_IDENTITY 2
>>> +
>>> +/* Xen specific code. */
>>> +struct iommu_domain {
>>> +    /* Runtime SMMU configuration for this iommu_domain */
>>> +    atomic_t ref;
>>> +    /*
>>> +     * Used to link iommu_domain contexts for a same domain.
>>> +     * There is at least one per-SMMU to used by the domain.
>>> +     */
>>> +    struct list_head    list;
>>> +};
>>> +
>>> +/* Describes information required for a Xen domain */
>>> +struct arm_smmu_xen_domain {
>>> +    spinlock_t      lock;
>>> +
>>> +    /* List of iommu domains associated to this domain */
>>> +    struct list_head    contexts;
>>> +};
>>> +
>>> +/*
>>> + * Information about each device stored in dev->archdata.iommu
>>> + * The dev->archdata.iommu stores the iommu_domain (runtime configuration 
>>> of
>>> + * the SMMU).
>>> + */
>>> +struct arm_smmu_xen_device {
>>> +    struct iommu_domain *domain;
>>> +};
>> 
>> Do we need both struct arm_smmu_xen_device and struct iommu_domain?
>> 
> 
> No we don’t need both. I will remove the struct arm_smmu_xen_device. 
> 
>> 
>>> +/* Keep a list of devices associated with this driver */
>>> +static DEFINE_SPINLOCK(arm_smmu_devices_lock);
>>> +static LIST_HEAD(arm_smmu_devices);
>>> +
>>> +
>>> +static inline void *dev_iommu_priv_get(struct device *dev)
>>> +{
>>> +    struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>>> +
>>> +    return fwspec && fwspec->iommu_priv ? fwspec->iommu_priv : NULL;
>>> +}
>>> +
>>> +static inline void dev_iommu_priv_set(struct device *dev, void *priv)
>>> +{
>>> +    struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>>> +
>>> +    fwspec->iommu_priv = priv;
>>> +}
>>> +
>>> +int dt_property_match_string(const struct dt_device_node *np,
>>> +                             const char *propname, const char *string)
>>> +{
>>> +    const struct dt_property *dtprop = dt_find_property(np, propname, 
>>> NULL);
>>> +    size_t l;
>>> +    int i;
>>> +    const char *p, *end;
>>> +
>>> +    if ( !dtprop )
>>> +        return -EINVAL;
>>> +
>>> +    if ( !dtprop->value )
>>> +        return -ENODATA;
>>> +
>>> +    p = dtprop->value;
>>> +    end = p + dtprop->length;
>>> +
>>> +    for ( i = 0; p < end; i++, p += l )
>>> +    {
>>> +        l = strnlen(p, end - p) + 1;
>>> +
>>> +        if ( p + l > end )
>>> +            return -EILSEQ;
>>> +
>>> +        if ( strcmp(string, p) == 0 )
>>> +            return i; /* Found it; return index */
>>> +    }
>>> +
>>> +    return -ENODATA;
>>> +}
>> 
>> I think you should either use dt_property_read_string or move the
>> implementation of dt_property_match_string to xen/common/device_tree.c
>> (in which case I suggest to do it in a separate patch.)
> 
> I will prefer to move the code to xen/common/device_tree.c as it might help 
> in future to use.
>> 
>> 
>> I'd just use dt_property_read_string.
>> 
>> 
>> 
>>> +static int platform_get_irq_byname_optional(struct device *dev,
>>> +                                            const char *name)
>>> +{
>>> +    int index, ret;
>>> +    struct dt_device_node *np  = dev_to_dt(dev);
>>> +
>>> +    if ( unlikely(!name) )
>>> +        return -EINVAL;
>>> +
>>> +    index = dt_property_match_string(np, "interrupt-names", name);
>>> +    if ( index < 0 )
>>> +    {
>>> +        dev_info(dev, "IRQ %s not found\n", name);
>>> +        return index;
>>> +    }
>>> 
>>> -#include <linux/acpi.h>
>>> -#include <linux/acpi_iort.h>
>>> -#include <linux/bitfield.h>
>>> -#include <linux/bitops.h>
>>> -#include <linux/crash_dump.h>
>>> -#include <linux/delay.h>
>>> -#include <linux/dma-iommu.h>
>>> -#include <linux/err.h>
>>> -#include <linux/interrupt.h>
>>> -#include <linux/io-pgtable.h>
>>> -#include <linux/iommu.h>
>>> -#include <linux/iopoll.h>
>>> -#include <linux/module.h>
>>> -#include <linux/msi.h>
>>> -#include <linux/of.h>
>>> -#include <linux/of_address.h>
>>> -#include <linux/of_iommu.h>
>>> -#include <linux/of_platform.h>
>>> -#include <linux/pci.h>
>>> -#include <linux/pci-ats.h>
>>> -#include <linux/platform_device.h>
>>> -
>>> -#include <linux/amba/bus.h>
>>> +    ret = platform_get_irq(np, index);
>>> +    if ( ret < 0 )
>>> +    {
>>> +        dev_err(dev, "failed to get irq index %d\n", index);
>>> +        return -ENODEV;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/* Start of Linux SMMUv3 code */
>>> 
>>> /* MMIO registers */
>>> #define ARM_SMMU_IDR0                       0x0
>>> @@ -507,6 +751,7 @@ struct arm_smmu_s2_cfg {
>>>     u16                             vmid;
>>>     u64                             vttbr;
>>>     u64                             vtcr;
>>> +   struct domain           *domain;
>>> };
>>> 
>>> struct arm_smmu_strtab_cfg {
>>> @@ -567,8 +812,13 @@ struct arm_smmu_device {
>>> 
>>>     struct arm_smmu_strtab_cfg      strtab_cfg;
>>> 
>>> -   /* IOMMU core code handle */
>>> -   struct iommu_device             iommu;
>>> +   /* Need to keep a list of SMMU devices */
>>> +   struct list_head                devices;
>>> +
>>> +   /* Tasklets for handling evts/faults and pci page request IRQs*/
>>> +   struct tasklet          evtq_irq_tasklet;
>>> +   struct tasklet          priq_irq_tasklet;
>>> +   struct tasklet          combined_irq_tasklet;
>>> };
>>> 
>>> /* SMMU private data for each master */
>>> @@ -1110,7 +1360,7 @@ static int arm_smmu_init_l2_strtab(struct 
>>> arm_smmu_device *smmu, u32 sid)
>>> }
>>> 
>>> /* IRQ and event handlers */
>>> -static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
>>> +static void arm_smmu_evtq_thread(void *dev)
>> 
>> I think we shouldn't call it a thread given that's a tasklet. We should
>> rename the function or it will be confusing
> 
> Ok. 
>> 
>>> {
>>>     int i;
>>>     struct arm_smmu_device *smmu = dev;
>>> @@ -1140,7 +1390,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void 
>>> *dev)
>>>     /* Sync our overflow flag, as we believe we're up to speed */
>>>     llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) |
>>>                 Q_IDX(llq, llq->cons);
>>> -   return IRQ_HANDLED;
>>> }
>>> 
>>> static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
>>> @@ -1181,7 +1430,7 @@ static void arm_smmu_handle_ppr(struct 
>>> arm_smmu_device *smmu, u64 *evt)
>>>     }
>>> }
>>> 
>>> -static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
>>> +static void arm_smmu_priq_thread(void *dev)
>> 
>> same here
> 
> Ok. 
>> 
>> 
>>> {
>>>     struct arm_smmu_device *smmu = dev;
>>>     struct arm_smmu_queue *q = &smmu->priq.q;
>>> @@ -1200,12 +1449,12 @@ static irqreturn_t arm_smmu_priq_thread(int irq, 
>>> void *dev)
>>>     llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) |
>>>                   Q_IDX(llq, llq->cons);
>>>     queue_sync_cons_out(q);
>>> -   return IRQ_HANDLED;
>>> }
>>> 
>>> static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
>>> 
>>> -static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
>>> +static void arm_smmu_gerror_handler(int irq, void *dev,
>>> +                           struct cpu_user_regs *regs)
>>> {
>>>     u32 gerror, gerrorn, active;
>>>     struct arm_smmu_device *smmu = dev;
>>> @@ -1215,7 +1464,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, 
>>> void *dev)
>>> 
>>>     active = gerror ^ gerrorn;
>>>     if (!(active & GERROR_ERR_MASK))
>>> -           return IRQ_NONE; /* No errors pending */
>>> +           return; /* No errors pending */
>>> 
>>>     dev_warn(smmu->dev,
>>>              "unexpected global error reported (0x%08x), this could be 
>>> serious\n",
>>> @@ -1248,26 +1497,42 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, 
>>> void *dev)
>>>             arm_smmu_cmdq_skip_err(smmu);
>>> 
>>>     writel(gerror, smmu->base + ARM_SMMU_GERRORN);
>>> -   return IRQ_HANDLED;
>>> }
>>> 
>>> -static irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev)
>>> +static void arm_smmu_combined_irq_handler(int irq, void *dev,
>>> +                           struct cpu_user_regs *regs)
>>> +{
>>> +   struct arm_smmu_device *smmu = (struct arm_smmu_device *)dev;
>>> +
>>> +   arm_smmu_gerror_handler(irq, dev, regs);
>>> +
>>> +   tasklet_schedule(&(smmu->combined_irq_tasklet));
>>> +}
>>> +
>>> +static void arm_smmu_combined_irq_thread(void *dev)
>>> {
>>>     struct arm_smmu_device *smmu = dev;
>>> 
>>> -   arm_smmu_evtq_thread(irq, dev);
>>> +   arm_smmu_evtq_thread(dev);
>>>     if (smmu->features & ARM_SMMU_FEAT_PRI)
>>> -           arm_smmu_priq_thread(irq, dev);
>>> -
>>> -   return IRQ_HANDLED;
>>> +           arm_smmu_priq_thread(dev);
>>> }
>>> 
>>> -static irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev)
>>> +static void arm_smmu_evtq_irq_tasklet(int irq, void *dev,
>>> +                           struct cpu_user_regs *regs)
>>> {
>>> -   arm_smmu_gerror_handler(irq, dev);
>>> -   return IRQ_WAKE_THREAD;
>>> +   struct arm_smmu_device *smmu = (struct arm_smmu_device *)dev;
>>> +
>>> +   tasklet_schedule(&(smmu->evtq_irq_tasklet));
>>> }
>>> 
>>> +static void arm_smmu_priq_irq_tasklet(int irq, void *dev,
>>> +                           struct cpu_user_regs *regs)
>>> +{
>>> +   struct arm_smmu_device *smmu = (struct arm_smmu_device *)dev;
>>> +
>>> +   tasklet_schedule(&(smmu->priq_irq_tasklet));
>>> +}
>>> 
>>> /* IO_PGTABLE API */
>>> static void arm_smmu_tlb_inv_context(void *cookie)
>>> @@ -1354,27 +1619,69 @@ static void arm_smmu_domain_free(struct 
>>> iommu_domain *domain)
>>> }
>>> 
>>> static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
>>> -                                  struct arm_smmu_master *master,
>>> -                                  struct io_pgtable_cfg *pgtbl_cfg)
>>> +                                  struct arm_smmu_master *master)
>>> {
>>>     int vmid;
>>> +   u64 reg;
>>>     struct arm_smmu_device *smmu = smmu_domain->smmu;
>>>     struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
>>> 
>>> +   /* VTCR */
>>> +   reg = VTCR_RES1 | VTCR_SH0_IS | VTCR_IRGN0_WBWA | VTCR_ORGN0_WBWA;
>>> +
>>> +   switch (PAGE_SIZE) {
>>> +   case SZ_4K:
>>> +           reg |= VTCR_TG0_4K;
>>> +           break;
>>> +   case SZ_16K:
>>> +           reg |= VTCR_TG0_16K;
>>> +           break;
>>> +   case SZ_64K:
>>> +           reg |= VTCR_TG0_4K;
>>> +           break;
>>> +   }
>>> +
>>> +   switch (smmu->oas) {
>>> +   case 32:
>>> +           reg |= VTCR_PS(_AC(0x0,ULL));
>>> +           break;
>>> +   case 36:
>>> +           reg |= VTCR_PS(_AC(0x1,ULL));
>>> +           break;
>>> +   case 40:
>>> +           reg |= VTCR_PS(_AC(0x2,ULL));
>>> +           break;
>>> +   case 42:
>>> +           reg |= VTCR_PS(_AC(0x3,ULL));
>>> +           break;
>>> +   case 44:
>>> +           reg |= VTCR_PS(_AC(0x4,ULL));
>>> +           break;
>>> +           case 48:
>>> +           reg |= VTCR_PS(_AC(0x5,ULL));
>>> +           break;
>>> +   case 52:
>>> +           reg |= VTCR_PS(_AC(0x6,ULL));
>>> +           break;
>>> +   }
>>> +
>>> +   reg |= VTCR_T0SZ(64ULL - smmu->ias);
>>> +   reg |= VTCR_SL0(0x2);
>>> +   reg |= VTCR_VS;
>>> +
>>> +   cfg->vtcr   = reg;
>>> +
>>>     vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
>>>     if (vmid < 0)
>>>             return vmid;
>>> +   cfg->vmid  = (u16)vmid;
>>> +
>>> +   cfg->vttbr  = page_to_maddr(cfg->domain->arch.p2m.root);
>>> +
>>> +   printk(XENLOG_DEBUG
>>> +              "SMMUv3: d%u: vmid 0x%x vtcr 0x%"PRIpaddr" p2maddr 
>>> 0x%"PRIpaddr"\n",
>>> +              cfg->domain->domain_id, cfg->vmid, cfg->vtcr, cfg->vttbr);
>>> 
>>> -   vtcr = &pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
>>> -   cfg->vmid       = (u16)vmid;
>>> -   cfg->vttbr      = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
>>> -   cfg->vtcr       = FIELD_PREP(STRTAB_STE_2_VTCR_S2T0SZ, vtcr->tsz) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2SL0, vtcr->sl) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2IR0, vtcr->irgn) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2OR0, vtcr->orgn) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2SH0, vtcr->sh) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) |
>>> -                     FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps);
>>>     return 0;
>>> }
>>> 
>>> @@ -1382,28 +1689,12 @@ static int arm_smmu_domain_finalise(struct 
>>> iommu_domain *domain,
>>>                                 struct arm_smmu_master *master)
>>> {
>>>     int ret;
>>> -   unsigned long ias, oas;
>>> -   int (*finalise_stage_fn)(struct arm_smmu_domain *,
>>> -                            struct arm_smmu_master *,
>>> -                            struct io_pgtable_cfg *);
>>>     struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
>>> -   struct arm_smmu_device *smmu = smmu_domain->smmu;
>>> 
>>>     /* Restrict the stage to what we can actually support */
>>>     smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
>>> 
>>> -   switch (smmu_domain->stage) {
>>> -   case ARM_SMMU_DOMAIN_NESTED:
>>> -   case ARM_SMMU_DOMAIN_S2:
>>> -           ias = smmu->ias;
>>> -           oas = smmu->oas;
>>> -           finalise_stage_fn = arm_smmu_domain_finalise_s2;
>>> -           break;
>>> -   default:
>>> -           return -EINVAL;
>>> -   }
>>> -
>>> -   ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg);
>>> +   ret = arm_smmu_domain_finalise_s2(smmu_domain, master);
>>>     if (ret < 0) {
>>>             return ret;
>>>     }
>>> @@ -1553,7 +1844,8 @@ static int arm_smmu_init_one_queue(struct 
>>> arm_smmu_device *smmu,
>>>             return -ENOMEM;
>>>     }
>>> 
>>> -   if (!WARN_ON(q->base_dma & (qsz - 1))) {
>>> +   WARN_ON(q->base_dma & (qsz - 1));
>>> +   if (unlikely(q->base_dma & (qsz - 1))) {
>>>             dev_info(smmu->dev, "allocated %u entries for %s\n",
>>>                      1 << q->llq.max_n_shift, name);
>> 
>> We don't need both the WARNING and the dev_info. you could turn the
>> dev_warn / XENLOG_WARNING.
>> 
> Ack.
> 
>> 
>>>     }
>>> @@ -1758,9 +2050,7 @@ static void arm_smmu_setup_unique_irqs(struct 
>>> arm_smmu_device *smmu)
>>>     /* Request interrupt lines */
>>>     irq = smmu->evtq.q.irq;
>>>     if (irq) {
>>> -           ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
>>> -                                           arm_smmu_evtq_thread,
>>> -                                           IRQF_ONESHOT,
>>> +           ret = request_irq(irq, 0, arm_smmu_evtq_irq_tasklet,
>>>                                             "arm-smmu-v3-evtq", smmu);
>>>             if (ret < 0)
>>>                     dev_warn(smmu->dev, "failed to enable evtq irq\n");
>>> @@ -1770,8 +2060,8 @@ static void arm_smmu_setup_unique_irqs(struct 
>>> arm_smmu_device *smmu)
>>> 
>>>     irq = smmu->gerr_irq;
>>>     if (irq) {
>>> -           ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
>>> -                                  0, "arm-smmu-v3-gerror", smmu);
>>> +           ret = request_irq(irq, 0, arm_smmu_gerror_handler,
>>> +                                           "arm-smmu-v3-gerror", smmu);
>>>             if (ret < 0)
>>>                     dev_warn(smmu->dev, "failed to enable gerror irq\n");
>>>     } else {
>>> @@ -1781,11 +2071,8 @@ static void arm_smmu_setup_unique_irqs(struct 
>>> arm_smmu_device *smmu)
>>>     if (smmu->features & ARM_SMMU_FEAT_PRI) {
>>>             irq = smmu->priq.q.irq;
>>>             if (irq) {
>>> -                   ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
>>> -                                                   arm_smmu_priq_thread,
>>> -                                                   IRQF_ONESHOT,
>>> -                                                   "arm-smmu-v3-priq",
>>> -                                                   smmu);
>>> +                   ret = request_irq(irq, 0, arm_smmu_priq_irq_tasklet,
>>> +                                                   "arm-smmu-v3-priq", 
>>> smmu);
>>>                     if (ret < 0)
>>>                             dev_warn(smmu->dev,
>>>                                      "failed to enable priq irq\n");
>>> @@ -1814,11 +2101,8 @@ static int arm_smmu_setup_irqs(struct 
>>> arm_smmu_device *smmu)
>>>              * Cavium ThunderX2 implementation doesn't support unique irq
>>>              * lines. Use a single irq line for all the SMMUv3 interrupts.
>>>              */
>>> -           ret = devm_request_threaded_irq(smmu->dev, irq,
>>> -                                   arm_smmu_combined_irq_handler,
>>> -                                   arm_smmu_combined_irq_thread,
>>> -                                   IRQF_ONESHOT,
>>> -                                   "arm-smmu-v3-combined-irq", smmu);
>>> +           ret = request_irq(irq, 0, arm_smmu_combined_irq_handler,
>>> +                                           "arm-smmu-v3-combined-irq", 
>>> smmu);
>>>             if (ret < 0)
>>>                     dev_warn(smmu->dev, "failed to enable combined irq\n");
>>>     } else
>>> @@ -1857,7 +2141,7 @@ static int arm_smmu_device_reset(struct 
>>> arm_smmu_device *smmu, bool bypass)
>>>     reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
>>>     if (reg & CR0_SMMUEN) {
>>>             dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
>>> -           WARN_ON(is_kdump_kernel() && !disable_bypass);
>>> +           WARN_ON(!disable_bypass);
>>>             arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0);
>>>     }
>>> 
>>> @@ -1952,8 +2236,11 @@ static int arm_smmu_device_reset(struct 
>>> arm_smmu_device *smmu, bool bypass)
>>>             return ret;
>>>     }
>>> 
>>> -   if (is_kdump_kernel())
>>> -           enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
>>> +   /* Initialize tasklets for threaded IRQs*/
>>> +   tasklet_init(&smmu->evtq_irq_tasklet, arm_smmu_evtq_thread, smmu);
>>> +   tasklet_init(&smmu->priq_irq_tasklet, arm_smmu_priq_thread, smmu);
>>> +   tasklet_init(&smmu->combined_irq_tasklet, arm_smmu_combined_irq_thread,
>>> +                            smmu);
>>> 
>>>     /* Enable the SMMU interface, or ensure bypass */
>>>     if (!bypass || disable_bypass) {
>>> @@ -2195,7 +2482,7 @@ static inline int arm_smmu_device_acpi_probe(struct 
>>> platform_device *pdev,
>>> static int arm_smmu_device_dt_probe(struct platform_device *pdev,
>>>                                 struct arm_smmu_device *smmu)
>>> {
>>> -   struct device *dev = &pdev->dev;
>>> +   struct device *dev = pdev;
>>>     u32 cells;
>>>     int ret = -EINVAL;
>>> 
>>> @@ -2219,130 +2506,449 @@ static unsigned long 
>>> arm_smmu_resource_size(struct arm_smmu_device *smmu)
>>>             return SZ_128K;
>>> }
>>> 
>>> +/* Start of Xen specific code. */
>>> static int arm_smmu_device_probe(struct platform_device *pdev)
>>> {
>>> -   int irq, ret;
>>> -   struct resource *res;
>>> -   resource_size_t ioaddr;
>>> -   struct arm_smmu_device *smmu;
>>> -   struct device *dev = &pdev->dev;
>>> -   bool bypass;
>>> +    int irq, ret;
>>> +    paddr_t ioaddr, iosize;
>>> +    struct arm_smmu_device *smmu;
>>> +    bool bypass;
>>> +
>>> +    smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
>>> +    if ( !smmu )
>>> +    {
>>> +        dev_err(pdev, "failed to allocate arm_smmu_device\n");
>>> +        return -ENOMEM;
>>> +    }
>>> +    smmu->dev = pdev;
>>> +
>>> +    if ( pdev->of_node )
>>> +    {
>>> +        ret = arm_smmu_device_dt_probe(pdev, smmu);
>>> +    } else
>>> +    {
>> 
>> coding style
> 
> Ack. 
>> 
>> 
>>> +        ret = arm_smmu_device_acpi_probe(pdev, smmu);
>>> +        if ( ret == -ENODEV )
>>> +            return ret;
>>> +    }
>>> +
>>> +    /* Set bypass mode according to firmware probing result */
>>> +    bypass = !!ret;
>>> +
>>> +    /* Base address */
>>> +    ret = dt_device_get_address(dev_to_dt(pdev), 0, &ioaddr, &iosize);
>>> +    if( ret )
>>> +        return -ENODEV;
>>> +
>>> +    if ( iosize < arm_smmu_resource_size(smmu) )
>>> +    {
>>> +        dev_err(pdev, "MMIO region too small (%lx)\n", iosize);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    /*
>>> +     * Don't map the IMPLEMENTATION DEFINED regions, since they may contain
>>> +     * the PMCG registers which are reserved by the PMU driver.
>> 
>> Does this apply to Xen too?
> 
> Yes. Performance monitoring facilities are optional. Currently there is no 
> plan to support also in XEN.

I missed to add that in next version of the patch I will use the 
ARM_SMMU_REG_SZ  as size as also suggested by Julien.

Regards,
Rahul
>> 
>> 
>>> +     */
>>> +    smmu->base = ioremap_nocache(ioaddr, iosize);
>>> +    if ( IS_ERR(smmu->base) )
>>> +        return PTR_ERR(smmu->base);
>>> +
>>> +    if ( iosize > SZ_64K )
>>> +    {
>>> +        smmu->page1 = ioremap_nocache(ioaddr + SZ_64K, ARM_SMMU_REG_SZ);
>>> +        if ( IS_ERR(smmu->page1) )
>>> +            return PTR_ERR(smmu->page1);
>>> +    }
>>> +    else
>>> +    {
>>> +        smmu->page1 = smmu->base;
>>> +    }
>>> +
>>> +    /* Interrupt lines */
>>> +
>>> +    irq = platform_get_irq_byname_optional(pdev, "combined");
>>> +    if ( irq > 0 )
>>> +        smmu->combined_irq = irq;
>>> +    else
>>> +    {
>>> +        irq = platform_get_irq_byname_optional(pdev, "eventq");
>>> +        if ( irq > 0 )
>>> +            smmu->evtq.q.irq = irq;
>>> +
>>> +        irq = platform_get_irq_byname_optional(pdev, "priq");
>>> +        if ( irq > 0 )
>>> +            smmu->priq.q.irq = irq;
>>> +
>>> +        irq = platform_get_irq_byname_optional(pdev, "gerror");
>>> +        if ( irq > 0 )
>>> +            smmu->gerr_irq = irq;
>>> +    }
>>> +    /* Probe the h/w */
>>> +    ret = arm_smmu_device_hw_probe(smmu);
>>> +    if ( ret )
>>> +        return ret;
>>> +
>>> +    /* Initialise in-memory data structures */
>>> +    ret = arm_smmu_init_structures(smmu);
>>> +    if ( ret )
>>> +        return ret;
>>> +
>>> +    /* Reset the device */
>>> +    ret = arm_smmu_device_reset(smmu, bypass);
>>> +    if ( ret )
>>> +        return ret;
>>> +
>>> +    /*
>>> +     * Keep a list of all probed devices. This will be used to query
>>> +     * the smmu devices based on the fwnode.
>>> +     */
>>> +    INIT_LIST_HEAD(&smmu->devices);
>>> +
>>> +    spin_lock(&arm_smmu_devices_lock);
>>> +    list_add(&smmu->devices, &arm_smmu_devices);
>>> +    spin_unlock(&arm_smmu_devices_lock);
>>> +
>>> +    return 0;
>>> +}


 


Rackspace

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