[PATCH] ARM: add dma coherent region reporting via procfs

Nicolas Pitre nico at fluxnic.net
Fri Jan 20 16:29:21 EST 2012


On Fri, 20 Jan 2012, Russell King - ARM Linux wrote:

> Add a new seqfile for reporting coherent DMA allocations.  This contains
> the address range, size and the function which was used to allocate
> each region, allowing these allocations to be viewed in much the same
> way as /proc/vmallocinfo.
> 
> The DMA coherent region has limited space, so this allows allocation
> failures to be viewed, as well as finding out how much space is being
> used.
> 
> Make sure this file is only readable by root - same as vmallocinfo - to
> prevent information leakage.
> 
> Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>

Acked-by: Nicolas Pitre <nico at linaro.org>


> ---
>  arch/arm/mm/dma-mapping.c |   20 ++++++++----
>  arch/arm/mm/vmregion.c    |   76 ++++++++++++++++++++++++++++++++++++++++++++-
>  arch/arm/mm/vmregion.h    |    5 ++-
>  3 files changed, 92 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
> index 1aa664a..db23ae4 100644
> --- a/arch/arm/mm/dma-mapping.c
> +++ b/arch/arm/mm/dma-mapping.c
> @@ -214,7 +214,8 @@ static int __init consistent_init(void)
>  core_initcall(consistent_init);
>  
>  static void *
> -__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot)
> +__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
> +	const void *caller)
>  {
>  	struct arm_vmregion *c;
>  	size_t align;
> @@ -241,7 +242,7 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot)
>  	 * Allocate a virtual address in the consistent mapping region.
>  	 */
>  	c = arm_vmregion_alloc(&consistent_head, align, size,
> -			    gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
> +			    gfp & ~(__GFP_DMA | __GFP_HIGHMEM), caller);
>  	if (c) {
>  		pte_t *pte;
>  		int idx = CONSISTENT_PTE_INDEX(c->vm_start);
> @@ -320,14 +321,14 @@ static void __dma_free_remap(void *cpu_addr, size_t size)
>  
>  #else	/* !CONFIG_MMU */
>  
> -#define __dma_alloc_remap(page, size, gfp, prot)	page_address(page)
> +#define __dma_alloc_remap(page, size, gfp, prot, c)	page_address(page)
>  #define __dma_free_remap(addr, size)			do { } while (0)
>  
>  #endif	/* CONFIG_MMU */
>  
>  static void *
>  __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
> -	    pgprot_t prot)
> +	    pgprot_t prot, const void *caller)
>  {
>  	struct page *page;
>  	void *addr;
> @@ -349,7 +350,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
>  		return NULL;
>  
>  	if (!arch_is_coherent())
> -		addr = __dma_alloc_remap(page, size, gfp, prot);
> +		addr = __dma_alloc_remap(page, size, gfp, prot, caller);
>  	else
>  		addr = page_address(page);
>  
> @@ -374,7 +375,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gf
>  		return memory;
>  
>  	return __dma_alloc(dev, size, handle, gfp,
> -			   pgprot_dmacoherent(pgprot_kernel));
> +			   pgprot_dmacoherent(pgprot_kernel),
> +			   __builtin_return_address(0));
>  }
>  EXPORT_SYMBOL(dma_alloc_coherent);
>  
> @@ -386,7 +388,8 @@ void *
>  dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
>  {
>  	return __dma_alloc(dev, size, handle, gfp,
> -			   pgprot_writecombine(pgprot_kernel));
> +			   pgprot_writecombine(pgprot_kernel),
> +			   __builtin_return_address(0));
>  }
>  EXPORT_SYMBOL(dma_alloc_writecombine);
>  
> @@ -723,6 +726,9 @@ EXPORT_SYMBOL(dma_set_mask);
>  
>  static int __init dma_debug_do_init(void)
>  {
> +#ifdef CONFIG_MMU
> +	arm_vmregion_create_proc("dma-mappings", &consistent_head);
> +#endif
>  	dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
>  	return 0;
>  }
> diff --git a/arch/arm/mm/vmregion.c b/arch/arm/mm/vmregion.c
> index 036fdbf..a631016 100644
> --- a/arch/arm/mm/vmregion.c
> +++ b/arch/arm/mm/vmregion.c
> @@ -1,5 +1,8 @@
> +#include <linux/fs.h>
>  #include <linux/spinlock.h>
>  #include <linux/list.h>
> +#include <linux/proc_fs.h>
> +#include <linux/seq_file.h>
>  #include <linux/slab.h>
>  
>  #include "vmregion.h"
> @@ -36,7 +39,7 @@
>  
>  struct arm_vmregion *
>  arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align,
> -		   size_t size, gfp_t gfp)
> +		   size_t size, gfp_t gfp, const void *caller)
>  {
>  	unsigned long start = head->vm_start, addr = head->vm_end;
>  	unsigned long flags;
> @@ -52,6 +55,8 @@ arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align,
>  	if (!new)
>  		goto out;
>  
> +	new->caller = caller;
> +
>  	spin_lock_irqsave(&head->vm_lock, flags);
>  
>  	addr = rounddown(addr - size, align);
> @@ -129,3 +134,72 @@ void arm_vmregion_free(struct arm_vmregion_head *head, struct arm_vmregion *c)
>  
>  	kfree(c);
>  }
> +
> +#ifdef CONFIG_PROC_FS
> +static int arm_vmregion_show(struct seq_file *m, void *p)
> +{
> +	struct arm_vmregion *c = list_entry(p, struct arm_vmregion, vm_list);
> +
> +	seq_printf(m, "0x%08lx-0x%08lx %7lu", c->vm_start, c->vm_end,
> +		c->vm_end - c->vm_start);
> +	if (c->caller)
> +		seq_printf(m, " %pS", (void *)c->caller);
> +	seq_putc(m, '\n');
> +	return 0;
> +}
> +
> +static void *arm_vmregion_start(struct seq_file *m, loff_t *pos)
> +{
> +	struct arm_vmregion_head *h = m->private;
> +	spin_lock_irq(&h->vm_lock);
> +	return seq_list_start(&h->vm_list, *pos);
> +}
> +
> +static void *arm_vmregion_next(struct seq_file *m, void *p, loff_t *pos)
> +{
> +	struct arm_vmregion_head *h = m->private;
> +	return seq_list_next(p, &h->vm_list, pos);
> +}
> +
> +static void arm_vmregion_stop(struct seq_file *m, void *p)
> +{
> +	struct arm_vmregion_head *h = m->private;
> +	spin_unlock_irq(&h->vm_lock);
> +}
> +
> +static const struct seq_operations arm_vmregion_ops = {
> +	.start	= arm_vmregion_start,
> +	.stop	= arm_vmregion_stop,
> +	.next	= arm_vmregion_next,
> +	.show	= arm_vmregion_show,
> +};
> +
> +static int arm_vmregion_open(struct inode *inode, struct file *file)
> +{
> +	struct arm_vmregion_head *h = PDE(inode)->data;
> +	int ret = seq_open(file, &arm_vmregion_ops);
> +	if (!ret) {
> +		struct seq_file *m = file->private_data;
> +		m->private = h;
> +	}
> +	return ret;
> +}
> +
> +static const struct file_operations arm_vmregion_fops = {
> +	.open	= arm_vmregion_open,
> +	.read	= seq_read,
> +	.llseek	= seq_lseek,
> +	.release = seq_release,
> +};
> +
> +int arm_vmregion_create_proc(const char *path, struct arm_vmregion_head *h)
> +{
> +	proc_create_data(path, S_IRUSR, NULL, &arm_vmregion_fops, h);
> +	return 0;
> +}
> +#else
> +int arm_vmregion_create_proc(const char *path, struct arm_vmregion_head *h)
> +{
> +	return 0;
> +}
> +#endif
> diff --git a/arch/arm/mm/vmregion.h b/arch/arm/mm/vmregion.h
> index 15e9f04..162be66 100644
> --- a/arch/arm/mm/vmregion.h
> +++ b/arch/arm/mm/vmregion.h
> @@ -19,11 +19,14 @@ struct arm_vmregion {
>  	unsigned long		vm_end;
>  	struct page		*vm_pages;
>  	int			vm_active;
> +	const void		*caller;
>  };
>  
> -struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, size_t, gfp_t);
> +struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, size_t, gfp_t, const void *);
>  struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *, unsigned long);
>  struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *, unsigned long);
>  void arm_vmregion_free(struct arm_vmregion_head *, struct arm_vmregion *);
>  
> +int arm_vmregion_create_proc(const char *, struct arm_vmregion_head *);
> +
>  #endif
> -- 
> 1.7.4.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 



More information about the linux-arm-kernel mailing list