Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/mm/ashmem.c b/mm/ashmem.c
index 66e3f23..c7e72bb 100644
--- a/mm/ashmem.c
+++ b/mm/ashmem.c
@@ -29,6 +29,7 @@
#include <linux/mutex.h>
#include <linux/shmem_fs.h>
#include <linux/ashmem.h>
+#include <asm/cacheflush.h>
#define ASHMEM_NAME_PREFIX "dev/ashmem/"
#define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1)
@@ -45,6 +46,8 @@
struct list_head unpinned_list; /* list of all ashmem areas */
struct file *file; /* the shmem-based backing file */
size_t size; /* size of the mapping, in bytes */
+ unsigned long vm_start; /* Start address of vm_area
+ * which maps this ashmem */
unsigned long prot_mask; /* allowed prot bits, as vm_flags */
};
@@ -326,6 +329,7 @@
vma->vm_file = asma->file;
}
vma->vm_flags |= VM_CAN_NONLINEAR;
+ asma->vm_start = vma->vm_start;
out:
mutex_unlock(&ashmem_mutex);
@@ -626,6 +630,84 @@
return ret;
}
+#ifdef CONFIG_OUTER_CACHE
+static unsigned int kgsl_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;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ pte = *pte_ptr;
+ physaddr = pte_pfn(pte);
+ pte_unmap(pte_ptr);
+ physaddr <<= PAGE_SHIFT;
+ return physaddr;
+}
+#endif
+
+static int ashmem_flush_cache_range(struct ashmem_area *asma)
+{
+#ifdef CONFIG_OUTER_CACHE
+ unsigned long end;
+#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();
+#else
+ clean_and_invalidate_caches(addr, size, 0);
+#endif
+done:
+ mutex_unlock(&ashmem_mutex);
+ return 0;
+}
+
static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct ashmem_area *asma = file->private_data;
@@ -671,11 +753,67 @@
ashmem_shrink(&ashmem_shrinker, &sc);
}
break;
+ case ASHMEM_CACHE_FLUSH_RANGE:
+ ret = ashmem_flush_cache_range(asma);
+ break;
}
return ret;
}
+static int is_ashmem_file(struct file *file)
+{
+ char fname[256], *name;
+ name = dentry_path(file->f_dentry, fname, 256);
+ return strcmp(name, "/ashmem") ? 0 : 1;
+}
+
+int get_ashmem_file(int fd, struct file **filp, struct file **vm_file,
+ unsigned long *len)
+{
+ int ret = -1;
+ struct file *file = fget(fd);
+ *filp = NULL;
+ *vm_file = NULL;
+ if (unlikely(file == NULL)) {
+ pr_err("ashmem: %s: requested data from file "
+ "descriptor that doesn't exist.\n", __func__);
+ } else {
+ char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+ pr_debug("filp %p rdev %d pid %u(%s) file %p(%ld)"
+ " dev id: %d\n", filp,
+ file->f_dentry->d_inode->i_rdev,
+ current->pid, get_task_comm(currtask_name, current),
+ file, file_count(file),
+ MINOR(file->f_dentry->d_inode->i_rdev));
+ if (is_ashmem_file(file)) {
+ struct ashmem_area *asma = file->private_data;
+ *filp = file;
+ *vm_file = asma->file;
+ *len = asma->size;
+ ret = 0;
+ } else {
+ pr_err("file descriptor is not an ashmem "
+ "region fd: %d\n", fd);
+ fput(file);
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL(get_ashmem_file);
+
+void put_ashmem_file(struct file *file)
+{
+ char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
+ pr_debug("rdev %d pid %u(%s) file %p(%ld)" " dev id: %d\n",
+ file->f_dentry->d_inode->i_rdev, current->pid,
+ get_task_comm(currtask_name, current), file,
+ file_count(file), MINOR(file->f_dentry->d_inode->i_rdev));
+ if (file && is_ashmem_file(file))
+ fput(file);
+}
+EXPORT_SYMBOL(put_ashmem_file);
+
static struct file_operations ashmem_fops = {
.owner = THIS_MODULE,
.open = ashmem_open,