Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8df452d7 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Allocate gpu virtual memory from user virtual space"

parents 75cb9c76 fef41593
Loading
Loading
Loading
Loading
+154 −53
Original line number Diff line number Diff line
@@ -89,6 +89,11 @@ static int kgsl_setup_dma_buf(struct kgsl_mem_entry *entry,

static const struct file_operations kgsl_fops;

static int __kgsl_check_collision(struct kgsl_process_private *private,
			struct kgsl_mem_entry *entry,
			unsigned long *gpuaddr, unsigned long len,
			int flag_top_down);

static int kgsl_memfree_hist_init(void)
{
	void *base;
@@ -297,7 +302,25 @@ kgsl_mem_entry_track_gpuaddr(struct kgsl_process_private *process,
		goto done;
	}
	if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
		ret = kgsl_mmu_get_gpuaddr(process->pagetable, &entry->memdesc);
		ret = -ENOMEM;
		/*
		 * For external allocations use mmu gen pool to assign
		 * virtual address
		 */
		if (KGSL_MEMFLAGS_USERMEM_MASK & entry->memdesc.flags)
			ret = kgsl_mmu_get_gpuaddr(process->pagetable,
						&entry->memdesc);
		if (-ENOMEM == ret) {
			unsigned long gpuaddr = 0;
			size_t size = entry->memdesc.size;
			if (kgsl_memdesc_has_guard_page(&entry->memdesc))
				size += PAGE_SIZE;

			ret = __kgsl_check_collision(process, NULL, &gpuaddr,
						size, 0);
			if (!ret)
				entry->memdesc.gpuaddr = gpuaddr;
		}
		if (ret)
			goto done;
	}
@@ -337,7 +360,9 @@ kgsl_mem_entry_untrack_gpuaddr(struct kgsl_process_private *process,
{
	assert_spin_locked(&process->mem_lock);
	if (entry->memdesc.gpuaddr) {
		kgsl_mmu_put_gpuaddr(process->pagetable, &entry->memdesc);
		if (KGSL_MEMFLAGS_USERMEM_MASK & entry->memdesc.flags)
			kgsl_mmu_put_gpuaddr(process->pagetable,
					&entry->memdesc);
		rb_erase(&entry->node, &entry->priv->mem_rb);
	}
}
@@ -1229,6 +1254,19 @@ kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id)
	return entry;
}

/**
 * kgsl_mem_entry_unset_pend() - Unset the pending free flag of an entry
 * @entry - The memory entry
 */
static inline void kgsl_mem_entry_unset_pend(struct kgsl_mem_entry *entry)
{
	if (entry == NULL)
		return;
	spin_lock(&entry->priv->mem_lock);
	entry->pending_free = 0;
	spin_unlock(&entry->priv->mem_lock);
}

/**
 * kgsl_mem_entry_set_pend() - Set the pending free flag of a memory entry
 * @entry - The memory entry
@@ -2295,6 +2333,10 @@ long kgsl_ioctl_cmdstream_freememontimestamp_ctxtid(
		param->timestamp);
	result = kgsl_add_event(dev_priv->device, &context->events,
		param->timestamp, kgsl_freemem_event_cb, entry);

	if (result)
		kgsl_mem_entry_unset_pend(entry);

	kgsl_mem_entry_put(entry);

out:
@@ -3652,10 +3694,20 @@ get_mmap_entry(struct kgsl_process_private *private,
		goto err_put;
	}

	if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
		if (len != kgsl_memdesc_mmapsize(&entry->memdesc)) {
			ret = -ERANGE;
			goto err_put;
		}
	} else if (len != kgsl_memdesc_mmapsize(&entry->memdesc) &&
		len != entry->memdesc.size) {
		/*
		 * If cpu_map != gpumap then user can map either the
		 * mmapsize or the entry size
		 */
		ret = -ERANGE;
		goto err_put;
	}

	*out_entry = entry;
	return 0;
@@ -3671,6 +3723,98 @@ mmap_range_valid(unsigned long addr, unsigned long len)
		KGSL_SVM_UPPER_BOUND);
}

/**
 * __kgsl_check_collision() - Find a non colliding gpuaddr for the process
 * @private: Process private pointer contaning the list of allocations
 * @entry: The entry colliding with given address
 * @gpuaddr: In out parameter. The In parameter contains the desired gpuaddr
 * if the gpuaddr collides then the out parameter contains the non colliding
 * address
 * @len: Length of address range
 * @flag_top_down: Indicates whether free address range should be checked in
 * top down or bottom up fashion
 */
static int __kgsl_check_collision(struct kgsl_process_private *private,
			struct kgsl_mem_entry *entry,
			unsigned long *gpuaddr, unsigned long len,
			int flag_top_down)
{
	int ret = 0;
	unsigned long addr = *gpuaddr;
	struct kgsl_mem_entry *collision_entry = entry;

	if (!addr) {
		addr = flag_top_down ? (KGSL_SVM_UPPER_BOUND - len) : PAGE_SIZE;
		collision_entry = NULL;
	}

	do {
		/*
		 * If address collided with an existing entry then find a new
		 * one
		 */
		if (collision_entry) {
			/*
			 * If top down search then next address to consider
			 * is lower. The highest lower address possible is the
			 * colliding entry address - the length of
			 * allocation
			 */
			if (flag_top_down) {
				addr = collision_entry->memdesc.gpuaddr - len;
				/* Check for loopback */
				if (addr > collision_entry->memdesc.gpuaddr) {
					KGSL_CORE_ERR_ONCE(
					"Underflow err ent:%x/%zx, addr:%lx/%lx\n",
					collision_entry->memdesc.gpuaddr,
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc),
					addr, len);
					*gpuaddr =
						KGSL_SVM_UPPER_BOUND;
					ret = -EAGAIN;
					break;
				}
			} else {
				/*
				 * Bottom up mode the next address to consider
				 * is higher. The lowest higher address possible
				 * colliding entry address + the size of the
				 * colliding entry
				 */
				addr = collision_entry->memdesc.gpuaddr +
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc);
				/* overflow check */
				if (addr < collision_entry->memdesc.gpuaddr ||
					!mmap_range_valid(addr, len)) {
					KGSL_CORE_ERR_ONCE(
					"Overflow err ent:%x/%zx, addr:%lx/%lx\n",
					collision_entry->memdesc.gpuaddr,
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc),
					addr, len);
					*gpuaddr =
						KGSL_SVM_UPPER_BOUND;
					ret = -EAGAIN;
					break;
				}
			}
		}
		if (kgsl_sharedmem_region_empty(private, addr, len,
						&collision_entry)) {
			/* no collision with addr then return */
			*gpuaddr = addr;
			break;
		} else if (!collision_entry) {
			ret = -ENOENT;
			break;
		}
	} while (1);

	return ret;
}

/**
 * kgsl_check_gpu_addr_collision() - Check if an address range collides with
 * existing allocations of a process
@@ -3732,56 +3876,13 @@ static int kgsl_check_gpu_addr_collision(
		 */
		len += 1 << align;

		/*
		 * Loop through the gpu map address space to find an unmapped
		 * region of size len, lopping is done either top down or bottom
		 * up based on flag_top_down setting
		 */
		do {
			if (!collision_entry) {
				ret = -ENOENT;
				break;
			}
			if (flag_top_down) {
				addr = collision_entry->memdesc.gpuaddr - len;
				if (addr > collision_entry->memdesc.gpuaddr) {
					KGSL_CORE_ERR_ONCE(
					"Underflow err ent:%x/%zx, addr:%lx/%lx align:%u",
					collision_entry->memdesc.gpuaddr,
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc),
					addr, len, align);
					*gpumap_free_addr =
						KGSL_SVM_UPPER_BOUND;
					ret = -EAGAIN;
					break;
				}
			} else {
				addr = collision_entry->memdesc.gpuaddr +
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc);
				/* overflow check */
				if (addr < collision_entry->memdesc.gpuaddr ||
					!mmap_range_valid(addr, len)) {
					KGSL_CORE_ERR_ONCE(
					"Overflow err ent:%x/%zx, addr:%lx/%lx align:%u",
					collision_entry->memdesc.gpuaddr,
					kgsl_memdesc_mmapsize(
						&collision_entry->memdesc),
					addr, len, align);
					*gpumap_free_addr =
						KGSL_SVM_UPPER_BOUND;
					ret = -EAGAIN;
					break;
				}
			}
			collision_entry = NULL;
			if (kgsl_sharedmem_region_empty(private, addr, len,
							&collision_entry)) {
		ret = __kgsl_check_collision(private, collision_entry, &addr,
						len, flag_top_down);
		if (!ret || -EAGAIN == ret) {
			*gpumap_free_addr = addr;
				break;
			ret = -EAGAIN;
		}
		} while (1);

		spin_unlock(&private->mem_lock);
	}
	return ret;
+2 −1
Original line number Diff line number Diff line
@@ -134,7 +134,8 @@ struct kgsl_memdesc_ops {
#define KGSL_MEMDESC_FROZEN BIT(2)
/* The memdesc is mapped into a pagetable */
#define KGSL_MEMDESC_MAPPED BIT(3)

/* Indicates gpuaddr is assigned via gen pool */
#define KGSL_MEMDESC_GENPOOL_ALLOC BIT(4)
/* shared memory allocation */
struct kgsl_memdesc {
	struct kgsl_pagetable *pagetable;
+3 −26
Original line number Diff line number Diff line
@@ -1341,13 +1341,6 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
	if (!iommu)
		return -ENOMEM;

	/*
	 * These are constant for all cases so set them early so
	 * kgsl_set_register_map() can use them
	 */
	mmu->global_pt_base = KGSL_IOMMU_GLOBAL_MEM_BASE;
	mmu->global_pt_size = KGSL_GLOBAL_PT_SIZE;

	mmu->priv = iommu;
	status = kgsl_get_iommu_ctxt(mmu);
	if (status)
@@ -1370,25 +1363,9 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
						"gtcu_iface_clk") >= 0)
		iommu->gtcu_iface_clk = clk_get(&pdev->dev, "gtcu_iface_clk");

	/*
	 * For IOMMU per-process pagetables, the allocatable range
	 * and the kernel global range must both be outside
	 * the userspace address range. There is a 1Mb gap
	 * between these address ranges to make overrun
	 * detection easier.
	 * For the shared pagetable case use 2GB and because
	 * mirroring the CPU address space is not possible and
	 * we're better off with extra room.
	 */
	if (mmu->pt_per_process) {
		mmu->pt_base = KGSL_PER_PROCESS_PT_BASE;
		mmu->pt_size = KGSL_PER_PROCESS_PT_SIZE;
		mmu->use_cpu_map = true;
	} else {
		mmu->pt_base = KGSL_PAGETABLE_BASE;
		mmu->pt_size = SZ_2G;
		mmu->use_cpu_map = false;
	}
	mmu->pt_base = KGSL_MMU_MAPPED_MEM_BASE;
	mmu->pt_size = KGSL_MMU_MAPPED_MEM_SIZE;
	mmu->use_cpu_map = mmu->pt_per_process;

	status = kgsl_iommu_init_sync_lock(mmu);
	if (status)
+0 −5
Original line number Diff line number Diff line
@@ -16,11 +16,6 @@
#include <linux/qcom_iommu.h>

/* Pagetable virtual base */
#define KGSL_PAGETABLE_BASE     0x10000000
#define KGSL_PER_PROCESS_PT_BASE	0xC0000000
#define KGSL_PER_PROCESS_PT_SIZE	(KGSL_IOMMU_GLOBAL_MEM_BASE -	\
					 KGSL_PER_PROCESS_PT_BASE - SZ_1M)

#define KGSL_IOMMU_CTX_OFFSET_V0	0
#define KGSL_IOMMU_CTX_OFFSET_V1	0x8000
#define KGSL_IOMMU_CTX_OFFSET_V2	0x9000
+16 −53
Original line number Diff line number Diff line
@@ -179,10 +179,9 @@ EXPORT_SYMBOL(kgsl_remove_global_pt_entry);
int kgsl_add_global_pt_entry(struct kgsl_device *device,
		struct kgsl_memdesc *memdesc)
{
	struct kgsl_mmu *mmu = &device->mmu;
	int i;
	int index = 0;
	unsigned int gaddr = mmu->global_pt_base;
	unsigned int gaddr = KGSL_MMU_GLOBAL_MEM_BASE;
	unsigned int size = ALIGN(memdesc->size, PAGE_SIZE);

	if (memdesc->priv & KGSL_MEMDESC_GLOBAL)
@@ -207,7 +206,8 @@ int kgsl_add_global_pt_entry(struct kgsl_device *device,
			break;
	}
	index = i;
	if ((gaddr + size) >= (mmu->global_pt_base + mmu->global_pt_size))
	if ((gaddr + size) >= (KGSL_MMU_GLOBAL_MEM_BASE +
				KGSL_GLOBAL_PT_SIZE))
		return -ENOMEM;

	memdesc->priv |= KGSL_MEMDESC_GLOBAL;
@@ -343,25 +343,6 @@ sysfs_show_mapped(struct kobject *kobj,
	return ret;
}

static ssize_t
sysfs_show_va_range(struct kobject *kobj,
		    struct kobj_attribute *attr,
		    char *buf)
{
	struct kgsl_pagetable *pt;
	int ret = 0;

	pt = _get_pt_from_kobj(kobj);

	if (pt) {
		ret += snprintf(buf, PAGE_SIZE, "0x%x\n",
			kgsl_mmu_get_ptsize(pt->mmu));
	}

	kgsl_put_pagetable(pt);
	return ret;
}

static ssize_t
sysfs_show_max_mapped(struct kobject *kobj,
		      struct kobj_attribute *attr,
@@ -391,12 +372,6 @@ static struct kobj_attribute attr_mapped = {
	.store = NULL,
};

static struct kobj_attribute attr_va_range = {
	.attr = { .name = "va_range", .mode = 0444 },
	.show = sysfs_show_va_range,
	.store = NULL,
};

static struct kobj_attribute attr_max_mapped = {
	.attr = { .name = "max_mapped", .mode = 0444 },
	.show = sysfs_show_max_mapped,
@@ -406,7 +381,6 @@ static struct kobj_attribute attr_max_mapped = {
static struct attribute *pagetable_attrs[] = {
	&attr_entries.attr,
	&attr_mapped.attr,
	&attr_va_range.attr,
	&attr_max_mapped.attr,
	NULL,
};
@@ -578,7 +552,6 @@ kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu,
	int status = 0;
	struct kgsl_pagetable *pagetable = NULL;
	unsigned long flags;
	unsigned int ptsize;

	pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL);
	if (pagetable == NULL)
@@ -588,7 +561,6 @@ kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu,

	spin_lock_init(&pagetable->lock);

	ptsize = kgsl_mmu_get_ptsize(mmu);
	pagetable->mmu = mmu;
	pagetable->name = name;
	pagetable->fault_addr = 0xFFFFFFFF;
@@ -600,8 +572,8 @@ kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu,
		goto err_alloc;
	}

	if (gen_pool_add(pagetable->pool, kgsl_mmu_get_base_addr(mmu),
				ptsize, -1)) {
	if (gen_pool_add(pagetable->pool, mmu->pt_base,
				mmu->pt_size, -1)) {
		KGSL_CORE_ERR("gen_pool_add failed\n");
		goto err_pool;
	}
@@ -737,15 +709,10 @@ kgsl_mmu_get_gpuaddr(struct kgsl_pagetable *pagetable,
	memdesc->gpuaddr = gen_pool_alloc_aligned(pagetable->pool, size,
		page_align);

	if (memdesc->gpuaddr == 0) {
		KGSL_CORE_ERR("gen_pool_alloc(%d) failed\n", size);
		KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
					pagetable->name,
					pagetable->stats.mapped,
					pagetable->stats.entries);
	if (memdesc->gpuaddr == 0)
		return -ENOMEM;
	}

	memdesc->priv |= KGSL_MEMDESC_GENPOOL_ALLOC;
	return 0;
}
EXPORT_SYMBOL(kgsl_mmu_get_gpuaddr);
@@ -817,6 +784,9 @@ kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable,
	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE)
		goto done;

	if (!(KGSL_MEMDESC_GENPOOL_ALLOC & memdesc->priv))
		goto done;

	/* Add space for the guard page when freeing the mmu VA. */
	size = memdesc->size;
	if (kgsl_memdesc_has_guard_page(memdesc))
@@ -824,10 +794,6 @@ kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable,

	pool = pagetable->pool;

	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
		if (kgsl_memdesc_use_cpu_map(memdesc))
			pool = NULL;
	}
	if (pool)
		gen_pool_free(pool, memdesc->gpuaddr, size);
	/*
@@ -835,8 +801,10 @@ kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable,
	 * may be in use by other pagetables
	 */
done:
	if (!kgsl_memdesc_is_global(memdesc))
	if (!kgsl_memdesc_is_global(memdesc)) {
		memdesc->gpuaddr = 0;
		memdesc->priv &= ~KGSL_MEMDESC_GENPOOL_ALLOC;
	}
	return 0;
}
EXPORT_SYMBOL(kgsl_mmu_put_gpuaddr);
@@ -948,13 +916,8 @@ int kgsl_mmu_gpuaddr_in_range(struct kgsl_pagetable *pt, unsigned int gpuaddr)
{
	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
		return (gpuaddr != 0);
	if (gpuaddr >= kgsl_mmu_get_base_addr(pt->mmu) &&
		gpuaddr < kgsl_mmu_get_base_addr(pt->mmu) +
		kgsl_mmu_get_ptsize(pt->mmu))
		return 1;
	if (kgsl_mmu_use_cpu_map(pt->mmu))
		return (gpuaddr > 0 && gpuaddr < KGSL_SVM_UPPER_BOUND);
	return 0;

	return (gpuaddr > 0 && gpuaddr < KGSL_MMU_GLOBAL_MEM_BASE);
}
EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range);
Loading