diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 3f1b5d30..626c66d8 100755 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -121,18 +121,20 @@ kgsl_mem_entry_destroy(struct kref *kref) struct kgsl_mem_entry *entry = container_of(kref, struct kgsl_mem_entry, refcount); - size_t size = entry->memdesc.size; + + entry->priv->stats[entry->memtype].cur -= entry->memdesc.size; + + if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) + kgsl_driver.stats.mapped -= entry->memdesc.size; kgsl_sharedmem_free(&entry->memdesc); - if (entry->memtype == KGSL_USER_MEMORY) - entry->priv->stats.user -= size; - else if (entry->memtype == KGSL_MAPPED_MEMORY) { - if (entry->file_ptr) - fput(entry->file_ptr); - - kgsl_driver.stats.mapped -= size; - entry->priv->stats.mapped -= size; + switch (entry->memtype) { + case KGSL_MEM_ENTRY_PMEM: + case KGSL_MEM_ENTRY_ASHMEM: + if (entry->priv_data) + fput(entry->priv_data); + break; } kfree(entry); @@ -585,11 +587,6 @@ kgsl_put_process_private(struct kgsl_device *device, if (--private->refcnt) goto unlock; - KGSL_MEM_INFO(device, - "Memory usage: user (%d/%d) mapped (%d/%d)\n", - private->stats.user, private->stats.user_max, - private->stats.mapped, private->stats.mapped_max); - kgsl_process_uninit_sysfs(private); list_del(&private->list); @@ -1203,13 +1200,12 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, param->gpuaddr = entry->memdesc.gpuaddr; - entry->memtype = KGSL_USER_MEMORY; + entry->memtype = KGSL_MEM_ENTRY_KERNEL; kgsl_mem_entry_attach_process(entry, private); /* Process specific statistics */ - KGSL_STATS_ADD(len, private->stats.user, - private->stats.user_max); + kgsl_process_add_stats(private, entry->memtype, len); kgsl_check_idle(dev_priv->device); return 0; @@ -1310,7 +1306,7 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, } - entry->file_ptr = filep; + entry->priv_data = filep; entry->memdesc.pagetable = pagetable; entry->memdesc.size = size; @@ -1482,7 +1478,7 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, goto err; } - entry->file_ptr = filep; + entry->priv_data = filep; entry->memdesc.pagetable = pagetable; entry->memdesc.size = ALIGN(size, PAGE_SIZE); entry->memdesc.hostptr = hostptr; @@ -1533,6 +1529,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_phys_file(entry, private->pagetable, param->fd, param->offset, param->len); + entry->memtype = KGSL_MEM_ENTRY_PMEM; break; case KGSL_USER_MEM_TYPE_ADDR: @@ -1549,6 +1546,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_hostptr(entry, private->pagetable, (void *) param->hostptr, param->offset, param->len); + entry->memtype = KGSL_MEM_ENTRY_USER; break; case KGSL_USER_MEM_TYPE_ASHMEM: @@ -1565,6 +1563,8 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_ashmem(entry, private->pagetable, param->fd, (void *) param->hostptr, param->len); + + entry->memtype = KGSL_MEM_ENTRY_ASHMEM; break; default: KGSL_CORE_ERR("Invalid memory type: %x\n", memtype); @@ -1584,14 +1584,10 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, /* Adjust the returned value for a non 4k aligned offset */ param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK); - entry->memtype = KGSL_MAPPED_MEMORY; - KGSL_STATS_ADD(param->len, kgsl_driver.stats.mapped, kgsl_driver.stats.mapped_max); - /* Statistics */ - KGSL_STATS_ADD(param->len, private->stats.mapped, - private->stats.mapped_max); + kgsl_process_add_stats(private, entry->memtype, param->len); kgsl_mem_entry_attach_process(entry, private); @@ -1599,8 +1595,8 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, return result; error_put_file_ptr: - if (entry->file_ptr) - fput(entry->file_ptr); + if (entry->priv_data) + fput(entry->priv_data); error: kfree(entry); @@ -1626,6 +1622,7 @@ kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, result = -EINVAL; goto done; } + if (!entry->memdesc.hostptr) { KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", param->gpuaddr); @@ -1633,9 +1630,6 @@ kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, } kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); - - /* Statistics - keep track of how many flushes each process does */ - private->stats.flushes++; done: spin_unlock(&private->mem_lock); return result; @@ -1658,12 +1652,11 @@ kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, param->size, param->flags); if (result == 0) { - entry->memtype = KGSL_USER_MEMORY; + entry->memtype = KGSL_MEM_ENTRY_KERNEL; kgsl_mem_entry_attach_process(entry, private); param->gpuaddr = entry->memdesc.gpuaddr; - KGSL_STATS_ADD(entry->memdesc.size, private->stats.user, - private->stats.user_max); + kgsl_process_add_stats(private, entry->memtype, param->size); } else kfree(entry); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 324b6b65..0c5eef57 100755 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -104,9 +104,6 @@ struct kgsl_driver { extern struct kgsl_driver kgsl_driver; -#define KGSL_USER_MEMORY 1 -#define KGSL_MAPPED_MEMORY 2 - struct kgsl_pagetable; struct kgsl_memdesc_ops; @@ -123,11 +120,19 @@ struct kgsl_memdesc { struct kgsl_memdesc_ops *ops; }; +/* List of different memory entry types */ + +#define KGSL_MEM_ENTRY_KERNEL 0 +#define KGSL_MEM_ENTRY_PMEM 1 +#define KGSL_MEM_ENTRY_ASHMEM 2 +#define KGSL_MEM_ENTRY_USER 3 +#define KGSL_MEM_ENTRY_MAX 4 + struct kgsl_mem_entry { struct kref refcount; struct kgsl_memdesc memdesc; int memtype; - struct file *file_ptr; + void *priv_data; struct list_head list; uint32_t free_timestamp; /* back pointer to private structure under whose context this diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 44c17bc5..b7524d87 100755 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -199,15 +199,12 @@ struct kgsl_process_private { struct list_head mem_list; struct kgsl_pagetable *pagetable; struct list_head list; - struct kobject *kobj; + struct kobject kobj; struct { - unsigned int user; - unsigned int user_max; - unsigned int mapped; - unsigned int mapped_max; - unsigned int flushes; - } stats; + unsigned int cur; + unsigned int max; + } stats[KGSL_MEM_ENTRY_MAX]; }; struct kgsl_device_private { @@ -222,6 +219,14 @@ struct kgsl_power_stats { struct kgsl_device *kgsl_get_device(int dev_idx); +static inline void kgsl_process_add_stats(struct kgsl_process_private *priv, + unsigned int type, size_t size) +{ + priv->stats[type].cur += size; + if (priv->stats[type].max < priv->stats[type].cur) + priv->stats[type].max = priv->stats[type].cur; +} + static inline void kgsl_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 09dbd7e8..db190d25 100755 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -23,6 +23,52 @@ #include "kgsl_device.h" #include "adreno_ringbuffer.h" +/* An attribute for showing per-process memory statistics */ +struct kgsl_mem_entry_attribute { + struct attribute attr; + int memtype; + ssize_t (*show)(struct kgsl_process_private *priv, + int type, char *buf); +}; + +#define to_mem_entry_attr(a) \ +container_of(a, struct kgsl_mem_entry_attribute, attr) + +#define __MEM_ENTRY_ATTR(_type, _name, _show) \ +{ \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .memtype = _type, \ + .show = _show, \ +} + +/* + * A structure to hold the attributes for a particular memory type. + * For each memory type in each process we store the current and maximum + * memory usage and display the counts in sysfs. This structure and + * the following macro allow us to simplify the definition for those + * adding new memory types + */ + +struct mem_entry_stats { + int memtype; + struct kgsl_mem_entry_attribute attr; + struct kgsl_mem_entry_attribute max_attr; +}; + + +#define MEM_ENTRY_STAT(_type, _name) \ +{ \ + .memtype = _type, \ + .attr = __MEM_ENTRY_ATTR(_type, _name, mem_entry_show), \ + .max_attr = __MEM_ENTRY_ATTR(_type, _name##_max, \ + mem_entry_max_show), \ +} + + +/** + * Given a kobj, find the process structure attached to it + */ + static struct kgsl_process_private * _get_priv_from_kobj(struct kobject *kobj) { @@ -43,87 +89,106 @@ _get_priv_from_kobj(struct kobject *kobj) return NULL; } -/* sharedmem / memory sysfs files */ +/** + * Show the current amount of memory allocated for the given memtype + */ static ssize_t -process_show(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) +mem_entry_show(struct kgsl_process_private *priv, int type, char *buf) { + return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].cur); +} + +/** + * Show the maximum memory allocated for the given memtype through the life of + * the process + */ + +static ssize_t +mem_entry_max_show(struct kgsl_process_private *priv, int type, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].max); +} + + +static void mem_entry_sysfs_release(struct kobject *kobj) +{ +} + +static ssize_t mem_entry_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kgsl_mem_entry_attribute *pattr = to_mem_entry_attr(attr); struct kgsl_process_private *priv; - unsigned int val = 0; + ssize_t ret; mutex_lock(&kgsl_driver.process_mutex); priv = _get_priv_from_kobj(kobj); - if (priv == NULL) { - mutex_unlock(&kgsl_driver.process_mutex); - return 0; - } - - if (!strncmp(attr->attr.name, "user", 4)) - val = priv->stats.user; - if (!strncmp(attr->attr.name, "user_max", 8)) - val = priv->stats.user_max; - if (!strncmp(attr->attr.name, "mapped", 6)) - val = priv->stats.mapped; - if (!strncmp(attr->attr.name, "mapped_max", 10)) - val = priv->stats.mapped_max; - if (!strncmp(attr->attr.name, "flushes", 7)) - val = priv->stats.flushes; + if (priv && pattr->show) + ret = pattr->show(priv, pattr->memtype, buf); + else + ret = -EIO; mutex_unlock(&kgsl_driver.process_mutex); - return snprintf(buf, PAGE_SIZE, "%u\n", val); + return ret; } -#define KGSL_MEMSTAT_ATTR(_name, _show) \ - static struct kobj_attribute attr_##_name = \ - __ATTR(_name, 0444, _show, NULL) - -KGSL_MEMSTAT_ATTR(user, process_show); -KGSL_MEMSTAT_ATTR(user_max, process_show); -KGSL_MEMSTAT_ATTR(mapped, process_show); -KGSL_MEMSTAT_ATTR(mapped_max, process_show); -KGSL_MEMSTAT_ATTR(flushes, process_show); - -static struct attribute *process_attrs[] = { - &attr_user.attr, - &attr_user_max.attr, - &attr_mapped.attr, - &attr_mapped_max.attr, - &attr_flushes.attr, - NULL +static const struct sysfs_ops mem_entry_sysfs_ops = { + .show = mem_entry_sysfs_show, }; -static struct attribute_group process_attr_group = { - .attrs = process_attrs, +static struct kobj_type ktype_mem_entry = { + .sysfs_ops = &mem_entry_sysfs_ops, + .default_attrs = NULL, + .release = mem_entry_sysfs_release +}; + +static struct mem_entry_stats mem_stats[] = { + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_KERNEL, kernel), +#ifdef CONFIG_ANDROID_PMEM + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_PMEM, pmem), +#endif +#ifdef CONFIG_ASHMEM + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ASHMEM, ashmem), +#endif + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_USER, user), }; void kgsl_process_uninit_sysfs(struct kgsl_process_private *private) { - /* Remove the sysfs entry */ - if (private->kobj) { - sysfs_remove_group(private->kobj, &process_attr_group); - kobject_put(private->kobj); + int i; + + for (i = 0; i < ARRAY_SIZE(mem_stats); i++) { + sysfs_remove_file(&private->kobj, &mem_stats[i].attr.attr); + sysfs_remove_file(&private->kobj, + &mem_stats[i].max_attr.attr); } + + kobject_put(&private->kobj); } void kgsl_process_init_sysfs(struct kgsl_process_private *private) { unsigned char name[16]; + int i, ret; - /* Add a entry to the sysfs device */ snprintf(name, sizeof(name), "%d", private->pid); - private->kobj = kobject_create_and_add(name, kgsl_driver.prockobj); - /* sysfs failure isn't fatal, just annoying */ - if (private->kobj != NULL) { - if (sysfs_create_group(private->kobj, &process_attr_group)) { - kobject_put(private->kobj); - private->kobj = NULL; - } + if (kobject_init_and_add(&private->kobj, &ktype_mem_entry, + kgsl_driver.prockobj, name)) + return; + + for (i = 0; i < ARRAY_SIZE(mem_stats); i++) { + /* We need to check the value of sysfs_create_file, but we + * don't really care if it passed or not */ + + ret = sysfs_create_file(&private->kobj, + &mem_stats[i].attr.attr); + ret = sysfs_create_file(&private->kobj, + &mem_stats[i].max_attr.attr); } }