gpu: ion: Add dump of memory map
Add dump of physical memory map, showing allocated and free areas, to debugfs interface for heaps with carveout memory. Change-Id: I9bda9f3e555e55570c95e652616ca1fcc25eb0ab Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
4f3cadaf47
commit
eeab21e633
@@ -1551,6 +1551,144 @@ static size_t ion_debug_heap_total(struct ion_client *client,
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through a clients handles to find if the buffer is owned
|
||||||
|
* by this client. Used for debug output.
|
||||||
|
* @param client pointer to candidate owner of buffer
|
||||||
|
* @param buf pointer to buffer that we are trying to find the owner of
|
||||||
|
* @return 1 if found, 0 otherwise
|
||||||
|
*/
|
||||||
|
static int ion_debug_find_buffer_owner(const struct ion_client *client,
|
||||||
|
const struct ion_buffer *buf)
|
||||||
|
{
|
||||||
|
struct rb_node *n;
|
||||||
|
|
||||||
|
for (n = rb_first(&client->handles); n; n = rb_next(n)) {
|
||||||
|
const struct ion_handle *handle = rb_entry(n,
|
||||||
|
const struct ion_handle,
|
||||||
|
node);
|
||||||
|
if (handle->buffer == buf)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds mem_map_data pointer to the tree of mem_map
|
||||||
|
* Used for debug output.
|
||||||
|
* @param mem_map The mem_map tree
|
||||||
|
* @param data The new data to add to the tree
|
||||||
|
*/
|
||||||
|
static void ion_debug_mem_map_add(struct rb_root *mem_map,
|
||||||
|
struct mem_map_data *data)
|
||||||
|
{
|
||||||
|
struct rb_node **p = &mem_map->rb_node;
|
||||||
|
struct rb_node *parent = NULL;
|
||||||
|
struct mem_map_data *entry;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
parent = *p;
|
||||||
|
entry = rb_entry(parent, struct mem_map_data, node);
|
||||||
|
|
||||||
|
if (data->addr < entry->addr) {
|
||||||
|
p = &(*p)->rb_left;
|
||||||
|
} else if (data->addr > entry->addr) {
|
||||||
|
p = &(*p)->rb_right;
|
||||||
|
} else {
|
||||||
|
pr_err("%s: mem_map_data already found.", __func__);
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rb_link_node(&data->node, parent, p);
|
||||||
|
rb_insert_color(&data->node, mem_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for an owner of a buffer by iterating over all ION clients.
|
||||||
|
* @param dev ion device containing pointers to all the clients.
|
||||||
|
* @param buffer pointer to buffer we are trying to find the owner of.
|
||||||
|
* @return name of owner.
|
||||||
|
*/
|
||||||
|
const char *ion_debug_locate_owner(const struct ion_device *dev,
|
||||||
|
const struct ion_buffer *buffer)
|
||||||
|
{
|
||||||
|
struct rb_node *j;
|
||||||
|
const char *client_name = NULL;
|
||||||
|
|
||||||
|
for (j = rb_first(&dev->clients); j && !client_name;
|
||||||
|
j = rb_next(j)) {
|
||||||
|
struct ion_client *client = rb_entry(j, struct ion_client,
|
||||||
|
node);
|
||||||
|
if (ion_debug_find_buffer_owner(client, buffer))
|
||||||
|
client_name = client->name;
|
||||||
|
}
|
||||||
|
return client_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mem_map of the heap.
|
||||||
|
* @param s seq_file to log error message to.
|
||||||
|
* @param heap The heap to create mem_map for.
|
||||||
|
* @param mem_map The mem map to be created.
|
||||||
|
*/
|
||||||
|
void ion_debug_mem_map_create(struct seq_file *s, struct ion_heap *heap,
|
||||||
|
struct rb_root *mem_map)
|
||||||
|
{
|
||||||
|
struct ion_device *dev = heap->dev;
|
||||||
|
struct rb_node *n;
|
||||||
|
|
||||||
|
for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
|
||||||
|
struct ion_buffer *buffer =
|
||||||
|
rb_entry(n, struct ion_buffer, node);
|
||||||
|
if (buffer->heap->id == heap->id) {
|
||||||
|
struct mem_map_data *data =
|
||||||
|
kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data) {
|
||||||
|
seq_printf(s, "ERROR: out of memory. "
|
||||||
|
"Part of memory map will not be logged\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data->addr = buffer->priv_phys;
|
||||||
|
data->addr_end = buffer->priv_phys + buffer->size-1;
|
||||||
|
data->size = buffer->size;
|
||||||
|
data->client_name = ion_debug_locate_owner(dev, buffer);
|
||||||
|
ion_debug_mem_map_add(mem_map, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the memory allocated by ion_debug_mem_map_create
|
||||||
|
* @param mem_map The mem map to free.
|
||||||
|
*/
|
||||||
|
static void ion_debug_mem_map_destroy(struct rb_root *mem_map)
|
||||||
|
{
|
||||||
|
if (mem_map) {
|
||||||
|
struct rb_node *n;
|
||||||
|
while ((n = rb_first(mem_map)) != 0) {
|
||||||
|
struct mem_map_data *data =
|
||||||
|
rb_entry(n, struct mem_map_data, node);
|
||||||
|
rb_erase(&data->node, mem_map);
|
||||||
|
kfree(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print heap debug information.
|
||||||
|
* @param s seq_file to log message to.
|
||||||
|
* @param heap pointer to heap that we will print debug information for.
|
||||||
|
*/
|
||||||
|
static void ion_heap_print_debug(struct seq_file *s, struct ion_heap *heap)
|
||||||
|
{
|
||||||
|
if (heap->ops->print_debug) {
|
||||||
|
struct rb_root mem_map = RB_ROOT;
|
||||||
|
ion_debug_mem_map_create(s, heap, &mem_map);
|
||||||
|
heap->ops->print_debug(heap, s, &mem_map);
|
||||||
|
ion_debug_mem_map_destroy(&mem_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
||||||
{
|
{
|
||||||
struct ion_heap *heap = s->private;
|
struct ion_heap *heap = s->private;
|
||||||
@@ -1577,8 +1715,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||||||
client->pid, size);
|
client->pid, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (heap->ops->print_debug)
|
ion_heap_print_debug(s, heap);
|
||||||
heap->ops->print_debug(heap, s);
|
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,7 +260,8 @@ int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s)
|
static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s,
|
||||||
|
const struct rb_root *mem_map)
|
||||||
{
|
{
|
||||||
struct ion_carveout_heap *carveout_heap =
|
struct ion_carveout_heap *carveout_heap =
|
||||||
container_of(heap, struct ion_carveout_heap, heap);
|
container_of(heap, struct ion_carveout_heap, heap);
|
||||||
@@ -269,6 +270,44 @@ static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s)
|
|||||||
carveout_heap->allocated_bytes);
|
carveout_heap->allocated_bytes);
|
||||||
seq_printf(s, "total heap size: %lx\n", carveout_heap->total_size);
|
seq_printf(s, "total heap size: %lx\n", carveout_heap->total_size);
|
||||||
|
|
||||||
|
if (mem_map) {
|
||||||
|
unsigned long base = carveout_heap->base;
|
||||||
|
unsigned long size = carveout_heap->total_size;
|
||||||
|
unsigned long end = base+size;
|
||||||
|
unsigned long last_end = base;
|
||||||
|
struct rb_node *n;
|
||||||
|
|
||||||
|
seq_printf(s, "\nMemory Map\n");
|
||||||
|
seq_printf(s, "%16.s %14.s %14.s %14.s\n",
|
||||||
|
"client", "start address", "end address",
|
||||||
|
"size (hex)");
|
||||||
|
|
||||||
|
for (n = rb_first(mem_map); n; n = rb_next(n)) {
|
||||||
|
struct mem_map_data *data =
|
||||||
|
rb_entry(n, struct mem_map_data, node);
|
||||||
|
const char *client_name = "(null)";
|
||||||
|
|
||||||
|
if (last_end < data->addr) {
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
|
||||||
|
"FREE", last_end, data->addr-1,
|
||||||
|
data->addr-last_end,
|
||||||
|
data->addr-last_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->client_name)
|
||||||
|
client_name = data->client_name;
|
||||||
|
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
|
||||||
|
client_name, data->addr,
|
||||||
|
data->addr_end,
|
||||||
|
data->size, data->size);
|
||||||
|
last_end = data->addr_end+1;
|
||||||
|
}
|
||||||
|
if (last_end < end) {
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n", "FREE",
|
||||||
|
last_end, end-1, end-last_end, end-last_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -572,7 +572,8 @@ int ion_cp_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s)
|
static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s,
|
||||||
|
const struct rb_root *mem_map)
|
||||||
{
|
{
|
||||||
unsigned long total_alloc;
|
unsigned long total_alloc;
|
||||||
unsigned long total_size;
|
unsigned long total_size;
|
||||||
@@ -597,6 +598,45 @@ static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s)
|
|||||||
seq_printf(s, "heap protected: %s\n", heap_protected ? "Yes" : "No");
|
seq_printf(s, "heap protected: %s\n", heap_protected ? "Yes" : "No");
|
||||||
seq_printf(s, "reusable: %s\n", cp_heap->reusable ? "Yes" : "No");
|
seq_printf(s, "reusable: %s\n", cp_heap->reusable ? "Yes" : "No");
|
||||||
|
|
||||||
|
if (mem_map) {
|
||||||
|
unsigned long base = cp_heap->base;
|
||||||
|
unsigned long size = cp_heap->total_size;
|
||||||
|
unsigned long end = base+size;
|
||||||
|
unsigned long last_end = base;
|
||||||
|
struct rb_node *n;
|
||||||
|
|
||||||
|
seq_printf(s, "\nMemory Map\n");
|
||||||
|
seq_printf(s, "%16.s %14.s %14.s %14.s\n",
|
||||||
|
"client", "start address", "end address",
|
||||||
|
"size (hex)");
|
||||||
|
|
||||||
|
for (n = rb_first(mem_map); n; n = rb_next(n)) {
|
||||||
|
struct mem_map_data *data =
|
||||||
|
rb_entry(n, struct mem_map_data, node);
|
||||||
|
const char *client_name = "(null)";
|
||||||
|
|
||||||
|
if (last_end < data->addr) {
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
|
||||||
|
"FREE", last_end, data->addr-1,
|
||||||
|
data->addr-last_end,
|
||||||
|
data->addr-last_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->client_name)
|
||||||
|
client_name = data->client_name;
|
||||||
|
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n",
|
||||||
|
client_name, data->addr,
|
||||||
|
data->addr_end,
|
||||||
|
data->size, data->size);
|
||||||
|
last_end = data->addr_end+1;
|
||||||
|
}
|
||||||
|
if (last_end < end) {
|
||||||
|
seq_printf(s, "%16.s %14lx %14lx %14lu (%lx)\n", "FREE",
|
||||||
|
last_end, end-1, end-last_end, end-last_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,6 +957,14 @@ void ion_cp_heap_destroy(struct ion_heap *heap)
|
|||||||
cp_heap = NULL;
|
cp_heap = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ion_cp_heap_get_base(struct ion_heap *heap, unsigned long *base,
|
||||||
|
unsigned long *size) \
|
||||||
|
{
|
||||||
|
struct ion_cp_heap *cp_heap =
|
||||||
|
container_of(heap, struct ion_cp_heap, heap);
|
||||||
|
*base = cp_heap->base;
|
||||||
|
*size = cp_heap->total_size;
|
||||||
|
}
|
||||||
|
|
||||||
/* SCM related code for locking down memory for content protection */
|
/* SCM related code for locking down memory for content protection */
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,8 @@ struct ion_heap_ops {
|
|||||||
unsigned long iova_length,
|
unsigned long iova_length,
|
||||||
unsigned long flags);
|
unsigned long flags);
|
||||||
void (*unmap_iommu)(struct ion_iommu_map *data);
|
void (*unmap_iommu)(struct ion_iommu_map *data);
|
||||||
int (*print_debug)(struct ion_heap *heap, struct seq_file *s);
|
int (*print_debug)(struct ion_heap *heap, struct seq_file *s,
|
||||||
|
const struct rb_root *mem_map);
|
||||||
int (*secure_heap)(struct ion_heap *heap);
|
int (*secure_heap)(struct ion_heap *heap);
|
||||||
int (*unsecure_heap)(struct ion_heap *heap);
|
int (*unsecure_heap)(struct ion_heap *heap);
|
||||||
};
|
};
|
||||||
@@ -173,7 +174,22 @@ struct ion_heap {
|
|||||||
const char *name;
|
const char *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mem_map_data - represents information about the memory map for a heap
|
||||||
|
* @node: rb node used to store in the tree of mem_map_data
|
||||||
|
* @addr: start address of memory region.
|
||||||
|
* @addr: end address of memory region.
|
||||||
|
* @size: size of memory region
|
||||||
|
* @client_name: name of the client who owns this buffer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct mem_map_data {
|
||||||
|
struct rb_node node;
|
||||||
|
unsigned long addr;
|
||||||
|
unsigned long addr_end;
|
||||||
|
unsigned long size;
|
||||||
|
const char *client_name;
|
||||||
|
};
|
||||||
|
|
||||||
#define iommu_map_domain(__m) ((__m)->domain_info[1])
|
#define iommu_map_domain(__m) ((__m)->domain_info[1])
|
||||||
#define iommu_map_partition(__m) ((__m)->domain_info[0])
|
#define iommu_map_partition(__m) ((__m)->domain_info[0])
|
||||||
@@ -286,4 +302,9 @@ int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
|
|||||||
void *uaddr, unsigned long offset, unsigned long len,
|
void *uaddr, unsigned long offset, unsigned long len,
|
||||||
unsigned int cmd);
|
unsigned int cmd);
|
||||||
|
|
||||||
|
void ion_cp_heap_get_base(struct ion_heap *heap, unsigned long *base,
|
||||||
|
unsigned long *size);
|
||||||
|
|
||||||
|
void ion_mem_map_show(struct ion_heap *heap);
|
||||||
|
|
||||||
#endif /* _ION_PRIV_H */
|
#endif /* _ION_PRIV_H */
|
||||||
|
|||||||
@@ -214,7 +214,8 @@ int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s)
|
static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s,
|
||||||
|
const struct rb_root *unused)
|
||||||
{
|
{
|
||||||
seq_printf(s, "total bytes currently allocated: %lx\n",
|
seq_printf(s, "total bytes currently allocated: %lx\n",
|
||||||
(unsigned long) atomic_read(&system_heap_allocated));
|
(unsigned long) atomic_read(&system_heap_allocated));
|
||||||
@@ -417,7 +418,8 @@ int ion_system_contig_heap_cache_ops(struct ion_heap *heap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int ion_system_contig_print_debug(struct ion_heap *heap,
|
static int ion_system_contig_print_debug(struct ion_heap *heap,
|
||||||
struct seq_file *s)
|
struct seq_file *s,
|
||||||
|
const struct rb_root *unused)
|
||||||
{
|
{
|
||||||
seq_printf(s, "total bytes currently allocated: %lx\n",
|
seq_printf(s, "total bytes currently allocated: %lx\n",
|
||||||
(unsigned long) atomic_read(&system_contig_heap_allocated));
|
(unsigned long) atomic_read(&system_contig_heap_allocated));
|
||||||
|
|||||||
Reference in New Issue
Block a user