msm: kgsl: Fixup per-process memory statistics
Make the framework for reporting per-process memory statistics a little bit more generic. This should make it easier to keep track of more external memory sources as they are added.
This commit is contained in:
parent
9d909cf27b
commit
91bbe54c4f
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user