mm: ashmem: Add cache clean and invalidate ioctls for ashmem

CRs-fixed: 291124
Signed-off-by: Shubhraprakash Das <sadas@codeaurora.org>
diff --git a/include/linux/ashmem.h b/include/linux/ashmem.h
index c05c180..25a190e 100644
--- a/include/linux/ashmem.h
+++ b/include/linux/ashmem.h
@@ -45,6 +45,8 @@
 #define ASHMEM_GET_PIN_STATUS	_IO(__ASHMEMIOC, 9)
 #define ASHMEM_PURGE_ALL_CACHES	_IO(__ASHMEMIOC, 10)
 #define ASHMEM_CACHE_FLUSH_RANGE	_IO(__ASHMEMIOC, 11)
+#define ASHMEM_CACHE_CLEAN_RANGE	_IO(__ASHMEMIOC, 12)
+#define ASHMEM_CACHE_INV_RANGE		_IO(__ASHMEMIOC, 13)
 
 int get_ashmem_file(int fd, struct file **filp, struct file **vm_file,
 			unsigned long *len);
diff --git a/mm/ashmem.c b/mm/ashmem.c
index c7e72bb..bc977ee 100644
--- a/mm/ashmem.c
+++ b/mm/ashmem.c
@@ -631,79 +631,64 @@
 }
 
 #ifdef CONFIG_OUTER_CACHE
-static unsigned int kgsl_virtaddr_to_physaddr(unsigned int virtaddr)
+static unsigned int virtaddr_to_physaddr(unsigned int virtaddr)
 {
 	unsigned int physaddr = 0;
 	pgd_t *pgd_ptr = NULL;
 	pmd_t *pmd_ptr = NULL;
 	pte_t *pte_ptr = NULL, pte;
 
+	spin_lock(&current->mm->page_table_lock);
 	pgd_ptr = pgd_offset(current->mm, virtaddr);
 	if (pgd_none(*pgd) || pgd_bad(*pgd)) {
-		pr_info
-		    ("Invalid pgd entry found while trying to convert virtual "
-		     "address to physical\n");
-		return 0;
+		pr_err("Failed to convert virtaddr %x to pgd_ptr\n",
+			virtaddr);
+		goto done;
 	}
 
 	pmd_ptr = pmd_offset(pgd_ptr, virtaddr);
 	if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) {
-		pr_info
-		    ("Invalid pmd entry found while trying to convert virtual "
-		     "address to physical\n");
-		return 0;
+		pr_err("Failed to convert pgd_ptr %p to pmd_ptr\n",
+			(void *)pgd_ptr);
+		goto done;
 	}
 
 	pte_ptr = pte_offset_map(pmd_ptr, virtaddr);
 	if (!pte_ptr) {
-		pr_info
-		    ("Unable to map pte entry while trying to convert virtual "
-		     "address to physical\n");
-		return 0;
+		pr_err("Failed to convert pmd_ptr %p to pte_ptr\n",
+			(void *)pmd_ptr);
+		goto done;
 	}
 	pte = *pte_ptr;
 	physaddr = pte_pfn(pte);
 	pte_unmap(pte_ptr);
+done:
+	spin_unlock(&current->mm->page_table_lock);
 	physaddr <<= PAGE_SHIFT;
 	return physaddr;
 }
 #endif
 
-static int ashmem_flush_cache_range(struct ashmem_area *asma)
+static int ashmem_cache_op(struct ashmem_area *asma,
+	void (*cache_func)(unsigned long vstart, unsigned long length,
+				unsigned long pstart))
 {
 #ifdef CONFIG_OUTER_CACHE
-	unsigned long end;
+	unsigned long vaddr;
 #endif
-	unsigned long addr;
-	unsigned int size, result = 0;
-
 	mutex_lock(&ashmem_mutex);
-
-	size = asma->size;
-	addr = asma->vm_start;
-	if (!addr || (addr & (PAGE_SIZE - 1)) || !size ||
-		(size & (PAGE_SIZE - 1))) {
-		result =  -EINVAL;
-		goto done;
-	}
-
-#ifdef CONFIG_OUTER_CACHE
-	flush_cache_user_range(addr, addr + size);
-	for (end = addr; end < (addr + size); end += PAGE_SIZE) {
-		unsigned long physaddr;
-		physaddr = kgsl_virtaddr_to_physaddr(end);
-		if (!physaddr) {
-			result =  -EINVAL;
-			goto done;
-		}
-
-		outer_flush_range(physaddr, physaddr + PAGE_SIZE);
-	}
-	mb();
+#ifndef CONFIG_OUTER_CACHE
+	cache_func(asma->vm_start, asma->size, 0);
 #else
-	clean_and_invalidate_caches(addr, size, 0);
+	for (vaddr = asma->vm_start; vaddr < asma->vm_start + asma->size;
+		vaddr += PAGE_SIZE) {
+		unsigned long physaddr;
+		physaddr = virtaddr_to_physaddr(vaddr);
+		if (!physaddr)
+			return -EINVAL;
+		cache_func(vaddr, PAGE_SIZE, physaddr);
+	}
 #endif
-done:
 	mutex_unlock(&ashmem_mutex);
 	return 0;
 }
@@ -754,7 +739,13 @@
 		}
 		break;
 	case ASHMEM_CACHE_FLUSH_RANGE:
-		ret = ashmem_flush_cache_range(asma);
+		ret = ashmem_cache_op(asma, &clean_and_invalidate_caches);
+		break;
+	case ASHMEM_CACHE_CLEAN_RANGE:
+		ret = ashmem_cache_op(asma, &clean_caches);
+		break;
+	case ASHMEM_CACHE_INV_RANGE:
+		ret = ashmem_cache_op(asma, &invalidate_caches);
 		break;
 	}