android_kernel_samsung_sm86.../qcom/opensource/graphics-kernel/kgsl_sharedmem.c
David Wronek 880d405719 Add 'qcom/opensource/graphics-kernel/' from commit 'b4fdc4c04295ac59109ae19d64747522740c3f14'
git-subtree-dir: qcom/opensource/graphics-kernel
git-subtree-mainline: 992813d9c1
git-subtree-split: b4fdc4c042
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/graphics-kernel
tag: GRAPHICS.LA.14.0.r1-07700-lanai.0
2024-10-06 16:44:56 +02:00

1974 lines
48 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <asm/cacheflush.h>
#include <linux/of_platform.h>
#include <linux/highmem.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/shmem_fs.h>
#include <linux/sched/signal.h>
#include <linux/version.h>
#include "kgsl_device.h"
#include "kgsl_pool.h"
#include "kgsl_reclaim.h"
#include "kgsl_sharedmem.h"
/*
* The user can set this from debugfs to force failed memory allocations to
* fail without trying OOM first. This is a debug setting useful for
* stress applications that want to test failure cases without pushing the
* system into unrecoverable OOM panics
*/
bool kgsl_sharedmem_noretry_flag;
static DEFINE_MUTEX(kernel_map_global_lock);
#define MEMTYPE(_type, _name) \
static struct kgsl_memtype memtype_##_name = { \
.type = _type, \
.attr = { .name = __stringify(_name), .mode = 0444 } \
}
struct kgsl_memtype {
unsigned int type;
struct attribute attr;
};
/* We can not use macro MEMTYPE for "any(0)" because of special characters */
static struct kgsl_memtype memtype_any0 = {
.type = KGSL_MEMTYPE_OBJECTANY,
.attr = { .name = "any(0)", .mode = 0444 },
};
MEMTYPE(KGSL_MEMTYPE_FRAMEBUFFER, framebuffer);
MEMTYPE(KGSL_MEMTYPE_RENDERBUFFER, renderbuffer);
MEMTYPE(KGSL_MEMTYPE_ARRAYBUFFER, arraybuffer);
MEMTYPE(KGSL_MEMTYPE_ELEMENTARRAYBUFFER, elementarraybuffer);
MEMTYPE(KGSL_MEMTYPE_VERTEXARRAYBUFFER, vertexarraybuffer);
MEMTYPE(KGSL_MEMTYPE_TEXTURE, texture);
MEMTYPE(KGSL_MEMTYPE_SURFACE, surface);
MEMTYPE(KGSL_MEMTYPE_EGL_SURFACE, egl_surface);
MEMTYPE(KGSL_MEMTYPE_GL, gl);
MEMTYPE(KGSL_MEMTYPE_CL, cl);
MEMTYPE(KGSL_MEMTYPE_CL_BUFFER_MAP, cl_buffer_map);
MEMTYPE(KGSL_MEMTYPE_CL_BUFFER_NOMAP, cl_buffer_nomap);
MEMTYPE(KGSL_MEMTYPE_CL_IMAGE_MAP, cl_image_map);
MEMTYPE(KGSL_MEMTYPE_CL_IMAGE_NOMAP, cl_image_nomap);
MEMTYPE(KGSL_MEMTYPE_CL_KERNEL_STACK, cl_kernel_stack);
MEMTYPE(KGSL_MEMTYPE_COMMAND, command);
MEMTYPE(KGSL_MEMTYPE_2D, 2d);
MEMTYPE(KGSL_MEMTYPE_EGL_IMAGE, egl_image);
MEMTYPE(KGSL_MEMTYPE_EGL_SHADOW, egl_shadow);
MEMTYPE(KGSL_MEMTYPE_MULTISAMPLE, egl_multisample);
MEMTYPE(KGSL_MEMTYPE_KERNEL, kernel);
static struct attribute *memtype_attrs[] = {
&memtype_any0.attr,
&memtype_framebuffer.attr,
&memtype_renderbuffer.attr,
&memtype_arraybuffer.attr,
&memtype_elementarraybuffer.attr,
&memtype_vertexarraybuffer.attr,
&memtype_texture.attr,
&memtype_surface.attr,
&memtype_egl_surface.attr,
&memtype_gl.attr,
&memtype_cl.attr,
&memtype_cl_buffer_map.attr,
&memtype_cl_buffer_nomap.attr,
&memtype_cl_image_map.attr,
&memtype_cl_image_nomap.attr,
&memtype_cl_kernel_stack.attr,
&memtype_command.attr,
&memtype_2d.attr,
&memtype_egl_image.attr,
&memtype_egl_shadow.attr,
&memtype_egl_multisample.attr,
&memtype_kernel.attr,
NULL,
};
ATTRIBUTE_GROUPS(memtype);
/* An attribute for showing per-process memory statistics */
struct kgsl_mem_entry_attribute {
struct kgsl_process_attribute attr;
int memtype;
ssize_t (*show)(struct kgsl_process_private *priv,
int type, char *buf);
};
static inline struct kgsl_process_attribute *to_process_attr(
struct attribute *attr)
{
return container_of(attr, struct kgsl_process_attribute, attr);
}
#define to_mem_entry_attr(a) \
container_of(a, struct kgsl_mem_entry_attribute, attr)
#define __MEM_ENTRY_ATTR(_type, _name, _show) \
{ \
.attr = __ATTR(_name, 0444, mem_entry_sysfs_show, NULL), \
.memtype = _type, \
.show = _show, \
}
#define MEM_ENTRY_ATTR(_type, _name, _show) \
static struct kgsl_mem_entry_attribute mem_entry_##_name = \
__MEM_ENTRY_ATTR(_type, _name, _show)
static ssize_t mem_entry_sysfs_show(struct kobject *kobj,
struct kgsl_process_attribute *attr, char *buf)
{
struct kgsl_mem_entry_attribute *pattr = to_mem_entry_attr(attr);
struct kgsl_process_private *priv =
container_of(kobj, struct kgsl_process_private, kobj);
return pattr->show(priv, pattr->memtype, buf);
}
struct deferred_work {
struct kgsl_process_private *private;
struct work_struct work;
};
static void process_private_deferred_put(struct work_struct *work)
{
struct deferred_work *free_work =
container_of(work, struct deferred_work, work);
kgsl_process_private_put(free_work->private);
kfree(free_work);
}
static ssize_t memtype_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct kgsl_process_private *priv;
struct kgsl_memtype *memtype;
struct kgsl_mem_entry *entry;
u64 size = 0;
int id = 0;
struct deferred_work *work = kzalloc(sizeof(struct deferred_work),
GFP_KERNEL);
if (!work)
return -ENOMEM;
priv = container_of(kobj, struct kgsl_process_private, kobj_memtype);
memtype = container_of(attr, struct kgsl_memtype, attr);
/*
* Take a process refcount here and put it back in a deferred manner.
* This is to avoid a deadlock where we put back last reference of the
* process private (via kgsl_mem_entry_put) here and end up trying to
* remove sysfs kobject while we are still in the middle of reading one
* of the sysfs files.
*/
if (!kgsl_process_private_get(priv)) {
kfree(work);
return -ENOENT;
}
work->private = priv;
INIT_WORK(&work->work, process_private_deferred_put);
spin_lock(&priv->mem_lock);
for (entry = idr_get_next(&priv->mem_idr, &id); entry;
id++, entry = idr_get_next(&priv->mem_idr, &id)) {
struct kgsl_memdesc *memdesc;
unsigned int type;
if (!kgsl_mem_entry_get(entry))
continue;
spin_unlock(&priv->mem_lock);
memdesc = &entry->memdesc;
type = kgsl_memdesc_get_memtype(memdesc);
if (type == memtype->type)
size += memdesc->size;
kgsl_mem_entry_put(entry);
spin_lock(&priv->mem_lock);
}
spin_unlock(&priv->mem_lock);
queue_work(kgsl_driver.lockless_workqueue, &work->work);
return scnprintf(buf, PAGE_SIZE, "%llu\n", size);
}
static const struct sysfs_ops memtype_sysfs_ops = {
.show = memtype_sysfs_show,
};
static struct kobj_type ktype_memtype = {
.sysfs_ops = &memtype_sysfs_ops,
.default_groups = memtype_groups,
};
static ssize_t
imported_mem_show(struct kgsl_process_private *priv,
int type, char *buf)
{
struct kgsl_mem_entry *entry;
uint64_t imported_mem = 0;
int id = 0;
struct deferred_work *work = kzalloc(sizeof(struct deferred_work),
GFP_KERNEL);
if (!work)
return -ENOMEM;
/*
* Take a process refcount here and put it back in a deferred manner.
* This is to avoid a deadlock where we put back last reference of the
* process private (via kgsl_mem_entry_put) here and end up trying to
* remove sysfs kobject while we are still in the middle of reading one
* of the sysfs files.
*/
if (!kgsl_process_private_get(priv)) {
kfree(work);
return -ENOENT;
}
work->private = priv;
INIT_WORK(&work->work, process_private_deferred_put);
spin_lock(&priv->mem_lock);
for (entry = idr_get_next(&priv->mem_idr, &id); entry;
id++, entry = idr_get_next(&priv->mem_idr, &id)) {
int egl_surface_count = 0, egl_image_count = 0;
struct kgsl_memdesc *m;
if (!kgsl_mem_entry_get(entry))
continue;
spin_unlock(&priv->mem_lock);
m = &entry->memdesc;
if (kgsl_memdesc_usermem_type(m) == KGSL_MEM_ENTRY_ION) {
kgsl_get_egl_counts(entry, &egl_surface_count,
&egl_image_count);
if (kgsl_memdesc_get_memtype(m) ==
KGSL_MEMTYPE_EGL_SURFACE)
imported_mem += m->size;
else if (egl_surface_count == 0) {
uint64_t size = m->size;
do_div(size, (egl_image_count ?
egl_image_count : 1));
imported_mem += size;
}
}
kgsl_mem_entry_put(entry);
spin_lock(&priv->mem_lock);
}
spin_unlock(&priv->mem_lock);
queue_work(kgsl_driver.lockless_workqueue, &work->work);
return scnprintf(buf, PAGE_SIZE, "%llu\n", imported_mem);
}
static ssize_t
gpumem_mapped_show(struct kgsl_process_private *priv,
int type, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%lld\n",
atomic64_read(&priv->gpumem_mapped));
}
static ssize_t
gpumem_unmapped_show(struct kgsl_process_private *priv, int type, char *buf)
{
u64 gpumem_total = atomic64_read(&priv->stats[type].cur);
u64 gpumem_mapped = atomic64_read(&priv->gpumem_mapped);
if (gpumem_mapped > gpumem_total)
return -EIO;
return scnprintf(buf, PAGE_SIZE, "%llu\n",
gpumem_total - gpumem_mapped);
}
/**
* Show the current amount of memory allocated for the given memtype
*/
static ssize_t
mem_entry_show(struct kgsl_process_private *priv, int type, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%lld\n",
atomic64_read(&priv->stats[type].cur));
}
/**
* Show the maximum memory allocated for the given memtype through the life of
* the process
*/
static ssize_t
mem_entry_max_show(struct kgsl_process_private *priv, int type, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%llu\n", priv->stats[type].max);
}
static ssize_t process_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct kgsl_process_attribute *pattr = to_process_attr(attr);
return pattr->show(kobj, pattr, buf);
}
static ssize_t process_sysfs_store(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t count)
{
struct kgsl_process_attribute *pattr = to_process_attr(attr);
if (pattr->store)
return pattr->store(kobj, pattr, buf, count);
return -EIO;
}
/* Dummy release function - we have nothing to do here */
static void process_sysfs_release(struct kobject *kobj)
{
}
static const struct sysfs_ops process_sysfs_ops = {
.show = process_sysfs_show,
.store = process_sysfs_store,
};
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_KERNEL, kernel, mem_entry_show);
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_KERNEL, kernel_max, mem_entry_max_show);
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_USER, user, mem_entry_show);
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_USER, user_max, mem_entry_max_show);
#ifdef CONFIG_ION
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_USER, ion, mem_entry_show);
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_USER, ion_max, mem_entry_max_show);
#endif
MEM_ENTRY_ATTR(0, imported_mem, imported_mem_show);
MEM_ENTRY_ATTR(0, gpumem_mapped, gpumem_mapped_show);
MEM_ENTRY_ATTR(KGSL_MEM_ENTRY_KERNEL, gpumem_unmapped, gpumem_unmapped_show);
static struct attribute *mem_entry_attrs[] = {
&mem_entry_kernel.attr.attr,
&mem_entry_kernel_max.attr.attr,
&mem_entry_user.attr.attr,
&mem_entry_user_max.attr.attr,
#ifdef CONFIG_ION
&mem_entry_ion.attr.attr,
&mem_entry_ion_max.attr.attr,
#endif
&mem_entry_imported_mem.attr.attr,
&mem_entry_gpumem_mapped.attr.attr,
&mem_entry_gpumem_unmapped.attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(mem_entry);
static struct kobj_type process_ktype = {
.sysfs_ops = &process_sysfs_ops,
.release = &process_sysfs_release,
.default_groups = mem_entry_groups,
};
#ifdef CONFIG_QCOM_KGSL_PROCESS_RECLAIM
static struct device_attribute dev_attr_max_reclaim_limit = {
.attr = { .name = "max_reclaim_limit", .mode = 0644 },
.show = kgsl_proc_max_reclaim_limit_show,
.store = kgsl_proc_max_reclaim_limit_store,
};
static struct device_attribute dev_attr_page_reclaim_per_call = {
.attr = { .name = "page_reclaim_per_call", .mode = 0644 },
.show = kgsl_nr_to_scan_show,
.store = kgsl_nr_to_scan_store,
};
#endif
/**
* kgsl_process_init_sysfs() - Initialize and create sysfs files for a process
*
* @device: Pointer to kgsl device struct
* @private: Pointer to the structure for the process
*
* kgsl_process_init_sysfs() is called at the time of creating the
* process struct when a process opens the kgsl device for the first time.
* This function creates the sysfs files for the process.
*/
void kgsl_process_init_sysfs(struct kgsl_device *device,
struct kgsl_process_private *private)
{
if (kobject_init_and_add(&private->kobj, &process_ktype,
kgsl_driver.prockobj, "%d", pid_nr(private->pid))) {
dev_err(device->dev, "Unable to add sysfs for process %d\n",
pid_nr(private->pid));
}
kgsl_reclaim_proc_sysfs_init(private);
if (kobject_init_and_add(&private->kobj_memtype, &ktype_memtype,
&private->kobj, "memtype")) {
dev_err(device->dev, "Unable to add memtype sysfs for process %d\n",
pid_nr(private->pid));
}
}
static ssize_t memstat_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
uint64_t val = 0;
if (!strcmp(attr->attr.name, "vmalloc"))
val = atomic_long_read(&kgsl_driver.stats.vmalloc);
else if (!strcmp(attr->attr.name, "vmalloc_max"))
val = atomic_long_read(&kgsl_driver.stats.vmalloc_max);
else if (!strcmp(attr->attr.name, "page_alloc"))
val = atomic_long_read(&kgsl_driver.stats.page_alloc);
else if (!strcmp(attr->attr.name, "page_alloc_max"))
val = atomic_long_read(&kgsl_driver.stats.page_alloc_max);
else if (!strcmp(attr->attr.name, "coherent"))
val = atomic_long_read(&kgsl_driver.stats.coherent);
else if (!strcmp(attr->attr.name, "coherent_max"))
val = atomic_long_read(&kgsl_driver.stats.coherent_max);
else if (!strcmp(attr->attr.name, "secure"))
val = atomic_long_read(&kgsl_driver.stats.secure);
else if (!strcmp(attr->attr.name, "secure_max"))
val = atomic_long_read(&kgsl_driver.stats.secure_max);
else if (!strcmp(attr->attr.name, "mapped"))
val = atomic_long_read(&kgsl_driver.stats.mapped);
else if (!strcmp(attr->attr.name, "mapped_max"))
val = atomic_long_read(&kgsl_driver.stats.mapped_max);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t full_cache_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
unsigned int thresh = 0;
ret = kstrtou32(buf, 0, &thresh);
if (ret)
return ret;
kgsl_driver.full_cache_threshold = thresh;
return count;
}
static ssize_t full_cache_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n",
kgsl_driver.full_cache_threshold);
}
static DEVICE_ATTR(vmalloc, 0444, memstat_show, NULL);
static DEVICE_ATTR(vmalloc_max, 0444, memstat_show, NULL);
static DEVICE_ATTR(page_alloc, 0444, memstat_show, NULL);
static DEVICE_ATTR(page_alloc_max, 0444, memstat_show, NULL);
static DEVICE_ATTR(coherent, 0444, memstat_show, NULL);
static DEVICE_ATTR(coherent_max, 0444, memstat_show, NULL);
static DEVICE_ATTR(secure, 0444, memstat_show, NULL);
static DEVICE_ATTR(secure_max, 0444, memstat_show, NULL);
static DEVICE_ATTR(mapped, 0444, memstat_show, NULL);
static DEVICE_ATTR(mapped_max, 0444, memstat_show, NULL);
static DEVICE_ATTR_RW(full_cache_threshold);
static const struct attribute *drv_attr_list[] = {
&dev_attr_vmalloc.attr,
&dev_attr_vmalloc_max.attr,
&dev_attr_page_alloc.attr,
&dev_attr_page_alloc_max.attr,
&dev_attr_coherent.attr,
&dev_attr_coherent_max.attr,
&dev_attr_secure.attr,
&dev_attr_secure_max.attr,
&dev_attr_mapped.attr,
&dev_attr_mapped_max.attr,
&dev_attr_full_cache_threshold.attr,
#ifdef CONFIG_QCOM_KGSL_PROCESS_RECLAIM
&dev_attr_max_reclaim_limit.attr,
&dev_attr_page_reclaim_per_call.attr,
#endif
NULL,
};
int
kgsl_sharedmem_init_sysfs(void)
{
return sysfs_create_files(&kgsl_driver.virtdev.kobj, drv_attr_list);
}
static vm_fault_t kgsl_paged_vmfault(struct kgsl_memdesc *memdesc,
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
int pgoff, ret;
struct page *page;
unsigned int offset = vmf->address - vma->vm_start;
if (offset >= memdesc->size)
return VM_FAULT_SIGBUS;
pgoff = offset >> PAGE_SHIFT;
spin_lock(&memdesc->lock);
if (memdesc->pages[pgoff]) {
page = memdesc->pages[pgoff];
get_page(page);
} else {
struct kgsl_process_private *priv =
((struct kgsl_mem_entry *)vma->vm_private_data)->priv;
/* We are here because page was reclaimed */
memdesc->priv |= KGSL_MEMDESC_SKIP_RECLAIM;
spin_unlock(&memdesc->lock);
page = shmem_read_mapping_page_gfp(
memdesc->shmem_filp->f_mapping, pgoff,
kgsl_gfp_mask(0));
if (IS_ERR(page))
return VM_FAULT_SIGBUS;
kgsl_page_sync(memdesc->dev, page, PAGE_SIZE, DMA_BIDIRECTIONAL);
spin_lock(&memdesc->lock);
/*
* Update the pages array only if the page was
* not already brought back.
*/
if (!memdesc->pages[pgoff]) {
memdesc->pages[pgoff] = page;
atomic_dec(&priv->unpinned_page_count);
get_page(page);
}
}
spin_unlock(&memdesc->lock);
ret = vmf_insert_page(vma, vmf->address, page);
put_page(page);
return ret;
}
static void kgsl_paged_unmap_kernel(struct kgsl_memdesc *memdesc)
{
mutex_lock(&kernel_map_global_lock);
if (!memdesc->hostptr) {
/* If already unmapped the refcount should be 0 */
WARN_ON(memdesc->hostptr_count);
goto done;
}
memdesc->hostptr_count--;
if (memdesc->hostptr_count)
goto done;
vunmap(memdesc->hostptr);
atomic_long_sub(memdesc->size, &kgsl_driver.stats.vmalloc);
memdesc->hostptr = NULL;
done:
mutex_unlock(&kernel_map_global_lock);
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
#include <soc/qcom/secure_buffer.h>
int kgsl_lock_sgt(struct sg_table *sgt, u64 size)
{
int dest_perms = PERM_READ | PERM_WRITE;
int source_vm = VMID_HLOS;
int dest_vm = VMID_CP_PIXEL;
int ret;
do {
ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm,
&dest_perms, 1);
} while (ret == -EAGAIN);
if (ret) {
/*
* If returned error code is EADDRNOTAVAIL, then this
* memory may no longer be in a usable state as security
* state of the pages is unknown after this failure. This
* memory can neither be added back to the pool nor buddy
* system.
*/
if (ret == -EADDRNOTAVAIL)
pr_err("Failure to lock secure GPU memory 0x%llx bytes will not be recoverable\n",
size);
return ret;
}
return 0;
}
int kgsl_unlock_sgt(struct sg_table *sgt)
{
int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC;
int source_vm = VMID_CP_PIXEL;
int dest_vm = VMID_HLOS;
int ret;
do {
ret = hyp_assign_table(sgt, &source_vm, 1, &dest_vm,
&dest_perms, 1);
} while (ret == -EAGAIN);
if (ret)
return ret;
return 0;
}
#endif
static int kgsl_paged_map_kernel(struct kgsl_memdesc *memdesc)
{
int ret = 0;
/* Sanity check - don't map more than we could possibly chew */
if (memdesc->size > ULONG_MAX)
return -ENOMEM;
mutex_lock(&kernel_map_global_lock);
if ((!memdesc->hostptr) && (memdesc->pages != NULL)) {
pgprot_t page_prot;
u64 cache;
/* Determine user-side caching policy */
cache = kgsl_memdesc_get_cachemode(memdesc);
switch (cache) {
case KGSL_CACHEMODE_WRITETHROUGH:
page_prot = PAGE_KERNEL;
WARN_ONCE(1, "WRITETHROUGH is deprecated for arm64");
break;
case KGSL_CACHEMODE_WRITEBACK:
page_prot = PAGE_KERNEL;
break;
case KGSL_CACHEMODE_UNCACHED:
case KGSL_CACHEMODE_WRITECOMBINE:
default:
page_prot = pgprot_writecombine(PAGE_KERNEL);
break;
}
memdesc->hostptr = vmap(memdesc->pages, memdesc->page_count,
VM_IOREMAP, page_prot);
if (memdesc->hostptr)
KGSL_STATS_ADD(memdesc->size,
&kgsl_driver.stats.vmalloc,
&kgsl_driver.stats.vmalloc_max);
else
ret = -ENOMEM;
}
if (memdesc->hostptr)
memdesc->hostptr_count++;
mutex_unlock(&kernel_map_global_lock);
return ret;
}
static vm_fault_t kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc,
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
unsigned long offset, pfn;
offset = ((unsigned long) vmf->address - vma->vm_start) >>
PAGE_SHIFT;
pfn = (memdesc->physaddr >> PAGE_SHIFT) + offset;
return vmf_insert_pfn(vma, vmf->address, pfn);
}
static void _dma_cache_op(struct device *dev, struct page *page,
unsigned int op)
{
struct scatterlist sgl;
sg_init_table(&sgl, 1);
sg_set_page(&sgl, page, PAGE_SIZE, 0);
sg_dma_address(&sgl) = page_to_phys(page);
/*
* APIs for Cache Maintenance Operations are updated in kernel
* version 6.1. Prior to 6.1, dma_sync_sg_for_device() with
* DMA_FROM_DEVICE as direction triggers cache invalidate and
* clean whereas in kernel version 6.1, it triggers only cache
* clean. Hence use dma_sync_sg_for_cpu() for cache invalidate
* for kernel version 6.1 and above.
*/
switch (op) {
case KGSL_CACHE_OP_FLUSH:
dma_sync_sg_for_device(dev, &sgl, 1, DMA_TO_DEVICE);
#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
dma_sync_sg_for_cpu(dev, &sgl, 1, DMA_FROM_DEVICE);
#else
dma_sync_sg_for_device(dev, &sgl, 1, DMA_FROM_DEVICE);
#endif
break;
case KGSL_CACHE_OP_CLEAN:
dma_sync_sg_for_device(dev, &sgl, 1, DMA_TO_DEVICE);
break;
case KGSL_CACHE_OP_INV:
dma_sync_sg_for_device(dev, &sgl, 1, DMA_FROM_DEVICE);
#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
dma_sync_sg_for_cpu(dev, &sgl, 1, DMA_FROM_DEVICE);
#endif
break;
}
}
int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
uint64_t size, unsigned int op)
{
int i;
if (memdesc->flags & KGSL_MEMFLAGS_IOCOHERENT)
return 0;
if (size == 0 || size > UINT_MAX)
return -EINVAL;
/* Make sure that the offset + size does not overflow */
if ((offset + size < offset) || (offset + size < size))
return -ERANGE;
/* Check that offset+length does not exceed memdesc->size */
if (offset + size > memdesc->size)
return -ERANGE;
size += offset & PAGE_MASK;
offset &= ~PAGE_MASK;
/* If there is a sgt, use for_each_sg_page to walk it */
if (memdesc->sgt) {
struct sg_page_iter sg_iter;
for_each_sg_page(memdesc->sgt->sgl, &sg_iter,
PAGE_ALIGN(size) >> PAGE_SHIFT, offset >> PAGE_SHIFT)
_dma_cache_op(memdesc->dev, sg_page_iter_page(&sg_iter), op);
return 0;
}
/* Otherwise just walk through the list of pages */
for (i = 0; i < memdesc->page_count; i++) {
u64 cur = (i << PAGE_SHIFT);
if ((cur < offset) || (cur >= (offset + size)))
continue;
_dma_cache_op(memdesc->dev, memdesc->pages[i], op);
}
return 0;
}
void kgsl_memdesc_init(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, uint64_t flags)
{
struct kgsl_mmu *mmu = &device->mmu;
unsigned int align;
memset(memdesc, 0, sizeof(*memdesc));
/* Turn off SVM if the system doesn't support it */
if (!kgsl_mmu_is_perprocess(mmu))
flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
/* Secure memory disables advanced addressing modes */
if (flags & KGSL_MEMFLAGS_SECURE)
flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
/* Disable IO coherence if it is not supported on the chip */
if (!kgsl_mmu_has_feature(device, KGSL_MMU_IO_COHERENT)) {
flags &= ~((uint64_t) KGSL_MEMFLAGS_IOCOHERENT);
WARN_ONCE(IS_ENABLED(CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT),
"I/O coherency is not supported on this target\n");
} else if (IS_ENABLED(CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT))
flags |= KGSL_MEMFLAGS_IOCOHERENT;
/*
* We can't enable I/O coherency on uncached surfaces because of
* situations where hardware might snoop the cpu caches which can
* have stale data. This happens primarily due to the limitations
* of dma caching APIs available on arm64
*/
if (!kgsl_cachemode_is_cached(flags))
flags &= ~((u64) KGSL_MEMFLAGS_IOCOHERENT);
if (kgsl_mmu_has_feature(device, KGSL_MMU_NEED_GUARD_PAGE) ||
(flags & KGSL_MEMFLAGS_GUARD_PAGE))
memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
if (flags & KGSL_MEMFLAGS_SECURE)
memdesc->priv |= KGSL_MEMDESC_SECURE;
memdesc->flags = flags;
/*
* For io-coherent buffers don't set memdesc->dev, so that we skip DMA
* cache operations at allocation time
*/
if (!(flags & KGSL_MEMFLAGS_IOCOHERENT))
memdesc->dev = &device->pdev->dev;
align = max_t(unsigned int,
kgsl_memdesc_get_align(memdesc), ilog2(PAGE_SIZE));
kgsl_memdesc_set_align(memdesc, align);
spin_lock_init(&memdesc->lock);
}
void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
{
if (!memdesc || !memdesc->size)
return;
/* Assume if no operations were specified something went bad early */
if (!memdesc->ops)
return;
if (memdesc->ops->put_gpuaddr)
memdesc->ops->put_gpuaddr(memdesc);
if (memdesc->ops->free)
memdesc->ops->free(memdesc);
}
int
kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
uint32_t *dst,
uint64_t offsetbytes)
{
uint32_t *src;
if (WARN_ON(memdesc == NULL || memdesc->hostptr == NULL ||
dst == NULL))
return -EINVAL;
WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
if (offsetbytes % sizeof(uint32_t) != 0)
return -EINVAL;
WARN_ON(offsetbytes > (memdesc->size - sizeof(uint32_t)));
if (offsetbytes > (memdesc->size - sizeof(uint32_t)))
return -ERANGE;
/*
* We are reading shared memory between CPU and GPU.
* Make sure reads before this are complete
*/
rmb();
src = (uint32_t *)(memdesc->hostptr + offsetbytes);
*dst = *src;
return 0;
}
void
kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
uint64_t offsetbytes,
uint32_t src)
{
/* Quietly return if the memdesc isn't valid */
if (IS_ERR_OR_NULL(memdesc) || WARN_ON(!memdesc->hostptr))
return;
if (WARN_ON(!IS_ALIGNED(offsetbytes, sizeof(u32))))
return;
if (WARN_ON(offsetbytes > (memdesc->size - sizeof(u32))))
return;
*((u32 *) (memdesc->hostptr + offsetbytes)) = src;
/* Make sure the write is posted before continuing */
wmb();
}
int
kgsl_sharedmem_readq(const struct kgsl_memdesc *memdesc,
uint64_t *dst,
uint64_t offsetbytes)
{
uint64_t *src;
if (WARN_ON(memdesc == NULL || memdesc->hostptr == NULL ||
dst == NULL))
return -EINVAL;
WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
if (offsetbytes % sizeof(uint32_t) != 0)
return -EINVAL;
WARN_ON(offsetbytes > (memdesc->size - sizeof(uint32_t)));
if (offsetbytes > (memdesc->size - sizeof(uint32_t)))
return -ERANGE;
/*
* We are reading shared memory between CPU and GPU.
* Make sure reads before this are complete
*/
rmb();
src = (uint64_t *)(memdesc->hostptr + offsetbytes);
*dst = *src;
return 0;
}
void
kgsl_sharedmem_writeq(const struct kgsl_memdesc *memdesc,
uint64_t offsetbytes,
uint64_t src)
{
/* Quietly return if the memdesc isn't valid */
if (IS_ERR_OR_NULL(memdesc) || WARN_ON(!memdesc->hostptr))
return;
if (WARN_ON(!IS_ALIGNED(offsetbytes, sizeof(u64))))
return;
if (WARN_ON(offsetbytes > (memdesc->size - sizeof(u64))))
return;
*((u64 *) (memdesc->hostptr + offsetbytes)) = src;
/* Make sure the write is posted before continuing */
wmb();
}
void kgsl_get_memory_usage(char *name, size_t name_size, uint64_t memflags)
{
unsigned int type = FIELD_GET(KGSL_MEMTYPE_MASK, memflags);
struct kgsl_memtype *memtype;
int i;
for (i = 0; memtype_attrs[i]; i++) {
memtype = container_of(memtype_attrs[i], struct kgsl_memtype, attr);
if (memtype->type == type) {
strscpy(name, memtype->attr.name, name_size);
return;
}
}
snprintf(name, name_size, "VK/others(%3d)", type);
}
int kgsl_memdesc_sg_dma(struct kgsl_memdesc *memdesc,
phys_addr_t addr, u64 size)
{
int ret;
struct page *page = phys_to_page(addr);
memdesc->sgt = kmalloc(sizeof(*memdesc->sgt), GFP_KERNEL);
if (memdesc->sgt == NULL)
return -ENOMEM;
ret = sg_alloc_table(memdesc->sgt, 1, GFP_KERNEL);
if (ret) {
kfree(memdesc->sgt);
memdesc->sgt = NULL;
return ret;
}
sg_set_page(memdesc->sgt->sgl, page, (size_t) size, 0);
return 0;
}
static void _kgsl_contiguous_free(struct kgsl_memdesc *memdesc)
{
dma_free_attrs(memdesc->dev, memdesc->size,
memdesc->hostptr, memdesc->physaddr,
memdesc->attrs);
sg_free_table(memdesc->sgt);
kfree(memdesc->sgt);
memdesc->sgt = NULL;
}
static void kgsl_contiguous_free(struct kgsl_memdesc *memdesc)
{
if (!memdesc->hostptr)
return;
if (memdesc->priv & KGSL_MEMDESC_MAPPED)
return;
atomic_long_sub(memdesc->size, &kgsl_driver.stats.coherent);
_kgsl_contiguous_free(memdesc);
}
#ifdef CONFIG_QCOM_KGSL_USE_SHMEM
#include <linux/shmem_fs.h>
#include <trace/hooks/mm.h>
static int _kgsl_shmem_alloc_page(struct kgsl_memdesc *memdesc, u32 order)
{
int pcount;
struct page *page;
gfp_t gfp_mask = kgsl_gfp_mask(order);
if (fatal_signal_pending(current))
return -ENOMEM;
/* Allocate non compound page to split 4K page chunks */
gfp_mask &= ~__GFP_COMP;
page = alloc_pages(gfp_mask, order);
if (page == NULL) {
/* Retry with lower order pages */
if (order > 1)
return -EAGAIN;
else
return -ENOMEM;
}
/* Split non-compound higher-order pages to 4k pages */
split_page(page, order);
for (pcount = 0; pcount < (1 << order); pcount++) {
clear_highpage(&page[pcount]);
list_add_tail(&page[pcount].lru, &memdesc->shmem_page_list);
}
return pcount;
}
static int kgsl_shmem_alloc_pages(struct kgsl_memdesc *memdesc)
{
int ret, count = 0;
u32 size, align, order;
/* Length of remaining unallocated memdesc pages */
u64 len = memdesc->size - ((u64)memdesc->page_count << PAGE_SHIFT);
/* 4k allocation managed by the SHMEM */
if (len == PAGE_SIZE)
return 0;
/* Start with 1MB alignment to get the biggest page we can */
align = ilog2(SZ_1M);
size = kgsl_get_page_size(len, align);
order = get_order(size);
while (len) {
ret = _kgsl_shmem_alloc_page(memdesc, order);
if (ret == -EAGAIN) {
size = PAGE_SIZE << --order;
size = kgsl_get_page_size(size, ilog2(size));
align = ilog2(size);
continue;
} else if (ret <= 0) {
return -ENOMEM;
}
count += ret;
len -= size;
size = kgsl_get_page_size(len, align);
align = ilog2(size);
order = get_order(size);
}
return count;
}
static void kgsl_shmem_fill_page(void *ptr,
struct shmem_inode_info *inode, struct folio **folio)
{
struct kgsl_memdesc *memdesc = (struct kgsl_memdesc *)inode->android_vendor_data1;
if (IS_ERR_OR_NULL(memdesc))
return;
if (list_empty(&memdesc->shmem_page_list)) {
int ret = kgsl_shmem_alloc_pages(memdesc);
if (ret <= 0)
return;
}
*folio = list_first_entry(&memdesc->shmem_page_list, struct folio, lru);
list_del(&(*folio)->lru);
}
void kgsl_register_shmem_callback(void)
{
register_trace_android_rvh_shmem_get_folio(kgsl_shmem_fill_page, NULL);
}
static int kgsl_alloc_page(struct kgsl_memdesc *memdesc, int *page_size,
struct page **pages, unsigned int pages_len,
unsigned int *align, unsigned int page_off)
{
struct page *page;
u32 pcount = (memdesc->size >> PAGE_SHIFT) - memdesc->page_count;
if (pages == NULL)
return -EINVAL;
if (fatal_signal_pending(current))
return -ENOMEM;
page = shmem_read_mapping_page_gfp(memdesc->shmem_filp->f_mapping, page_off,
kgsl_gfp_mask(0));
if (IS_ERR(page))
return PTR_ERR(page);
/* Clear only shmem driver allocated pages */
if ((memdesc->size == PAGE_SIZE) ||
(list_empty(&memdesc->shmem_page_list) && (pcount > 1)))
clear_highpage(page);
kgsl_page_sync(memdesc->dev, page, PAGE_SIZE, DMA_TO_DEVICE);
*page_size = PAGE_SIZE;
*pages = page;
return 1;
}
static int kgsl_memdesc_file_setup(struct kgsl_memdesc *memdesc)
{
int ret;
memdesc->shmem_filp = shmem_file_setup("kgsl-3d0", memdesc->size,
VM_NORESERVE);
if (IS_ERR(memdesc->shmem_filp)) {
ret = PTR_ERR(memdesc->shmem_filp);
memdesc->shmem_filp = NULL;
return ret;
}
INIT_LIST_HEAD(&memdesc->shmem_page_list);
SHMEM_I(memdesc->shmem_filp->f_mapping->host)->android_vendor_data1 = (u64)memdesc;
mapping_set_unevictable(memdesc->shmem_filp->f_mapping);
return 0;
}
static void kgsl_free_page(struct page *p)
{
put_page(p);
}
static void _kgsl_free_pages(struct kgsl_memdesc *memdesc)
{
int i;
WARN(!list_empty(&memdesc->shmem_page_list),
"KGSL shmem page list is not empty\n");
for (i = 0; i < memdesc->page_count; i++)
if (memdesc->pages[i])
put_page(memdesc->pages[i]);
SHMEM_I(memdesc->shmem_filp->f_mapping->host)->android_vendor_data1 = 0;
fput(memdesc->shmem_filp);
}
/* If CONFIG_QCOM_KGSL_USE_SHMEM is defined we don't use compound pages */
static u32 kgsl_get_page_order(struct page *page)
{
return 0;
}
#else
void kgsl_register_shmem_callback(void) { }
static int kgsl_alloc_page(struct kgsl_memdesc *memdesc, int *page_size,
struct page **pages, unsigned int pages_len,
unsigned int *align, unsigned int page_off)
{
if (fatal_signal_pending(current))
return -ENOMEM;
return kgsl_pool_alloc_page(page_size, pages,
pages_len, align, memdesc->dev);
}
static int kgsl_memdesc_file_setup(struct kgsl_memdesc *memdesc)
{
return 0;
}
static void kgsl_free_page(struct page *p)
{
kgsl_pool_free_page(p);
}
static void _kgsl_free_pages(struct kgsl_memdesc *memdesc)
{
kgsl_pool_free_pages(memdesc->pages, memdesc->page_count);
}
static u32 kgsl_get_page_order(struct page *page)
{
return compound_order(page);
}
#endif
void kgsl_page_sync(struct device *dev, struct page *page,
size_t size, enum dma_data_direction dir)
{
struct scatterlist sg;
/* The caller may choose not to specify a device on purpose */
if (!dev)
return;
sg_init_table(&sg, 1);
sg_set_page(&sg, page, size, 0);
sg_dma_address(&sg) = page_to_phys(page);
/*
* APIs for Cache Maintenance Operations are updated in kernel
* version 6.1. Prior to 6.1, dma_sync_sg_for_device() with
* DMA_BIDIRECTIONAL as direction triggers cache invalidate and
* clean whereas in kernel version 6.1, it triggers only cache
* clean. Hence use dma_sync_sg_for_cpu() for cache invalidate
* for kernel version 6.1 and above.
*/
if ((dir == DMA_BIDIRECTIONAL) &&
KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) {
dma_sync_sg_for_device(dev, &sg, 1, DMA_TO_DEVICE);
dma_sync_sg_for_cpu(dev, &sg, 1, DMA_FROM_DEVICE);
} else
dma_sync_sg_for_device(dev, &sg, 1, dir);
}
void kgsl_zero_page(struct page *p, unsigned int order,
struct device *dev)
{
int i;
for (i = 0; i < (1 << order); i++) {
struct page *page = nth_page(p, i);
clear_highpage(page);
}
kgsl_page_sync(dev, p, PAGE_SIZE << order, DMA_TO_DEVICE);
}
gfp_t kgsl_gfp_mask(int page_order)
{
gfp_t gfp_mask = __GFP_HIGHMEM;
if (page_order > 0) {
gfp_mask |= __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN;
gfp_mask &= ~__GFP_RECLAIM;
} else
gfp_mask |= GFP_KERNEL;
if (kgsl_sharedmem_noretry_flag)
gfp_mask |= __GFP_NORETRY | __GFP_NOWARN;
return gfp_mask;
}
static int _kgsl_alloc_pages(struct kgsl_memdesc *memdesc,
struct page ***pages)
{
int count = 0;
int npages = memdesc->size >> PAGE_SHIFT;
struct page **local = kvcalloc(npages, sizeof(*local), GFP_KERNEL);
u32 page_size, align;
u64 len = memdesc->size;
bool memwq_flush_done = false;
if (!local)
return -ENOMEM;
count = kgsl_memdesc_file_setup(memdesc);
if (count) {
kvfree(local);
return count;
}
/* Start with 1MB alignment to get the biggest page we can */
align = ilog2(SZ_1M);
page_size = kgsl_get_page_size(len, align);
while (len) {
int ret = kgsl_alloc_page(memdesc, &page_size, &local[count],
npages, &align, count);
if (ret == -EAGAIN)
continue;
else if (ret <= 0) {
int i;
/* if OOM, retry once after flushing lockless_workqueue */
if (ret == -ENOMEM && !memwq_flush_done) {
flush_workqueue(kgsl_driver.lockless_workqueue);
memwq_flush_done = true;
continue;
}
for (i = 0; i < count; ) {
int n = 1 << kgsl_get_page_order(local[i]);
kgsl_free_page(local[i]);
i += n;
}
kvfree(local);
if (!kgsl_sharedmem_noretry_flag)
pr_err_ratelimited("kgsl: out of memory: only allocated %lldKb of %lldKb requested\n",
(memdesc->size - len) >> 10, memdesc->size >> 10);
if (memdesc->shmem_filp)
fput(memdesc->shmem_filp);
return -ENOMEM;
}
count += ret;
memdesc->page_count += ret;
npages -= ret;
len -= page_size;
page_size = kgsl_get_page_size(len, align);
}
*pages = local;
return count;
}
static void kgsl_free_pages(struct kgsl_memdesc *memdesc)
{
kgsl_paged_unmap_kernel(memdesc);
WARN_ON(memdesc->hostptr);
if (memdesc->priv & KGSL_MEMDESC_MAPPED)
return;
atomic_long_sub(memdesc->size, &kgsl_driver.stats.page_alloc);
_kgsl_free_pages(memdesc);
memdesc->page_count = 0;
kvfree(memdesc->pages);
memdesc->pages = NULL;
}
static void kgsl_free_system_pages(struct kgsl_memdesc *memdesc)
{
int i;
kgsl_paged_unmap_kernel(memdesc);
WARN_ON(memdesc->hostptr);
if (memdesc->priv & KGSL_MEMDESC_MAPPED)
return;
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;
}
void kgsl_unmap_and_put_gpuaddr(struct kgsl_memdesc *memdesc)
{
if (!memdesc->size || !memdesc->gpuaddr)
return;
if (WARN_ON(kgsl_memdesc_is_global(memdesc)))
return;
/*
* Don't release the GPU address if the memory fails to unmap because
* the IOMMU driver will BUG later if we reallocated the address and
* tried to map it
*/
if (!kgsl_memdesc_is_reclaimed(memdesc) &&
kgsl_mmu_unmap(memdesc->pagetable, memdesc))
return;
kgsl_mmu_put_gpuaddr(memdesc->pagetable, memdesc);
memdesc->gpuaddr = 0;
memdesc->pagetable = NULL;
}
static const struct kgsl_memdesc_ops kgsl_contiguous_ops = {
.free = kgsl_contiguous_free,
.vmflags = VM_DONTDUMP | VM_PFNMAP | VM_DONTEXPAND | VM_DONTCOPY,
.vmfault = kgsl_contiguous_vmfault,
.put_gpuaddr = kgsl_unmap_and_put_gpuaddr,
};
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static void kgsl_free_pages_from_sgt(struct kgsl_memdesc *memdesc)
{
int i;
struct scatterlist *sg;
if (WARN_ON(!memdesc->sgt))
return;
for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) {
/*
* sg_alloc_table_from_pages() will collapse any physically
* adjacent pages into a single scatterlist entry. We cannot
* just call __free_pages() on the entire set since we cannot
* ensure that the size is a whole order. Instead, free each
* page or compound page group individually.
*/
struct page *p = sg_page(sg), *next;
unsigned int count;
unsigned int j = 0;
while (j < (sg->length/PAGE_SIZE)) {
count = 1 << compound_order(p);
next = nth_page(p, count);
kgsl_free_page(p);
p = next;
j += count;
}
}
if (memdesc->shmem_filp)
fput(memdesc->shmem_filp);
}
static void kgsl_free_secure_system_pages(struct kgsl_memdesc *memdesc)
{
int i;
struct scatterlist *sg;
int ret;
if (memdesc->priv & KGSL_MEMDESC_MAPPED)
return;
ret = kgsl_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_pages(struct kgsl_memdesc *memdesc)
{
int ret;
if (memdesc->priv & KGSL_MEMDESC_MAPPED)
return;
ret = kgsl_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);
kgsl_free_pages_from_sgt(memdesc);
sg_free_table(memdesc->sgt);
kfree(memdesc->sgt);
memdesc->sgt = NULL;
}
void kgsl_free_secure_page(struct page *page)
{
struct sg_table sgt;
struct scatterlist sgl;
if (!page)
return;
sgt.sgl = &sgl;
sgt.nents = 1;
sgt.orig_nents = 1;
sg_init_table(&sgl, 1);
sg_set_page(&sgl, page, PAGE_SIZE, 0);
kgsl_unlock_sgt(&sgt);
__free_page(page);
}
struct page *kgsl_alloc_secure_page(void)
{
struct page *page;
struct sg_table sgt;
struct scatterlist sgl;
int status;
page = alloc_page(GFP_KERNEL | __GFP_ZERO |
__GFP_NORETRY | __GFP_HIGHMEM);
if (!page)
return NULL;
sgt.sgl = &sgl;
sgt.nents = 1;
sgt.orig_nents = 1;
sg_init_table(&sgl, 1);
sg_set_page(&sgl, page, PAGE_SIZE, 0);
status = kgsl_lock_sgt(&sgt, PAGE_SIZE);
if (status) {
if (status == -EADDRNOTAVAIL)
return NULL;
__free_page(page);
return NULL;
}
return page;
}
static const 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 const struct kgsl_memdesc_ops kgsl_secure_page_ops = {
.free = kgsl_free_secure_pages,
/* FIXME: Make sure vmflags / vmfault does the right thing here */
.put_gpuaddr = kgsl_unmap_and_put_gpuaddr,
};
#else
void kgsl_free_secure_page(struct page *page)
{
}
struct page *kgsl_alloc_secure_page(void)
{
return NULL;
}
#endif
static const struct kgsl_memdesc_ops kgsl_page_ops = {
.free = kgsl_free_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,
.put_gpuaddr = kgsl_unmap_and_put_gpuaddr,
};
static const 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(struct kgsl_memdesc *memdesc, struct page ***pages)
{
struct page **local;
int i, npages = memdesc->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;
if (!fatal_signal_pending(current))
local[i] = alloc_pages(gfp, get_order(PAGE_SIZE));
else
local[i] = NULL;
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 */
kgsl_page_sync(memdesc->dev, local[i], PAGE_SIZE, DMA_TO_DEVICE);
}
*pages = local;
memdesc->page_count = npages;
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)
{
struct page **pages;
int count;
struct sg_table *sgt;
int ret;
size = PAGE_ALIGN(size);
if (!size || size > UINT_MAX)
return -EINVAL;
kgsl_memdesc_init(device, memdesc, flags);
memdesc->priv |= priv;
memdesc->size = size;
if (priv & KGSL_MEMDESC_SYSMEM) {
memdesc->ops = &kgsl_secure_system_ops;
count = kgsl_system_alloc_pages(memdesc, &pages);
} else {
memdesc->ops = &kgsl_secure_page_ops;
count = _kgsl_alloc_pages(memdesc, &pages);
}
if (count < 0)
return count;
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) {
_kgsl_free_pages(memdesc);
kvfree(pages);
return -ENOMEM;
}
ret = sg_alloc_table_from_pages(sgt, pages, count, 0, size, GFP_KERNEL);
if (ret) {
kfree(sgt);
_kgsl_free_pages(memdesc);
kvfree(pages);
return ret;
}
/* Now that we've moved to a sg table don't need the pages anymore */
kvfree(pages);
memdesc->sgt = sgt;
ret = kgsl_lock_sgt(sgt, size);
if (ret) {
if (ret != -EADDRNOTAVAIL)
kgsl_free_pages_from_sgt(memdesc);
sg_free_table(sgt);
kfree(sgt);
memdesc->sgt = NULL;
return ret;
}
KGSL_STATS_ADD(size, &kgsl_driver.stats.secure,
&kgsl_driver.stats.secure_max);
return 0;
}
static int kgsl_allocate_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
return kgsl_alloc_secure_pages(device, memdesc, size, flags, priv);
}
#else
static int kgsl_allocate_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
return -ENODEV;
}
#endif
static int kgsl_alloc_pages(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
struct page **pages;
int count;
size = PAGE_ALIGN(size);
if (!size || size > UINT_MAX)
return -EINVAL;
kgsl_memdesc_init(device, memdesc, flags);
memdesc->priv |= priv;
memdesc->size = size;
if (priv & KGSL_MEMDESC_SYSMEM) {
memdesc->ops = &kgsl_system_ops;
count = kgsl_system_alloc_pages(memdesc, &pages);
} else {
memdesc->ops = &kgsl_page_ops;
count = _kgsl_alloc_pages(memdesc, &pages);
}
if (count < 0)
return count;
memdesc->pages = pages;
KGSL_STATS_ADD(size, &kgsl_driver.stats.page_alloc,
&kgsl_driver.stats.page_alloc_max);
return 0;
}
static int _kgsl_alloc_contiguous(struct device *dev,
struct kgsl_memdesc *memdesc, u64 size, unsigned long attrs)
{
int ret;
phys_addr_t phys;
void *ptr;
ptr = dma_alloc_attrs(dev, (size_t) size, &phys,
GFP_KERNEL, attrs);
if (!ptr)
return -ENOMEM;
memdesc->size = size;
memdesc->dev = dev;
memdesc->hostptr = ptr;
memdesc->physaddr = phys;
memdesc->gpuaddr = phys;
memdesc->attrs = attrs;
ret = kgsl_memdesc_sg_dma(memdesc, phys, size);
if (ret)
dma_free_attrs(dev, (size_t) size, ptr, phys, attrs);
return ret;
}
static int kgsl_alloc_contiguous(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
int ret;
size = PAGE_ALIGN(size);
if (!size || size > UINT_MAX)
return -EINVAL;
kgsl_memdesc_init(device, memdesc, flags);
memdesc->priv |= priv;
memdesc->ops = &kgsl_contiguous_ops;
ret = _kgsl_alloc_contiguous(&device->pdev->dev, memdesc, size, 0);
if (!ret)
KGSL_STATS_ADD(size, &kgsl_driver.stats.coherent,
&kgsl_driver.stats.coherent_max);
return ret;
}
int kgsl_allocate_user(struct kgsl_device *device, struct kgsl_memdesc *memdesc,
u64 size, u64 flags, u32 priv)
{
if (device->mmu.type == KGSL_MMU_TYPE_NONE)
return kgsl_alloc_contiguous(device, memdesc, size, flags,
priv);
else if (flags & KGSL_MEMFLAGS_SECURE)
return kgsl_allocate_secure(device, memdesc, size, flags, priv);
return kgsl_alloc_pages(device, memdesc, size, flags, priv);
}
int kgsl_allocate_kernel(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
int ret;
ret = kgsl_allocate_user(device, memdesc, size, flags, priv);
if (ret)
return ret;
if (memdesc->ops->map_kernel) {
ret = memdesc->ops->map_kernel(memdesc);
if (ret) {
kgsl_sharedmem_free(memdesc);
return ret;
}
}
return 0;
}
int kgsl_memdesc_init_fixed(struct kgsl_device *device,
struct platform_device *pdev, const char *resource,
struct kgsl_memdesc *memdesc)
{
u32 entry[2];
if (of_property_read_u32_array(pdev->dev.of_node,
resource, entry, 2))
return -ENODEV;
kgsl_memdesc_init(device, memdesc, 0);
memdesc->physaddr = entry[0];
memdesc->size = entry[1];
return kgsl_memdesc_sg_dma(memdesc, entry[0], entry[1]);
}
struct kgsl_memdesc *kgsl_allocate_global_fixed(struct kgsl_device *device,
const char *resource, const char *name)
{
struct kgsl_global_memdesc *gmd = kzalloc(sizeof(*gmd), GFP_KERNEL);
int ret;
if (!gmd)
return ERR_PTR(-ENOMEM);
ret = kgsl_memdesc_init_fixed(device, device->pdev, resource,
&gmd->memdesc);
if (ret) {
kfree(gmd);
return ERR_PTR(ret);
}
gmd->memdesc.priv = KGSL_MEMDESC_GLOBAL;
gmd->name = name;
/*
* No lock here, because this function is only called during probe/init
* while the caller is holding the mutex
*/
list_add_tail(&gmd->node, &device->globals);
kgsl_mmu_map_global(device, &gmd->memdesc, 0);
return &gmd->memdesc;
}
static struct kgsl_memdesc *
kgsl_allocate_secure_global(struct kgsl_device *device,
u64 size, u64 flags, u32 priv, const char *name)
{
struct kgsl_global_memdesc *md;
int ret;
md = kzalloc(sizeof(*md), GFP_KERNEL);
if (!md)
return ERR_PTR(-ENOMEM);
/* 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) {
kfree(md);
return ERR_PTR(ret);
}
md->name = name;
/*
* No lock here, because this function is only called during probe/init
* while the caller is holding the mutex
*/
list_add_tail(&md->node, &device->globals);
/*
* No offset needed, we'll get an address inside of the pagetable
* normally
*/
kgsl_mmu_map_global(device, &md->memdesc, 0);
kgsl_trace_gpu_mem_total(device, md->memdesc.size);
return &md->memdesc;
}
struct kgsl_memdesc *kgsl_allocate_global(struct kgsl_device *device,
u64 size, u32 padding, u64 flags, u32 priv, const char *name)
{
int ret;
struct kgsl_global_memdesc *md;
if (flags & KGSL_MEMFLAGS_SECURE)
return kgsl_allocate_secure_global(device, size, flags, priv,
name);
md = kzalloc(sizeof(*md), GFP_KERNEL);
if (!md)
return ERR_PTR(-ENOMEM);
/*
* 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) {
kfree(md);
return ERR_PTR(ret);
}
md->name = name;
/*
* No lock here, because this function is only called during probe/init
* while the caller is holding the mute
*/
list_add_tail(&md->node, &device->globals);
kgsl_mmu_map_global(device, &md->memdesc, padding);
kgsl_trace_gpu_mem_total(device, md->memdesc.size);
return &md->memdesc;
}
void kgsl_free_globals(struct kgsl_device *device)
{
struct kgsl_global_memdesc *md, *tmp;
list_for_each_entry_safe(md, tmp, &device->globals, node) {
kgsl_sharedmem_free(&md->memdesc);
list_del(&md->node);
kfree(md);
}
}