OpenWrt – Rev 1

Subversion Repositories:
Rev:
From 361974032ae1b0eec36c51a8f1cd9b447864fcbd Mon Sep 17 00:00:00 2001
From: Sugizaki Yukimasa <i.can.speak.c.and.basic@gmail.com>
Date: Fri, 5 Jan 2018 00:44:00 +0900
Subject: [PATCH 150/454] vcsm: Unify cache manipulating functions

Signed-off-by: Sugizaki Yukimasa <i.can.speak.c.and.basic@gmail.com>
---
 drivers/char/broadcom/vc_sm/vmcs_sm.c | 309 +++++++++++---------------
 1 file changed, 132 insertions(+), 177 deletions(-)

--- a/drivers/char/broadcom/vc_sm/vmcs_sm.c
+++ b/drivers/char/broadcom/vc_sm/vmcs_sm.c
@@ -1256,61 +1256,106 @@ static const struct vm_operations_struct
        .fault = vcsm_vma_fault,
 };
 
-/* Walks a VMA and clean each valid page from the cache */
-static void vcsm_vma_cache_clean_page_range(unsigned long addr,
-                                           unsigned long end)
+static int clean_invalid_mem_2d(const void __user *addr,
+               const size_t block_count, const size_t block_size, const size_t stride,
+               const unsigned cache_op)
 {
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-       unsigned long pgd_next, pud_next, pmd_next;
-
-       if (addr >= end)
-               return;
-
-       /* Walk PGD */
-       pgd = pgd_offset(current->mm, addr);
-       do {
-               pgd_next = pgd_addr_end(addr, end);
-
-               if (pgd_none(*pgd) || pgd_bad(*pgd))
-                       continue;
-
-               /* Walk PUD */
-               pud = pud_offset(pgd, addr);
-               do {
-                       pud_next = pud_addr_end(addr, pgd_next);
-                       if (pud_none(*pud) || pud_bad(*pud))
-                               continue;
-
-                       /* Walk PMD */
-                       pmd = pmd_offset(pud, addr);
-                       do {
-                               pmd_next = pmd_addr_end(addr, pud_next);
-                               if (pmd_none(*pmd) || pmd_bad(*pmd))
-                                       continue;
-
-                               /* Walk PTE */
-                               pte = pte_offset_map(pmd, addr);
-                               do {
-                                       if (pte_none(*pte)
-                                           || !pte_present(*pte))
-                                               continue;
-
-                                       /* Clean + invalidate */
-                                       dmac_flush_range((const void *) addr,
-                                                        (const void *)
-                                                        (addr + PAGE_SIZE));
-
-                               } while (pte++, addr +=
-                                        PAGE_SIZE, addr != pmd_next);
-                               pte_unmap(pte);
+       size_t i;
+       void (*op_fn)(const void*, const void*);
 
-                       } while (pmd++, addr = pmd_next, addr != pud_next);
+       if (block_size <= 0) {
+               pr_err("[%s]: size cannot be 0\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (cache_op) {
+       case VCSM_CACHE_OP_INV:
+               op_fn = dmac_inv_range;
+               break;
+       case VCSM_CACHE_OP_CLEAN:
+               op_fn = dmac_clean_range;
+               break;
+       case VCSM_CACHE_OP_FLUSH:
+               op_fn = dmac_flush_range;
+               break;
+       default:
+               pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < block_count; i ++, addr += stride)
+               op_fn(addr, addr + block_size);
+
+       return 0;
+}
+
+static int clean_invalid_mem(const void __user *addr, const size_t size,
+               const unsigned cache_op)
+{
+       return clean_invalid_mem_2d(addr, 1, size, 0, cache_op);
+}
+
+static int clean_invalid_resource(const void __user *addr, const size_t size,
+               const unsigned cache_op, const int usr_hdl,
+               struct sm_resource_t *resource)
+{
+       int err;
+       enum sm_stats_t stat_attempt, stat_failure;
+       void __user *res_addr;
+
+       if (resource == NULL) {
+               pr_err("[%s]: resource is NULL\n", __func__);
+               return -EINVAL;
+       }
+       if (resource->res_cached != VMCS_SM_CACHE_HOST &&
+                               resource->res_cached != VMCS_SM_CACHE_BOTH)
+               return 0;
+
+       switch (cache_op) {
+       case VCSM_CACHE_OP_INV:
+               stat_attempt = INVALID;
+               stat_failure = INVALID_FAIL;
+               break;
+       case VCSM_CACHE_OP_CLEAN:
+               /* Like the original VMCS_SM_CMD_CLEAN_INVALID ioctl handler does. */
+               stat_attempt = FLUSH;
+               stat_failure = FLUSH_FAIL;
+               break;
+       case VCSM_CACHE_OP_FLUSH:
+               stat_attempt = FLUSH;
+               stat_failure = FLUSH_FAIL;
+               break;
+       default:
+               pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op);
+               return -EINVAL;
+       }
+       resource->res_stats[stat_attempt]++;
 
-               } while (pud++, addr = pud_next, addr != pgd_next);
-       } while (pgd++, addr = pgd_next, addr != end);
+       if (size > resource->res_size) {
+               pr_err("[%s]: size (0x%08zu) is larger than res_size (0x%08zu)\n",
+                               __func__, size, resource->res_size);
+               return -EFAULT;
+       }
+       res_addr = (void __user*) vmcs_sm_usr_address_from_pid_and_usr_handle(
+                       current->tgid, usr_hdl);
+       if (res_addr == NULL) {
+               pr_err("[%s]: Failed to get user address "
+                               "from pid (%d) and user handle (%d)\n", __func__, current->tgid,
+                               resource->res_handle);
+               return -EINVAL;
+       }
+       if (!(res_addr <= addr && addr + size <= res_addr + resource->res_size)) {
+               pr_err("[%s]: Addr (0x%p-0x%p) out of range (0x%p-0x%p)\n",
+                               __func__, addr, addr + size, res_addr,
+                               res_addr + resource->res_size);
+               return -EFAULT;
+       }
+
+       err = clean_invalid_mem(addr, size, cache_op);
+       if (err)
+               resource->res_stats[stat_failure]++;
+
+       return err;
 }
 
 /* Map an allocated data into something that the user space. */
@@ -1952,14 +1997,13 @@ static int vc_sm_ioctl_unlock(struct sm_
                        list_for_each_entry(map, &resource->map_list,
                                            resource_map_list) {
                                if (map->vma) {
-                                       unsigned long start;
-                                       unsigned long end;
-
-                                       start = map->vma->vm_start;
-                                       end = map->vma->vm_end;
+                                       const unsigned long start = map->vma->vm_start;
+                                       const unsigned long end = map->vma->vm_end;
 
-                                       vcsm_vma_cache_clean_page_range(
-                                                       start, end);
+                                       ret = clean_invalid_mem((void __user*) start, end - start,
+                                                       VCSM_CACHE_OP_FLUSH);
+                                       if (ret)
+                                               goto error;
                                }
                        }
                        up_read(&current->mm->mmap_sem);
@@ -2833,41 +2877,17 @@ static long vc_sm_ioctl(struct file *fil
                        /* Locate resource from GUID. */
                        resource =
                            vmcs_sm_acquire_resource(file_data, ioparam.handle);
-
-                       if ((resource != NULL) && resource->res_cached) {
-                               dma_addr_t phys_addr = 0;
-
-                               resource->res_stats[FLUSH]++;
-
-                               phys_addr =
-                                   (dma_addr_t)((uint32_t)
-                                                resource->res_base_mem &
-                                                0x3FFFFFFF);
-                               phys_addr += (dma_addr_t)mm_vc_mem_phys_addr;
-
-                               /* L1 cache flush */
-                               down_read(&current->mm->mmap_sem);
-                               vcsm_vma_cache_clean_page_range((unsigned long)
-                                                               ioparam.addr,
-                                                               (unsigned long)
-                                                               ioparam.addr +
-                                                               ioparam.size);
-                               up_read(&current->mm->mmap_sem);
-
-                               /* L2 cache flush */
-                               outer_clean_range(phys_addr,
-                                                 phys_addr +
-                                                 (size_t) ioparam.size);
-                       } else if (resource == NULL) {
+                       if (resource == NULL) {
                                ret = -EINVAL;
                                goto out;
                        }
 
-                       if (resource)
-                               vmcs_sm_release_resource(resource, 0);
-
-                       /* Done. */
-                       goto out;
+                       ret = clean_invalid_resource((void __user*) ioparam.addr,
+                                       ioparam.size, VCSM_CACHE_OP_FLUSH, ioparam.handle,
+                                       resource);
+                       vmcs_sm_release_resource(resource, 0);
+                       if (ret)
+                               goto out;
                }
                break;
 
@@ -2888,41 +2908,16 @@ static long vc_sm_ioctl(struct file *fil
                        /* Locate resource from GUID. */
                        resource =
                            vmcs_sm_acquire_resource(file_data, ioparam.handle);
-
-                       if ((resource != NULL) && resource->res_cached) {
-                               dma_addr_t phys_addr = 0;
-
-                               resource->res_stats[INVALID]++;
-
-                               phys_addr =
-                                   (dma_addr_t)((uint32_t)
-                                                resource->res_base_mem &
-                                                0x3FFFFFFF);
-                               phys_addr += (dma_addr_t)mm_vc_mem_phys_addr;
-
-                               /* L2 cache invalidate */
-                               outer_inv_range(phys_addr,
-                                               phys_addr +
-                                               (size_t) ioparam.size);
-
-                               /* L1 cache invalidate */
-                               down_read(&current->mm->mmap_sem);
-                               vcsm_vma_cache_clean_page_range((unsigned long)
-                                                               ioparam.addr,
-                                                               (unsigned long)
-                                                               ioparam.addr +
-                                                               ioparam.size);
-                               up_read(&current->mm->mmap_sem);
-                       } else if (resource == NULL) {
+                       if (resource == NULL) {
                                ret = -EINVAL;
                                goto out;
                        }
 
-                       if (resource)
-                               vmcs_sm_release_resource(resource, 0);
-
-                       /* Done. */
-                       goto out;
+                       ret = clean_invalid_resource((void __user*) ioparam.addr,
+                                       ioparam.size, VCSM_CACHE_OP_INV, ioparam.handle, resource);
+                       vmcs_sm_release_resource(resource, 0);
+                       if (ret)
+                               goto out;
                }
                break;
 
@@ -2941,43 +2936,27 @@ static long vc_sm_ioctl(struct file *fil
                                goto out;
                        }
                        for (i = 0; i < sizeof(ioparam.s) / sizeof(*ioparam.s); i++) {
-                               switch (ioparam.s[i].cmd) {
-                               case VCSM_CACHE_OP_INV: /* L1/L2 invalidate virtual range */
-                               case VCSM_CACHE_OP_FLUSH: /* L1/L2 clean physical range */
-                               case VCSM_CACHE_OP_CLEAN: /* L1/L2 clean+invalidate all */
-                                       /* Locate resource from GUID. */
-                                       resource =
-                                           vmcs_sm_acquire_resource(file_data, ioparam.s[i].handle);
-
-                                       if ((resource != NULL) && resource->res_cached) {
-                                               unsigned long base = ioparam.s[i].addr & ~(PAGE_SIZE - 1);
-                                               unsigned long end = (ioparam.s[i].addr + ioparam.s[i].size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
-                                               resource->res_stats[ioparam.s[i].cmd == 1 ? INVALID : FLUSH]++;
-
-                                               /* L1/L2 cache flush */
-                                               down_read(&current->mm->mmap_sem);
-                                               vcsm_vma_cache_clean_page_range(base, end);
-                                               up_read(&current->mm->mmap_sem);
-                                       } else if (resource == NULL) {
-                                               ret = -EINVAL;
-                                               goto out;
-                                       }
-
-                                       if (resource)
-                                               vmcs_sm_release_resource(resource, 0);
-
-                                       break;
-                               default:
-                                       break; /* NOOP */
+                               /* Locate resource from GUID. */
+                               resource =
+                                       vmcs_sm_acquire_resource(file_data, ioparam.s[i].handle);
+                               if (resource == NULL) {
+                                       ret = -EINVAL;
+                                       goto out;
                                }
+
+                               ret = clean_invalid_resource((void __user*) ioparam.s[i].addr,
+                                               ioparam.s[i].size, ioparam.s[i].cmd,
+                                               ioparam.s[i].handle, resource);
+                               vmcs_sm_release_resource(resource, 0);
+                               if (ret)
+                                       goto out;
                        }
                }
                break;
        /* Flush/Invalidate the cache for a given mapping. */
        case VMCS_SM_CMD_CLEAN_INVALID2:
                {
-                               int i, j;
+                               int i;
                                struct vmcs_sm_ioctl_clean_invalid2 ioparam;
                                struct vmcs_sm_ioctl_clean_invalid_block *block = NULL;
 
@@ -3006,36 +2985,12 @@ static long vc_sm_ioctl(struct file *fil
 
                                for (i = 0; i < ioparam.op_count; i++) {
                                        const struct vmcs_sm_ioctl_clean_invalid_block * const op = block + i;
-                                       void (*op_fn)(const void *, const void *);
 
-                                       switch(op->invalidate_mode & 3) {
-                                               case VCSM_CACHE_OP_INV:
-                                                       op_fn = dmac_inv_range;
-                                                       break;
-                                               case VCSM_CACHE_OP_CLEAN:
-                                                       op_fn = dmac_clean_range;
-                                                       break;
-                                               case VCSM_CACHE_OP_FLUSH:
-                                                       op_fn = dmac_flush_range;
-                                                       break;
-                                               default:
-                                                       op_fn = 0;
-                                                       break;
-                                       }
-
-                                       if ((op->invalidate_mode & ~3) != 0) {
-                                               ret = -EINVAL;
-                                               break;
-                                       }
-
-                                       if (op_fn == 0)
-                                               continue;
-
-                                       for (j = 0; j < op->block_count; ++j) {
-                                               const char * const base = (const char *)op->start_address + j * op->inter_block_stride;
-                                               const char * const end = base + op->block_size;
-                                               op_fn(base, end);
-                                       }
+                                       ret = clean_invalid_mem_2d((void __user*) op->start_address,
+                                                       op->block_count, op->block_size,
+                                                       op->inter_block_stride, op->invalidate_mode);
+                                       if (ret)
+                                               goto out;
                                }
                                kfree(block);
                        }