From b17f6dc1d79ae057294ac2d8d824aa2258ab09a8 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 20 Mar 2019 10:40:00 +0000 Subject: [PATCH] staging: vcsm-cma: Add cache control ioctls The old driver allowed for direct cache manipulation and that was used by various clients. Replicate here. Signed-off-by: Dave Stevenson --- .../staging/vc04_services/vc-sm-cma/vc_sm.c | 141 +++++++++++++++++- include/linux/broadcom/vc_sm_cma_ioctl.h | 27 ++++ 2 files changed, 165 insertions(+), 3 deletions(-) --- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "vchiq_connected.h" #include "vc_sm_cma_vchi.h" @@ -1258,6 +1259,99 @@ error: return ret; } +/* Converts VCSM_CACHE_OP_* to an operating function. */ +static void (*cache_op_to_func(const unsigned int cache_op)) + (const void*, const void*) +{ + switch (cache_op) { + case VC_SM_CACHE_OP_NOP: + return NULL; + + case VC_SM_CACHE_OP_INV: + return dmac_inv_range; + + case VC_SM_CACHE_OP_CLEAN: + return dmac_clean_range; + + case VC_SM_CACHE_OP_FLUSH: + return dmac_flush_range; + + default: + pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op); + return NULL; + } +} + +/* + * Clean/invalid/flush cache of which buffer is already pinned (i.e. accessed). + */ +static int clean_invalid_contig_2d(const void __user *addr, + const size_t block_count, + const size_t block_size, + const size_t stride, + const unsigned int cache_op) +{ + size_t i; + void (*op_fn)(const void *start, const void *end); + + if (!block_size) { + pr_err("[%s]: size cannot be 0\n", __func__); + return -EINVAL; + } + + op_fn = cache_op_to_func(cache_op); + if (!op_fn) + return -EINVAL; + + for (i = 0; i < block_count; i ++, addr += stride) + op_fn(addr, addr + block_size); + + return 0; +} + +static int vc_sm_cma_clean_invalid2(unsigned int cmdnr, unsigned long arg) +{ + struct vc_sm_cma_ioctl_clean_invalid2 ioparam; + struct vc_sm_cma_ioctl_clean_invalid_block *block = NULL; + int i, ret = 0; + + /* Get parameter data. */ + if (copy_from_user(&ioparam, (void *)arg, sizeof(ioparam))) { + pr_err("[%s]: failed to copy-from-user header for cmd %x\n", + __func__, cmdnr); + return -EFAULT; + } + block = kmalloc(ioparam.op_count * sizeof(*block), GFP_KERNEL); + if (!block) + return -EFAULT; + + if (copy_from_user(block, (void *)(arg + sizeof(ioparam)), + ioparam.op_count * sizeof(*block)) != 0) { + pr_err("[%s]: failed to copy-from-user payload for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + goto out; + } + + for (i = 0; i < ioparam.op_count; i++) { + const struct vc_sm_cma_ioctl_clean_invalid_block * const op = block + i; + + if (op->invalidate_mode == VC_SM_CACHE_OP_NOP) + continue; + + ret = clean_invalid_contig_2d((void __user *)op->start_address, + op->block_count, op->block_size, + op->inter_block_stride, + op->invalidate_mode); + if (ret) + break; + } +out: + kfree(block); + + return ret; +} + static long vc_sm_cma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1272,9 +1366,6 @@ static long vc_sm_cma_ioctl(struct file return -EPERM; } - pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr, - current->tgid, file_data->pid); - /* Action is a re-post of a previously interrupted action? */ if (file_data->restart_sys == -EINTR) { struct vc_sm_action_clean_t action_clean; @@ -1357,7 +1448,18 @@ static long vc_sm_cma_ioctl(struct file break; } + /* + * Flush/Invalidate the cache for a given mapping. + * Blocks must be pinned (i.e. accessed) before this call. + */ + case VC_SM_CMA_CMD_CLEAN_INVALID2: + ret = vc_sm_cma_clean_invalid2(cmdnr, arg); + break; + default: + pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr, + current->tgid, file_data->pid); + ret = -EINVAL; break; } @@ -1365,10 +1467,43 @@ static long vc_sm_cma_ioctl(struct file return ret; } +#ifdef CONFIG_COMPAT +struct vc_sm_cma_ioctl_clean_invalid2_32 { + u32 op_count; + struct vc_sm_cma_ioctl_clean_invalid_block { + u16 invalidate_mode; + u16 block_count; + compat_uptr_t start_address; + u32 block_size; + u32 inter_block_stride; + } s[0]; +}; + +#define VC_SM_CMA_CMD_CLEAN_INVALID2_32\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\ + struct vc_sm_cma_ioctl_clean_invalid2) + +static long vc_sm_cma_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case VC_SM_CMA_CMD_CLEAN_INVALID2_32: + /* FIXME */ + break; + + default: + return vc_sm_cma_compat_ioctl(file, cmd, arg); + } +} +#endif + /* Device operations that we managed in this driver. */ static const struct file_operations vc_sm_ops = { .owner = THIS_MODULE, .unlocked_ioctl = vc_sm_cma_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vc_sm_cma_compat_ioctl, +#endif .open = vc_sm_cma_open, .release = vc_sm_cma_release, }; --- a/include/linux/broadcom/vc_sm_cma_ioctl.h +++ b/include/linux/broadcom/vc_sm_cma_ioctl.h @@ -33,6 +33,8 @@ enum vc_sm_cma_cmd_e { VC_SM_CMA_CMD_IMPORT_DMABUF, + VC_SM_CMA_CMD_CLEAN_INVALID2, + VC_SM_CMA_CMD_LAST /* Do not delete */ }; @@ -75,6 +77,27 @@ struct vc_sm_cma_ioctl_import_dmabuf { __u64 dma_addr; }; +/* + * Cache functions to be set to struct vc_sm_cma_ioctl_clean_invalid2 + * invalidate_mode. + */ +#define VC_SM_CACHE_OP_NOP 0x00 +#define VC_SM_CACHE_OP_INV 0x01 +#define VC_SM_CACHE_OP_CLEAN 0x02 +#define VC_SM_CACHE_OP_FLUSH 0x03 + +struct vc_sm_cma_ioctl_clean_invalid2 { + __u32 op_count; + __u32 pad; + struct vc_sm_cma_ioctl_clean_invalid_block { + __u32 invalidate_mode; + __u32 block_count; + void * __user start_address; + __u32 block_size; + __u32 inter_block_stride; + } s[0]; +}; + /* IOCTL numbers */ #define VC_SM_CMA_IOCTL_MEM_ALLOC\ _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_ALLOC,\ @@ -84,4 +107,8 @@ struct vc_sm_cma_ioctl_import_dmabuf { _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_IMPORT_DMABUF,\ struct vc_sm_cma_ioctl_import_dmabuf) +#define VC_SM_CMA_IOCTL_MEM_CLEAN_INVALID2\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\ + struct vc_sm_cma_ioctl_clean_invalid2) + #endif /* __VC_SM_CMA_IOCTL_H */