diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 7116fedc5716..219b5d9d233b 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -176,6 +176,8 @@ struct kgsl_memdesc_ops { #define KGSL_MEMDESC_UCODE BIT(7) /* For global buffers, randomly assign an address from the region */ #define KGSL_MEMDESC_RANDOM BIT(8) +/* Allocate memory from the system instead of the pools */ +#define KGSL_MEMDESC_SYSMEM BIT(9) /** * struct kgsl_memdesc - GPU memory object descriptor diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index c4d3ee7e6967..f09b95701e5d 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -862,6 +862,38 @@ static void kgsl_contiguous_free(struct kgsl_memdesc *memdesc) } #if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER) +static void kgsl_free_secure_system_pages(struct kgsl_memdesc *memdesc) +{ + int i; + struct scatterlist *sg; + int ret = unlock_sgt(memdesc->sgt); + + if (ret) { + /* + * Unlock of the secure buffer failed. This buffer will + * be stuck in secure side forever and is unrecoverable. + * Give up on the buffer and don't return it to the + * pool. + */ + pr_err("kgsl: secure buf unlock failed: gpuaddr: %llx size: %llx ret: %d\n", + memdesc->gpuaddr, memdesc->size, ret); + return; + } + + atomic_long_sub(memdesc->size, &kgsl_driver.stats.secure); + + for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) { + struct page *page = sg_page(sg); + + __free_pages(page, get_order(PAGE_SIZE)); + } + + sg_free_table(memdesc->sgt); + kfree(memdesc->sgt); + + memdesc->sgt = NULL; +} + static void kgsl_free_secure_pool_pages(struct kgsl_memdesc *memdesc) { int ret = unlock_sgt(memdesc->sgt); @@ -904,6 +936,23 @@ static void kgsl_free_pool_pages(struct kgsl_memdesc *memdesc) memdesc->pages = NULL; } +static void kgsl_free_system_pages(struct kgsl_memdesc *memdesc) +{ + int i; + + kgsl_paged_unmap_kernel(memdesc); + WARN_ON(memdesc->hostptr); + + atomic_long_sub(memdesc->size, &kgsl_driver.stats.page_alloc); + + for (i = 0; i < memdesc->page_count; i++) + __free_pages(memdesc->pages[i], get_order(PAGE_SIZE)); + + memdesc->page_count = 0; + kvfree(memdesc->pages); + memdesc->pages = NULL; +} + static struct kgsl_memdesc_ops kgsl_contiguous_ops = { .free = kgsl_contiguous_free, .vmflags = VM_DONTDUMP | VM_PFNMAP | VM_DONTEXPAND | VM_DONTCOPY, @@ -911,6 +960,11 @@ static struct kgsl_memdesc_ops kgsl_contiguous_ops = { }; #if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER) +static struct kgsl_memdesc_ops kgsl_secure_system_ops = { + .free = kgsl_free_secure_system_pages, + /* FIXME: Make sure vmflags / vmfault does the right thing here */ +}; + static struct kgsl_memdesc_ops kgsl_secure_pool_ops = { .free = kgsl_free_secure_pool_pages, /* FIXME: Make sure vmflags / vmfault does the right thing here */ @@ -925,6 +979,49 @@ static struct kgsl_memdesc_ops kgsl_pool_ops = { .unmap_kernel = kgsl_paged_unmap_kernel, }; +static struct kgsl_memdesc_ops kgsl_system_ops = { + .free = kgsl_free_system_pages, + .vmflags = VM_DONTDUMP | VM_DONTEXPAND | VM_DONTCOPY | VM_MIXEDMAP, + .vmfault = kgsl_paged_vmfault, + .map_kernel = kgsl_paged_map_kernel, + .unmap_kernel = kgsl_paged_unmap_kernel, +}; + +static int kgsl_system_alloc_pages(u64 size, struct page ***pages, + struct device *dev) +{ + struct scatterlist sg; + struct page **local; + int i, npages = size >> PAGE_SHIFT; + + local = kvcalloc(npages, sizeof(*pages), GFP_KERNEL | __GFP_NORETRY); + if (!local) + return -ENOMEM; + + for (i = 0; i < npages; i++) { + gfp_t gfp = __GFP_ZERO | __GFP_HIGHMEM | + GFP_KERNEL | __GFP_NORETRY; + + local[i] = alloc_pages(gfp, get_order(PAGE_SIZE)); + if (!local[i]) { + for (i = i - 1; i >= 0; i--) + __free_pages(local[i], get_order(PAGE_SIZE)); + kvfree(local); + return -ENOMEM; + } + + /* Make sure the cache is clean */ + sg_init_table(&sg, 1); + sg_set_page(&sg, local[i], PAGE_SIZE, 0); + sg_dma_address(&sg) = page_to_phys(local[i]); + + dma_sync_sg_for_device(dev, &sg, 1, DMA_BIDIRECTIONAL); + } + + *pages = local; + return npages; +} + #if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER) static int kgsl_alloc_secure_pages(struct kgsl_device *device, struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv) @@ -942,8 +1039,13 @@ static int kgsl_alloc_secure_pages(struct kgsl_device *device, kgsl_memdesc_init(device, memdesc, flags); memdesc->priv |= priv; - memdesc->ops = &kgsl_secure_pool_ops; - count = kgsl_pool_alloc_pages(size, &pages, device->dev); + if (priv & KGSL_MEMDESC_SYSMEM) { + memdesc->ops = &kgsl_secure_system_ops; + count = kgsl_system_alloc_pages(size, &pages, device->dev); + } else { + memdesc->ops = &kgsl_secure_pool_ops; + count = kgsl_pool_alloc_pages(size, &pages, device->dev); + } if (count < 0) return count; @@ -997,8 +1099,13 @@ static int kgsl_alloc_pages(struct kgsl_device *device, kgsl_memdesc_init(device, memdesc, flags); memdesc->priv |= priv; - memdesc->ops = &kgsl_pool_ops; - count = kgsl_pool_alloc_pages(size, &pages, device->dev); + if (priv & KGSL_MEMDESC_SYSMEM) { + memdesc->ops = &kgsl_system_ops; + count = kgsl_system_alloc_pages(size, &pages, device->dev); + } else { + memdesc->ops = &kgsl_pool_ops; + count = kgsl_pool_alloc_pages(size, &pages, device->dev); + } if (count < 0) return count; @@ -1158,7 +1265,8 @@ kgsl_allocate_secure_global(struct kgsl_device *device, if (!md) return ERR_PTR(-ENOMEM); - priv |= KGSL_MEMDESC_GLOBAL; + /* Make sure that we get global memory from system memory */ + priv |= KGSL_MEMDESC_GLOBAL | KGSL_MEMDESC_SYSMEM; ret = kgsl_allocate_secure(device, &md->memdesc, size, flags, priv); if (ret) { @@ -1197,7 +1305,11 @@ struct kgsl_memdesc *kgsl_allocate_global(struct kgsl_device *device, if (!md) return ERR_PTR(-ENOMEM); - priv |= KGSL_MEMDESC_GLOBAL; + /* + * Make sure that we get global memory from system memory to keep from + * taking up pool memory for the life of the driver + */ + priv |= KGSL_MEMDESC_GLOBAL | KGSL_MEMDESC_SYSMEM; ret = kgsl_allocate_kernel(device, &md->memdesc, size, flags, priv); if (ret) {