diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 992f88dedeb..5fa27ba090e 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "kgsl.h" #include "kgsl_debugfs.h" @@ -2104,6 +2106,82 @@ kgsl_ioctl_gpumem_sync_cache(struct kgsl_device_private *dev_priv, return ret; } +static int mem_id_cmp(const void *_a, const void *_b) +{ + const unsigned int *a = _a, *b = _b; + int cmp = a - b; + return (cmp < 0) ? -1 : (cmp > 0); +} + +static long +kgsl_ioctl_gpumem_sync_cache_bulk(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int i; + struct kgsl_gpumem_sync_cache_bulk *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + unsigned int id, last_id = 0, *id_list = NULL, actual_count = 0; + struct kgsl_mem_entry **entries = NULL; + long ret = 0; + + if (param->id_list == NULL || param->count == 0 + || param->count > (UINT_MAX/sizeof(unsigned int))) + return -EINVAL; + + id_list = kzalloc(param->count * sizeof(unsigned int), GFP_KERNEL); + if (id_list == NULL) + return -ENOMEM; + + entries = kzalloc(param->count * sizeof(*entries), GFP_KERNEL); + if (entries == NULL) { + ret = -ENOMEM; + goto end; + } + + if (copy_from_user(id_list, param->id_list, + param->count * sizeof(unsigned int))) { + ret = -EFAULT; + goto end; + } + /* sort the ids so we can weed out duplicates */ + sort(id_list, param->count, sizeof(int), mem_id_cmp, NULL); + + for (i = 0; i < param->count; i++) { + unsigned int cachemode; + struct kgsl_mem_entry *entry = NULL; + + id = id_list[i]; + /* skip 0 ids or duplicates */ + if (id == last_id) + continue; + + entry = kgsl_sharedmem_find_id(private, id); + if (entry == NULL) + continue; + + /* skip uncached memory */ + cachemode = kgsl_memdesc_get_cachemode(&entry->memdesc); + if (cachemode != KGSL_CACHEMODE_WRITETHROUGH && + cachemode != KGSL_CACHEMODE_WRITEBACK) { + kgsl_mem_entry_put(entry); + continue; + } + + entries[actual_count++] = entry; + + last_id = id; + } + + for (i = 0; i < actual_count; i++) { + _kgsl_gpumem_sync_cache(entries[i], param->op); + kgsl_mem_entry_put(entries[i]); + } +end: + kfree(entries); + kfree(id_list); + return ret; +} + /* Legacy cache function, does a flush (clean + invalidate) */ static long @@ -2510,6 +2588,8 @@ static const struct { kgsl_ioctl_gpumem_get_info, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE, kgsl_ioctl_gpumem_sync_cache, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK, + kgsl_ioctl_gpumem_sync_cache_bulk, 0), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index e8de7693e51..ae88807df86 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -781,6 +781,27 @@ struct kgsl_perfcounter_read { #define IOCTL_KGSL_PERFCOUNTER_READ \ _IOWR(KGSL_IOC_TYPE, 0x3B, struct kgsl_perfcounter_read) +/* + * struct kgsl_gpumem_sync_cache_bulk - argument to + * IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK + * @id_list: list of GPU buffer ids of the buffers to sync + * @count: number of GPU buffer ids in id_list + * @op: a mask of KGSL_GPUMEM_CACHE_* values + * + * Sync the cache for memory headed to and from the GPU. Certain + * optimizations can be made on the cache operation based on the total + * size of the working set of memory to be managed. + */ +struct kgsl_gpumem_sync_cache_bulk { + unsigned int *id_list; + unsigned int count; + unsigned int op; +/* private: reserved for future use */ + unsigned int __pad[2]; /* For future binary compatibility */ +}; + +#define IOCTL_KGSL_GPUMEM_SYNC_CACHE_BULK \ + _IOWR(KGSL_IOC_TYPE, 0x3C, struct kgsl_gpumem_sync_cache_bulk) #ifdef __KERNEL__ #ifdef CONFIG_MSM_KGSL_DRM