Loading drivers/iommu/io-pgtable-arm.c +82 −12 Original line number Diff line number Diff line Loading @@ -274,7 +274,8 @@ static bool suppress_map_failures; static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, unsigned long iova, phys_addr_t paddr, arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep) arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep, bool flush) { arm_lpae_iopte pte = prot; Loading @@ -296,7 +297,11 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, pte |= pfn_to_iopte(paddr >> data->pg_shift, data); *ptep = pte; data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); if (flush) data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); if (prev_ptep) iopte_tblcnt_add(prev_ptep, 1); Loading @@ -304,22 +309,67 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, return 0; } struct map_state { unsigned long iova_end; unsigned int pgsize; arm_lpae_iopte *pgtable; arm_lpae_iopte *prev_pgtable; arm_lpae_iopte *pte_start; unsigned int num_pte; }; /* map state optimization works at level 3 (the 2nd-to-last level) */ #define MAP_STATE_LVL 3 static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, phys_addr_t paddr, size_t size, arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep) arm_lpae_iopte *prev_ptep, struct map_state *ms) { arm_lpae_iopte *cptep, pte; void *cookie = data->iop.cookie; size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data); arm_lpae_iopte *pgtable = ptep; /* Find our entry at the current level */ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); /* If we can install a leaf entry at this level, then do so */ if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) { if (!ms) return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep, prev_ptep, true); if (lvl == MAP_STATE_LVL) { if (ms->pgtable) data->iop.cfg.tlb->flush_pgtable( ms->pte_start, ms->num_pte * sizeof(*ptep), data->iop.cookie); ms->iova_end = round_down(iova, SZ_2M) + SZ_2M; ms->pgtable = pgtable; ms->prev_pgtable = prev_ptep; ms->pgsize = size; ms->pte_start = ptep; ms->num_pte = 1; } else { /* * We have some map state from previous page * mappings, but we're about to set up a block * mapping. Flush out the previous page mappings. */ if (ms->pgtable) data->iop.cfg.tlb->flush_pgtable( ms->pte_start, ms->num_pte * sizeof(*ptep), data->iop.cookie); memset(ms, 0, sizeof(*ms)); ms = NULL; } return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep, prev_ptep); prev_ptep, ms == NULL); } /* We can't allocate tables at the final level */ if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) Loading Loading @@ -347,7 +397,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, /* Rinse, repeat */ return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep, ptep); ptep, ms); } static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, Loading Loading @@ -403,7 +453,8 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, return 0; prot = arm_lpae_prot_to_pte(data, iommu_prot); return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep, NULL); return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep, NULL, NULL); } static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, Loading @@ -419,6 +470,7 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, int i, ret; unsigned int min_pagesz; unsigned long orig_iova = iova; struct map_state ms; /* If no access, then nothing to do */ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) Loading @@ -428,6 +480,8 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, min_pagesz = 1 << __ffs(data->iop.cfg.pgsize_bitmap); memset(&ms, 0, sizeof(ms)); for_each_sg(sg, s, nents, i) { phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; size_t size = s->length; Loading @@ -444,10 +498,21 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, while (size) { size_t pgsize = iommu_pgsize( data->iop.cfg.pgsize_bitmap, iova | phys, size); ret = __arm_lpae_map(data, iova, phys, pgsize, prot, lvl, ptep, NULL); if (ms.pgtable && (iova < ms.iova_end)) { arm_lpae_iopte *ptep = ms.pgtable + ARM_LPAE_LVL_IDX(iova, MAP_STATE_LVL, data); arm_lpae_init_pte( data, iova, phys, prot, MAP_STATE_LVL, ptep, ms.prev_pgtable, false); ms.num_pte++; } else { ret = __arm_lpae_map(data, iova, phys, pgsize, prot, lvl, ptep, NULL, &ms); if (ret) goto out_err; } iova += pgsize; mapped += pgsize; Loading @@ -456,6 +521,11 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, } } if (ms.pgtable) data->iop.cfg.tlb->flush_pgtable( ms.pte_start, ms.num_pte * sizeof(*ms.pte_start), data->iop.cookie); return mapped; out_err: Loading Loading @@ -533,7 +603,7 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, /* __arm_lpae_map expects a pointer to the start of the table */ tablep = &table - ARM_LPAE_LVL_IDX(blk_start, lvl, data); if (__arm_lpae_map(data, blk_start, blk_paddr, size, prot, lvl, tablep, prev_ptep) < 0) { tablep, prev_ptep, NULL) < 0) { if (table) { /* Free the table we allocated */ tablep = iopte_deref(table, data); Loading Loading
drivers/iommu/io-pgtable-arm.c +82 −12 Original line number Diff line number Diff line Loading @@ -274,7 +274,8 @@ static bool suppress_map_failures; static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, unsigned long iova, phys_addr_t paddr, arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep) arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep, bool flush) { arm_lpae_iopte pte = prot; Loading @@ -296,7 +297,11 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, pte |= pfn_to_iopte(paddr >> data->pg_shift, data); *ptep = pte; data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); if (flush) data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); if (prev_ptep) iopte_tblcnt_add(prev_ptep, 1); Loading @@ -304,22 +309,67 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, return 0; } struct map_state { unsigned long iova_end; unsigned int pgsize; arm_lpae_iopte *pgtable; arm_lpae_iopte *prev_pgtable; arm_lpae_iopte *pte_start; unsigned int num_pte; }; /* map state optimization works at level 3 (the 2nd-to-last level) */ #define MAP_STATE_LVL 3 static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, phys_addr_t paddr, size_t size, arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep, arm_lpae_iopte *prev_ptep) arm_lpae_iopte *prev_ptep, struct map_state *ms) { arm_lpae_iopte *cptep, pte; void *cookie = data->iop.cookie; size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data); arm_lpae_iopte *pgtable = ptep; /* Find our entry at the current level */ ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); /* If we can install a leaf entry at this level, then do so */ if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) { if (!ms) return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep, prev_ptep, true); if (lvl == MAP_STATE_LVL) { if (ms->pgtable) data->iop.cfg.tlb->flush_pgtable( ms->pte_start, ms->num_pte * sizeof(*ptep), data->iop.cookie); ms->iova_end = round_down(iova, SZ_2M) + SZ_2M; ms->pgtable = pgtable; ms->prev_pgtable = prev_ptep; ms->pgsize = size; ms->pte_start = ptep; ms->num_pte = 1; } else { /* * We have some map state from previous page * mappings, but we're about to set up a block * mapping. Flush out the previous page mappings. */ if (ms->pgtable) data->iop.cfg.tlb->flush_pgtable( ms->pte_start, ms->num_pte * sizeof(*ptep), data->iop.cookie); memset(ms, 0, sizeof(*ms)); ms = NULL; } return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep, prev_ptep); prev_ptep, ms == NULL); } /* We can't allocate tables at the final level */ if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) Loading Loading @@ -347,7 +397,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, /* Rinse, repeat */ return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep, ptep); ptep, ms); } static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, Loading Loading @@ -403,7 +453,8 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, return 0; prot = arm_lpae_prot_to_pte(data, iommu_prot); return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep, NULL); return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep, NULL, NULL); } static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, Loading @@ -419,6 +470,7 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, int i, ret; unsigned int min_pagesz; unsigned long orig_iova = iova; struct map_state ms; /* If no access, then nothing to do */ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) Loading @@ -428,6 +480,8 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, min_pagesz = 1 << __ffs(data->iop.cfg.pgsize_bitmap); memset(&ms, 0, sizeof(ms)); for_each_sg(sg, s, nents, i) { phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; size_t size = s->length; Loading @@ -444,10 +498,21 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, while (size) { size_t pgsize = iommu_pgsize( data->iop.cfg.pgsize_bitmap, iova | phys, size); ret = __arm_lpae_map(data, iova, phys, pgsize, prot, lvl, ptep, NULL); if (ms.pgtable && (iova < ms.iova_end)) { arm_lpae_iopte *ptep = ms.pgtable + ARM_LPAE_LVL_IDX(iova, MAP_STATE_LVL, data); arm_lpae_init_pte( data, iova, phys, prot, MAP_STATE_LVL, ptep, ms.prev_pgtable, false); ms.num_pte++; } else { ret = __arm_lpae_map(data, iova, phys, pgsize, prot, lvl, ptep, NULL, &ms); if (ret) goto out_err; } iova += pgsize; mapped += pgsize; Loading @@ -456,6 +521,11 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, } } if (ms.pgtable) data->iop.cfg.tlb->flush_pgtable( ms.pte_start, ms.num_pte * sizeof(*ms.pte_start), data->iop.cookie); return mapped; out_err: Loading Loading @@ -533,7 +603,7 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, /* __arm_lpae_map expects a pointer to the start of the table */ tablep = &table - ARM_LPAE_LVL_IDX(blk_start, lvl, data); if (__arm_lpae_map(data, blk_start, blk_paddr, size, prot, lvl, tablep, prev_ptep) < 0) { tablep, prev_ptep, NULL) < 0) { if (table) { /* Free the table we allocated */ tablep = iopte_deref(table, data); Loading