blob: 8829102992451ff441acaa9bf024e1e0ef4b5c06 [file] [log] [blame]
Jeff Boody28afec42012-01-18 15:47:46 -07001/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#include <linux/vmalloc.h>
14#include <linux/memory_alloc.h>
15#include <asm/cacheflush.h>
Anshuman Danieecd5202012-02-17 19:52:49 +053016#include <linux/slab.h>
17#include <linux/kmemleak.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018
19#include "kgsl.h"
20#include "kgsl_sharedmem.h"
21#include "kgsl_cffdump.h"
22#include "kgsl_device.h"
23
Jordan Crouse1b897cf2011-10-12 16:57:48 -060024/* An attribute for showing per-process memory statistics */
25struct kgsl_mem_entry_attribute {
26 struct attribute attr;
27 int memtype;
28 ssize_t (*show)(struct kgsl_process_private *priv,
29 int type, char *buf);
30};
31
32#define to_mem_entry_attr(a) \
33container_of(a, struct kgsl_mem_entry_attribute, attr)
34
35#define __MEM_ENTRY_ATTR(_type, _name, _show) \
36{ \
37 .attr = { .name = __stringify(_name), .mode = 0444 }, \
38 .memtype = _type, \
39 .show = _show, \
40}
41
42/*
43 * A structure to hold the attributes for a particular memory type.
44 * For each memory type in each process we store the current and maximum
45 * memory usage and display the counts in sysfs. This structure and
46 * the following macro allow us to simplify the definition for those
47 * adding new memory types
48 */
49
50struct mem_entry_stats {
51 int memtype;
52 struct kgsl_mem_entry_attribute attr;
53 struct kgsl_mem_entry_attribute max_attr;
54};
55
56
57#define MEM_ENTRY_STAT(_type, _name) \
58{ \
59 .memtype = _type, \
60 .attr = __MEM_ENTRY_ATTR(_type, _name, mem_entry_show), \
61 .max_attr = __MEM_ENTRY_ATTR(_type, _name##_max, \
62 mem_entry_max_show), \
63}
64
65
Jordan Crouse7d3139b2012-05-18 10:05:02 -060066/*
67 * One page allocation for a guard region to protect against over-zealous
68 * GPU pre-fetch
69 */
70
71static struct page *kgsl_guard_page;
72
Jordan Crouse1b897cf2011-10-12 16:57:48 -060073/**
74 * Given a kobj, find the process structure attached to it
75 */
76
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077static struct kgsl_process_private *
78_get_priv_from_kobj(struct kobject *kobj)
79{
80 struct kgsl_process_private *private;
81 unsigned long name;
82
83 if (!kobj)
84 return NULL;
85
86 if (sscanf(kobj->name, "%ld", &name) != 1)
87 return NULL;
88
89 list_for_each_entry(private, &kgsl_driver.process_list, list) {
90 if (private->pid == name)
91 return private;
92 }
93
94 return NULL;
95}
96
Jordan Crouse1b897cf2011-10-12 16:57:48 -060097/**
98 * Show the current amount of memory allocated for the given memtype
99 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100
101static ssize_t
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600102mem_entry_show(struct kgsl_process_private *priv, int type, char *buf)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103{
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600104 return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].cur);
105}
106
107/**
108 * Show the maximum memory allocated for the given memtype through the life of
109 * the process
110 */
111
112static ssize_t
113mem_entry_max_show(struct kgsl_process_private *priv, int type, char *buf)
114{
115 return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].max);
116}
117
118
119static void mem_entry_sysfs_release(struct kobject *kobj)
120{
121}
122
123static ssize_t mem_entry_sysfs_show(struct kobject *kobj,
124 struct attribute *attr, char *buf)
125{
126 struct kgsl_mem_entry_attribute *pattr = to_mem_entry_attr(attr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 struct kgsl_process_private *priv;
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600128 ssize_t ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129
130 mutex_lock(&kgsl_driver.process_mutex);
131 priv = _get_priv_from_kobj(kobj);
132
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600133 if (priv && pattr->show)
134 ret = pattr->show(priv, pattr->memtype, buf);
135 else
136 ret = -EIO;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137
138 mutex_unlock(&kgsl_driver.process_mutex);
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600139 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140}
141
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600142static const struct sysfs_ops mem_entry_sysfs_ops = {
143 .show = mem_entry_sysfs_show,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144};
145
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600146static struct kobj_type ktype_mem_entry = {
147 .sysfs_ops = &mem_entry_sysfs_ops,
148 .default_attrs = NULL,
149 .release = mem_entry_sysfs_release
150};
151
152static struct mem_entry_stats mem_stats[] = {
153 MEM_ENTRY_STAT(KGSL_MEM_ENTRY_KERNEL, kernel),
154#ifdef CONFIG_ANDROID_PMEM
155 MEM_ENTRY_STAT(KGSL_MEM_ENTRY_PMEM, pmem),
156#endif
157#ifdef CONFIG_ASHMEM
158 MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ASHMEM, ashmem),
159#endif
160 MEM_ENTRY_STAT(KGSL_MEM_ENTRY_USER, user),
Jordan Crouse8eab35a2011-10-12 16:57:48 -0600161#ifdef CONFIG_ION
Jeremy Gebbenff6eab02012-01-09 09:42:21 -0700162 MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ION, ion),
Jordan Crouse8eab35a2011-10-12 16:57:48 -0600163#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164};
165
166void
167kgsl_process_uninit_sysfs(struct kgsl_process_private *private)
168{
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600169 int i;
170
171 for (i = 0; i < ARRAY_SIZE(mem_stats); i++) {
172 sysfs_remove_file(&private->kobj, &mem_stats[i].attr.attr);
173 sysfs_remove_file(&private->kobj,
174 &mem_stats[i].max_attr.attr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 }
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600176
177 kobject_put(&private->kobj);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178}
179
180void
181kgsl_process_init_sysfs(struct kgsl_process_private *private)
182{
183 unsigned char name[16];
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600184 int i, ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 snprintf(name, sizeof(name), "%d", private->pid);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187
Jordan Crouse1b897cf2011-10-12 16:57:48 -0600188 if (kobject_init_and_add(&private->kobj, &ktype_mem_entry,
189 kgsl_driver.prockobj, name))
190 return;
191
192 for (i = 0; i < ARRAY_SIZE(mem_stats); i++) {
193 /* We need to check the value of sysfs_create_file, but we
194 * don't really care if it passed or not */
195
196 ret = sysfs_create_file(&private->kobj,
197 &mem_stats[i].attr.attr);
198 ret = sysfs_create_file(&private->kobj,
199 &mem_stats[i].max_attr.attr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 }
201}
202
203static int kgsl_drv_memstat_show(struct device *dev,
204 struct device_attribute *attr,
205 char *buf)
206{
207 unsigned int val = 0;
208
209 if (!strncmp(attr->attr.name, "vmalloc", 7))
210 val = kgsl_driver.stats.vmalloc;
211 else if (!strncmp(attr->attr.name, "vmalloc_max", 11))
212 val = kgsl_driver.stats.vmalloc_max;
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600213 else if (!strncmp(attr->attr.name, "page_alloc", 10))
214 val = kgsl_driver.stats.page_alloc;
215 else if (!strncmp(attr->attr.name, "page_alloc_max", 14))
216 val = kgsl_driver.stats.page_alloc_max;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 else if (!strncmp(attr->attr.name, "coherent", 8))
218 val = kgsl_driver.stats.coherent;
219 else if (!strncmp(attr->attr.name, "coherent_max", 12))
220 val = kgsl_driver.stats.coherent_max;
221 else if (!strncmp(attr->attr.name, "mapped", 6))
222 val = kgsl_driver.stats.mapped;
223 else if (!strncmp(attr->attr.name, "mapped_max", 10))
224 val = kgsl_driver.stats.mapped_max;
225
226 return snprintf(buf, PAGE_SIZE, "%u\n", val);
227}
228
229static int kgsl_drv_histogram_show(struct device *dev,
230 struct device_attribute *attr,
231 char *buf)
232{
233 int len = 0;
234 int i;
235
236 for (i = 0; i < 16; i++)
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600237 len += snprintf(buf + len, PAGE_SIZE - len, "%d ",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238 kgsl_driver.stats.histogram[i]);
239
Jeremy Gebbena87bb862011-08-08 16:09:38 -0600240 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241 return len;
242}
243
244DEVICE_ATTR(vmalloc, 0444, kgsl_drv_memstat_show, NULL);
245DEVICE_ATTR(vmalloc_max, 0444, kgsl_drv_memstat_show, NULL);
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600246DEVICE_ATTR(page_alloc, 0444, kgsl_drv_memstat_show, NULL);
247DEVICE_ATTR(page_alloc_max, 0444, kgsl_drv_memstat_show, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248DEVICE_ATTR(coherent, 0444, kgsl_drv_memstat_show, NULL);
249DEVICE_ATTR(coherent_max, 0444, kgsl_drv_memstat_show, NULL);
250DEVICE_ATTR(mapped, 0444, kgsl_drv_memstat_show, NULL);
251DEVICE_ATTR(mapped_max, 0444, kgsl_drv_memstat_show, NULL);
252DEVICE_ATTR(histogram, 0444, kgsl_drv_histogram_show, NULL);
253
254static const struct device_attribute *drv_attr_list[] = {
255 &dev_attr_vmalloc,
256 &dev_attr_vmalloc_max,
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600257 &dev_attr_page_alloc,
258 &dev_attr_page_alloc_max,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259 &dev_attr_coherent,
260 &dev_attr_coherent_max,
261 &dev_attr_mapped,
262 &dev_attr_mapped_max,
263 &dev_attr_histogram,
264 NULL
265};
266
267void
268kgsl_sharedmem_uninit_sysfs(void)
269{
270 kgsl_remove_device_sysfs_files(&kgsl_driver.virtdev, drv_attr_list);
271}
272
273int
274kgsl_sharedmem_init_sysfs(void)
275{
276 return kgsl_create_device_sysfs_files(&kgsl_driver.virtdev,
277 drv_attr_list);
278}
279
280#ifdef CONFIG_OUTER_CACHE
281static void _outer_cache_range_op(int op, unsigned long addr, size_t size)
282{
283 switch (op) {
284 case KGSL_CACHE_OP_FLUSH:
285 outer_flush_range(addr, addr + size);
286 break;
287 case KGSL_CACHE_OP_CLEAN:
288 outer_clean_range(addr, addr + size);
289 break;
290 case KGSL_CACHE_OP_INV:
291 outer_inv_range(addr, addr + size);
292 break;
293 }
294}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295
Jordan Croused17e9aa2011-10-12 16:57:48 -0600296static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297{
Jordan Croused17e9aa2011-10-12 16:57:48 -0600298 struct scatterlist *s;
299 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300
Jordan Croused17e9aa2011-10-12 16:57:48 -0600301 for_each_sg(sg, s, sglen, i) {
Jeremy Gebben582fe312012-03-23 10:19:44 -0600302 unsigned int paddr = kgsl_get_sg_pa(s);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600303 _outer_cache_range_op(op, paddr, s->length);
304 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305}
306
Jordan Croused17e9aa2011-10-12 16:57:48 -0600307#else
308static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310}
311#endif
312
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600313static int kgsl_page_alloc_vmfault(struct kgsl_memdesc *memdesc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 struct vm_area_struct *vma,
315 struct vm_fault *vmf)
316{
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600317 unsigned long offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 struct page *page;
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600319 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320
321 offset = (unsigned long) vmf->virtual_address - vma->vm_start;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600323 i = offset >> PAGE_SHIFT;
324 page = sg_page(&memdesc->sg[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 if (page == NULL)
326 return VM_FAULT_SIGBUS;
327
328 get_page(page);
329
330 vmf->page = page;
331 return 0;
332}
333
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600334static int kgsl_page_alloc_vmflags(struct kgsl_memdesc *memdesc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335{
336 return VM_RESERVED | VM_DONTEXPAND;
337}
338
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600339static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340{
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600341 int i = 0;
342 struct scatterlist *sg;
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600343 int sglen = memdesc->sglen;
344
345 /* Don't free the guard page if it was used */
346 if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
347 sglen--;
348
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600349 kgsl_driver.stats.page_alloc -= memdesc->size;
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600350
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600351 if (memdesc->hostptr) {
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600352 vunmap(memdesc->hostptr);
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600353 kgsl_driver.stats.vmalloc -= memdesc->size;
354 }
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600355 if (memdesc->sg)
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600356 for_each_sg(memdesc->sg, sg, sglen, i)
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600357 __free_page(sg_page(sg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358}
359
360static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc)
361{
362 return VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND;
363}
364
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600365/*
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600366 * kgsl_page_alloc_map_kernel - Map the memory in memdesc to kernel address
367 * space
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600368 *
369 * @memdesc - The memory descriptor which contains information about the memory
370 *
371 * Return: 0 on success else error code
372 */
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600373static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc)
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600374{
375 if (!memdesc->hostptr) {
376 pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
377 struct page **pages = NULL;
378 struct scatterlist *sg;
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600379 int sglen = memdesc->sglen;
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600380 int i;
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600381
382 /* Don't map the guard page if it exists */
383 if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
384 sglen--;
385
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600386 /* create a list of pages to call vmap */
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600387 pages = vmalloc(sglen * sizeof(struct page *));
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600388 if (!pages) {
389 KGSL_CORE_ERR("vmalloc(%d) failed\n",
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600390 sglen * sizeof(struct page *));
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600391 return -ENOMEM;
392 }
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600393 for_each_sg(memdesc->sg, sg, sglen, i)
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600394 pages[i] = sg_page(sg);
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600395 memdesc->hostptr = vmap(pages, sglen,
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600396 VM_IOREMAP, page_prot);
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600397 KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc,
398 kgsl_driver.stats.vmalloc_max);
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600399 vfree(pages);
400 }
401 if (!memdesc->hostptr)
402 return -ENOMEM;
403
404 return 0;
405}
406
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407static int kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc,
408 struct vm_area_struct *vma,
409 struct vm_fault *vmf)
410{
411 unsigned long offset, pfn;
412 int ret;
413
414 offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >>
415 PAGE_SHIFT;
416
417 pfn = (memdesc->physaddr >> PAGE_SHIFT) + offset;
418 ret = vm_insert_pfn(vma, (unsigned long) vmf->virtual_address, pfn);
419
420 if (ret == -ENOMEM || ret == -EAGAIN)
421 return VM_FAULT_OOM;
422 else if (ret == -EFAULT)
423 return VM_FAULT_SIGBUS;
424
425 return VM_FAULT_NOPAGE;
426}
427
428static void kgsl_ebimem_free(struct kgsl_memdesc *memdesc)
429
430{
431 kgsl_driver.stats.coherent -= memdesc->size;
432 if (memdesc->hostptr)
433 iounmap(memdesc->hostptr);
434
435 free_contiguous_memory_by_paddr(memdesc->physaddr);
436}
437
438static void kgsl_coherent_free(struct kgsl_memdesc *memdesc)
439{
440 kgsl_driver.stats.coherent -= memdesc->size;
441 dma_free_coherent(NULL, memdesc->size,
442 memdesc->hostptr, memdesc->physaddr);
443}
444
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445/* Global - also used by kgsl_drm.c */
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600446struct kgsl_memdesc_ops kgsl_page_alloc_ops = {
447 .free = kgsl_page_alloc_free,
448 .vmflags = kgsl_page_alloc_vmflags,
449 .vmfault = kgsl_page_alloc_vmfault,
450 .map_kernel_mem = kgsl_page_alloc_map_kernel,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451};
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600452EXPORT_SYMBOL(kgsl_page_alloc_ops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453
454static struct kgsl_memdesc_ops kgsl_ebimem_ops = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 .free = kgsl_ebimem_free,
456 .vmflags = kgsl_contiguous_vmflags,
457 .vmfault = kgsl_contiguous_vmfault,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458};
459
460static struct kgsl_memdesc_ops kgsl_coherent_ops = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461 .free = kgsl_coherent_free,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462};
463
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op)
465{
466 void *addr = memdesc->hostptr;
467 int size = memdesc->size;
468
469 switch (op) {
470 case KGSL_CACHE_OP_FLUSH:
471 dmac_flush_range(addr, addr + size);
472 break;
473 case KGSL_CACHE_OP_CLEAN:
474 dmac_clean_range(addr, addr + size);
475 break;
476 case KGSL_CACHE_OP_INV:
477 dmac_inv_range(addr, addr + size);
478 break;
479 }
480
Jordan Croused17e9aa2011-10-12 16:57:48 -0600481 outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482}
483EXPORT_SYMBOL(kgsl_cache_range_op);
484
485static int
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600486_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 struct kgsl_pagetable *pagetable,
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600488 size_t size, unsigned int protflags)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489{
Jordan Croused17e9aa2011-10-12 16:57:48 -0600490 int order, ret = 0;
491 int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
492 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600494 /*
495 * Add guard page to the end of the allocation when the
496 * IOMMU is in use.
497 */
498
499 if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
500 sglen++;
501
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 memdesc->size = size;
503 memdesc->pagetable = pagetable;
504 memdesc->priv = KGSL_MEMFLAGS_CACHED;
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600505 memdesc->ops = &kgsl_page_alloc_ops;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506
Jordan Crousea652a072012-04-06 16:26:33 -0600507 memdesc->sg = kgsl_sg_alloc(sglen);
508
Jordan Croused17e9aa2011-10-12 16:57:48 -0600509 if (memdesc->sg == NULL) {
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600510 KGSL_CORE_ERR("vmalloc(%d) failed\n",
511 sglen * sizeof(struct scatterlist));
Jordan Croused17e9aa2011-10-12 16:57:48 -0600512 ret = -ENOMEM;
513 goto done;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514 }
515
Anshuman Danieecd5202012-02-17 19:52:49 +0530516 kmemleak_not_leak(memdesc->sg);
517
Jordan Croused17e9aa2011-10-12 16:57:48 -0600518 memdesc->sglen = sglen;
519 sg_init_table(memdesc->sg, sglen);
520
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600521 for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) {
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600522 struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO |
523 __GFP_HIGHMEM);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600524 if (!page) {
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600525 ret = -ENOMEM;
526 memdesc->sglen = i;
Jordan Croused17e9aa2011-10-12 16:57:48 -0600527 goto done;
528 }
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600529 flush_dcache_page(page);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600530 sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0);
531 }
Jordan Crouse7d3139b2012-05-18 10:05:02 -0600532
533 /* ADd the guard page to the end of the sglist */
534
535 if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
536 if (kgsl_guard_page == NULL)
537 kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
538 __GFP_HIGHMEM);
539
540 if (kgsl_guard_page != NULL) {
541 sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page,
542 PAGE_SIZE, 0);
543 memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
544 } else
545 memdesc->sglen--;
546 }
547
Jeremy Gebben7018a212012-04-11 10:23:52 -0600548 outer_cache_range_op_sg(memdesc->sg, memdesc->sglen,
549 KGSL_CACHE_OP_FLUSH);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600550
Jordan Croused17e9aa2011-10-12 16:57:48 -0600551 ret = kgsl_mmu_map(pagetable, memdesc, protflags);
552
553 if (ret)
554 goto done;
555
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600556 KGSL_STATS_ADD(size, kgsl_driver.stats.page_alloc,
557 kgsl_driver.stats.page_alloc_max);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600558
559 order = get_order(size);
560
561 if (order < 16)
562 kgsl_driver.stats.histogram[order]++;
563
564done:
565 if (ret)
566 kgsl_sharedmem_free(memdesc);
567
568 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569}
570
571int
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600572kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 struct kgsl_pagetable *pagetable, size_t size)
574{
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600575 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576 BUG_ON(size == 0);
577
578 size = ALIGN(size, PAGE_SIZE * 2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600580 ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600582 if (!ret)
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600583 ret = kgsl_page_alloc_map_kernel(memdesc);
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600584 if (ret)
585 kgsl_sharedmem_free(memdesc);
586 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587}
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600588EXPORT_SYMBOL(kgsl_sharedmem_page_alloc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589
590int
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600591kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 struct kgsl_pagetable *pagetable,
593 size_t size, int flags)
594{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595 unsigned int protflags;
596
597 BUG_ON(size == 0);
Anshuman Danieecd5202012-02-17 19:52:49 +0530598
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599 protflags = GSL_PT_PAGE_RV;
600 if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
601 protflags |= GSL_PT_PAGE_WV;
602
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600603 return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604 protflags);
605}
Harsh Vardhan Dwivedif99c2632012-03-15 14:17:11 -0600606EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607
608int
609kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size)
610{
Jordan Croused17e9aa2011-10-12 16:57:48 -0600611 int result = 0;
612
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700613 size = ALIGN(size, PAGE_SIZE);
614
Jordan Croused17e9aa2011-10-12 16:57:48 -0600615 memdesc->size = size;
616 memdesc->ops = &kgsl_coherent_ops;
617
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618 memdesc->hostptr = dma_alloc_coherent(NULL, size, &memdesc->physaddr,
619 GFP_KERNEL);
620 if (memdesc->hostptr == NULL) {
621 KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600622 result = -ENOMEM;
623 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 }
625
Jordan Croused17e9aa2011-10-12 16:57:48 -0600626 result = memdesc_sg_phys(memdesc, memdesc->physaddr, size);
627 if (result)
628 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700629
630 /* Record statistics */
631
632 KGSL_STATS_ADD(size, kgsl_driver.stats.coherent,
633 kgsl_driver.stats.coherent_max);
634
Jordan Croused17e9aa2011-10-12 16:57:48 -0600635err:
636 if (result)
637 kgsl_sharedmem_free(memdesc);
638
639 return result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640}
641EXPORT_SYMBOL(kgsl_sharedmem_alloc_coherent);
642
643void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
644{
645 if (memdesc == NULL || memdesc->size == 0)
646 return;
647
648 if (memdesc->gpuaddr)
649 kgsl_mmu_unmap(memdesc->pagetable, memdesc);
650
Jordan Croused17e9aa2011-10-12 16:57:48 -0600651 if (memdesc->ops && memdesc->ops->free)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652 memdesc->ops->free(memdesc);
653
Jordan Crousea652a072012-04-06 16:26:33 -0600654 kgsl_sg_free(memdesc->sg, memdesc->sglen);
Jordan Croused17e9aa2011-10-12 16:57:48 -0600655
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656 memset(memdesc, 0, sizeof(*memdesc));
657}
658EXPORT_SYMBOL(kgsl_sharedmem_free);
659
660static int
661_kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
662 struct kgsl_pagetable *pagetable, size_t size)
663{
Jordan Croused17e9aa2011-10-12 16:57:48 -0600664 int result = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700665
Jordan Croused17e9aa2011-10-12 16:57:48 -0600666 memdesc->size = size;
667 memdesc->pagetable = pagetable;
668 memdesc->ops = &kgsl_ebimem_ops;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 memdesc->physaddr = allocate_contiguous_ebi_nomap(size, SZ_8K);
670
671 if (memdesc->physaddr == 0) {
672 KGSL_CORE_ERR("allocate_contiguous_ebi_nomap(%d) failed\n",
673 size);
674 return -ENOMEM;
675 }
676
Jordan Croused17e9aa2011-10-12 16:57:48 -0600677 result = memdesc_sg_phys(memdesc, memdesc->physaddr, size);
678
679 if (result)
680 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700681
682 result = kgsl_mmu_map(pagetable, memdesc,
683 GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
684
685 if (result)
Jordan Croused17e9aa2011-10-12 16:57:48 -0600686 goto err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687
688 KGSL_STATS_ADD(size, kgsl_driver.stats.coherent,
689 kgsl_driver.stats.coherent_max);
690
Jordan Croused17e9aa2011-10-12 16:57:48 -0600691err:
692 if (result)
693 kgsl_sharedmem_free(memdesc);
694
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 return result;
696}
697
698int
699kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
700 struct kgsl_pagetable *pagetable,
701 size_t size, int flags)
702{
703 size = ALIGN(size, PAGE_SIZE);
704 return _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
705}
706EXPORT_SYMBOL(kgsl_sharedmem_ebimem_user);
707
708int
709kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
710 struct kgsl_pagetable *pagetable, size_t size)
711{
712 int result;
713 size = ALIGN(size, 8192);
714 result = _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
715
716 if (result)
717 return result;
718
719 memdesc->hostptr = ioremap(memdesc->physaddr, size);
720
721 if (memdesc->hostptr == NULL) {
722 KGSL_CORE_ERR("ioremap failed\n");
723 kgsl_sharedmem_free(memdesc);
724 return -ENOMEM;
725 }
726
727 return 0;
728}
729EXPORT_SYMBOL(kgsl_sharedmem_ebimem);
730
731int
732kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
733 uint32_t *dst,
734 unsigned int offsetbytes)
735{
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700736 uint32_t *src;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700737 BUG_ON(memdesc == NULL || memdesc->hostptr == NULL || dst == NULL);
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700738 WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
739 if (offsetbytes % sizeof(uint32_t) != 0)
740 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700742 WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
743 if (offsetbytes + sizeof(uint32_t) > memdesc->size)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744 return -ERANGE;
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700745 src = (uint32_t *)(memdesc->hostptr + offsetbytes);
746 *dst = *src;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747 return 0;
748}
749EXPORT_SYMBOL(kgsl_sharedmem_readl);
750
751int
752kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc,
753 unsigned int offsetbytes,
754 uint32_t src)
755{
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700756 uint32_t *dst;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700757 BUG_ON(memdesc == NULL || memdesc->hostptr == NULL);
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700758 WARN_ON(offsetbytes % sizeof(uint32_t) != 0);
759 if (offsetbytes % sizeof(uint32_t) != 0)
760 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700762 WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
763 if (offsetbytes + sizeof(uint32_t) > memdesc->size)
764 return -ERANGE;
Jeremy Gebbena3d07a42011-10-17 12:08:16 -0600765 kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes,
Jeremy Gebbenaba13272012-01-31 17:31:23 -0700766 src, sizeof(uint32_t));
767 dst = (uint32_t *)(memdesc->hostptr + offsetbytes);
768 *dst = src;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 return 0;
770}
771EXPORT_SYMBOL(kgsl_sharedmem_writel);
772
773int
774kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes,
775 unsigned int value, unsigned int sizebytes)
776{
777 BUG_ON(memdesc == NULL || memdesc->hostptr == NULL);
778 BUG_ON(offsetbytes + sizebytes > memdesc->size);
779
Jeremy Gebbena3d07a42011-10-17 12:08:16 -0600780 kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes, value,
781 sizebytes);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700782 memset(memdesc->hostptr + offsetbytes, value, sizebytes);
783 return 0;
784}
785EXPORT_SYMBOL(kgsl_sharedmem_set);
Harsh Vardhan Dwivedi8cb835b2012-03-29 17:23:11 -0600786
787/*
788 * kgsl_sharedmem_map_vma - Map a user vma to physical memory
789 *
790 * @vma - The user vma to map
791 * @memdesc - The memory descriptor which contains information about the
792 * physical memory
793 *
794 * Return: 0 on success else error code
795 */
796int
797kgsl_sharedmem_map_vma(struct vm_area_struct *vma,
798 const struct kgsl_memdesc *memdesc)
799{
800 unsigned long addr = vma->vm_start;
801 unsigned long size = vma->vm_end - vma->vm_start;
802 int ret, i = 0;
803
804 if (!memdesc->sg || (size != memdesc->size) ||
805 (memdesc->sglen != (size / PAGE_SIZE)))
806 return -EINVAL;
807
808 for (; addr < vma->vm_end; addr += PAGE_SIZE, i++) {
809 ret = vm_insert_page(vma, addr, sg_page(&memdesc->sg[i]));
810 if (ret)
811 return ret;
812 }
813 return 0;
814}
815EXPORT_SYMBOL(kgsl_sharedmem_map_vma);