Re: [PATCH v2 2/4] iommu: Add Allwinner H6 IOMMU driver
From: Joerg Roedel <joro@8bytes.org>
Date: 2020-03-02 15:36:14
Also in:
linux-devicetree, linux-iommu
Hi Maxime, On Thu, Feb 20, 2020 at 07:15:14PM +0100, Maxime Ripard wrote:
+struct sun50i_iommu_domain {
+ struct iommu_domain domain;
+
+ /* Number of devices attached to the domain */
+ refcount_t refcnt;
+
+ /* Lock to modify the Directory Table */
+ spinlock_t dt_lock;I suggest you make page-table updates lock-less. Otherwise this lock will become a bottle-neck when using the IOMMU through DMA-API.
+
+static int sun50i_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+{
+ struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain);
+ struct sun50i_iommu *iommu = sun50i_domain->iommu;
+ u32 pte_index;
+ u32 *page_table, *pte_addr;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&sun50i_domain->dt_lock, flags);
+ page_table = sun50i_dte_get_page_table(sun50i_domain, iova, gfp);
+ if (IS_ERR(page_table)) {
+ ret = PTR_ERR(page_table);
+ goto out;
+ }
+
+ pte_index = sun50i_iova_get_pte_index(iova);
+ pte_addr = &page_table[pte_index];
+ if (sun50i_pte_is_page_valid(*pte_addr)) {You can use unlikely() here.
+ phys_addr_t page_phys = sun50i_pte_get_page_address(*pte_addr); + dev_err(iommu->dev, + "iova %pad already mapped to %pa cannot remap to %pa prot: %#x\n", + &iova, &page_phys, &paddr, prot); + ret = -EBUSY; + goto out; + } + + *pte_addr = sun50i_mk_pte(paddr, prot); + sun50i_table_flush(sun50i_domain, pte_addr, 1);
This maps only one page, right? But the function needs to map up to 'size' as given in the parameter list.
+ + spin_lock_irqsave(&iommu->iommu_lock, flags); + sun50i_iommu_tlb_invalidate(iommu, iova); + spin_unlock_irqrestore(&iommu->iommu_lock, flags);
Why is there a need to flush the TLB here? The IOMMU-API provides call-backs so that the user of the API can decide when it wants to flush the IO/TLB. Such flushes are usually expensive and doing them on every map and unmap will cost significant performance.
+static size_t sun50i_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+ size_t size, struct iommu_iotlb_gather *gather)
+{
+ struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain);
+ struct sun50i_iommu *iommu = sun50i_domain->iommu;
+ unsigned long flags;
+ phys_addr_t pt_phys;
+ dma_addr_t pte_dma;
+ u32 *pte_addr;
+ u32 dte;
+
+ spin_lock_irqsave(&sun50i_domain->dt_lock, flags);
+
+ dte = sun50i_domain->dt[sun50i_iova_get_dte_index(iova)];
+ if (!sun50i_dte_is_pt_valid(dte)) {
+ spin_unlock_irqrestore(&sun50i_domain->dt_lock, flags);
+ return 0;
+ }
+
+ pt_phys = sun50i_dte_get_pt_address(dte);
+ pte_addr = (u32 *)phys_to_virt(pt_phys) + sun50i_iova_get_pte_index(iova);
+ pte_dma = pt_phys + sun50i_iova_get_pte_index(iova) * PT_ENTRY_SIZE;
+
+ if (!sun50i_pte_is_page_valid(*pte_addr)) {
+ spin_unlock_irqrestore(&sun50i_domain->dt_lock, flags);
+ return 0;
+ }
+
+ memset(pte_addr, 0, sizeof(*pte_addr));
+ sun50i_table_flush(sun50i_domain, pte_addr, 1);
+
+ spin_lock(&iommu->iommu_lock);
+ sun50i_iommu_tlb_invalidate(iommu, iova);
+ sun50i_iommu_ptw_invalidate(iommu, iova);
+ spin_unlock(&iommu->iommu_lock);Same objections as in the map function. This only unmaps one page, and is the IO/TLB flush really needed here?
+static struct iommu_domain *sun50i_iommu_domain_alloc(unsigned type)
+{
+ struct sun50i_iommu_domain *sun50i_domain;
+
+ if (type != IOMMU_DOMAIN_DMA && type != IOMMU_DOMAIN_UNMANAGED)
+ return NULL;I think you should at least also support identity domains here. The iommu-core code might allocate those for default domains. Regards, Joerg _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel