diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 320b815e2beb..30f7c9543e38 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -84,15 +84,8 @@ static struct kmem_cache *addr_entry_cache; * * Here we define an array and a simple allocator to keep track of the currently * active global entries. Each entry is assigned a unique address inside of a - * MMU implementation specific "global" region. The addresses are assigned - * sequentially and never re-used to avoid having to go back and reprogram - * existing pagetables. The entire list of active entries are mapped and - * unmapped into every new pagetable as it is created and destroyed. - * - * Because there are relatively few entries and they are defined at boot time we - * don't need to go over the top to define a dynamic allocation scheme. It will - * be less wasteful to pick a static number with a little bit of growth - * potential. + * MMU implementation specific "global" region. We use a simple bitmap based + * allocator for the region to allow for both fixed and dynamic addressing. */ #define GLOBAL_PT_ENTRIES 32 @@ -102,10 +95,12 @@ struct global_pt_entry { char name[32]; }; +#define GLOBAL_MAP_PAGES (KGSL_IOMMU_GLOBAL_MEM_SIZE >> PAGE_SHIFT) + static struct global_pt_entry global_pt_entries[GLOBAL_PT_ENTRIES]; static struct kgsl_memdesc *kgsl_global_secure_pt_entry; +static DECLARE_BITMAP(global_map, GLOBAL_MAP_PAGES); static int global_pt_count; -uint64_t global_pt_alloc; static struct kgsl_memdesc gpu_qdss_desc; static struct kgsl_memdesc gpu_qtimer_desc; @@ -186,6 +181,12 @@ static void kgsl_iommu_remove_global(struct kgsl_mmu *mmu, for (i = 0; i < global_pt_count; i++) { if (global_pt_entries[i].memdesc == memdesc) { + u64 offset = memdesc->gpuaddr - + KGSL_IOMMU_GLOBAL_MEM_BASE(mmu); + + bitmap_clear(global_map, offset >> PAGE_SHIFT, + kgsl_memdesc_footprint(memdesc) >> PAGE_SHIFT); + memdesc->gpuaddr = 0; memdesc->priv &= ~KGSL_MEMDESC_GLOBAL; global_pt_entries[i].memdesc = NULL; @@ -197,15 +198,27 @@ static void kgsl_iommu_remove_global(struct kgsl_mmu *mmu, static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, struct kgsl_memdesc *memdesc, const char *name) { + int bit; + u64 size = kgsl_memdesc_footprint(memdesc); + if (memdesc->gpuaddr != 0) return; - BUG_ON(global_pt_count >= GLOBAL_PT_ENTRIES); - BUG_ON((global_pt_alloc + memdesc->size) >= KGSL_IOMMU_GLOBAL_MEM_SIZE); + if (WARN_ON(global_pt_count >= GLOBAL_PT_ENTRIES)) + return; + + bit = bitmap_find_next_zero_area(global_map, GLOBAL_MAP_PAGES, + 0, size >> PAGE_SHIFT, 0); + + if (WARN_ON(bit >= GLOBAL_MAP_PAGES)) + return; + + memdesc->gpuaddr = + KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) + (bit << PAGE_SHIFT); + + bitmap_set(global_map, bit, size >> PAGE_SHIFT); - memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) + global_pt_alloc; memdesc->priv |= KGSL_MEMDESC_GLOBAL; - global_pt_alloc += memdesc->size; global_pt_entries[global_pt_count].memdesc = memdesc; strlcpy(global_pt_entries[global_pt_count].name, name,