for the i.MX6 processor family.
aboutsummaryrefslogblamecommitdiffstats
path: root/kernel/relay.c
blob: 61134eb7a0c8a9b2d2e0557b33ff4fbd7ff96f31 (plain) (tree)
1
2
3
4
5
6
7
8
9


                                                                       
                                                           




                                                                       

                                                           










                                       
                      
                         



                                            





















































                                                                      
                                                                            





















                                                           
                                                                             
                                                                  
   
                                                                 



                                   

                                      








                                                                              
                                                                         




                                                                  
                              











                                                                   
                                
  
                                                              
   
                                                             
 
                                                                              






                                                                               
                                                             














                                                       
                                                                      


                                              
                                                    








                                                                             
                                                    









                                                        
                                   






                                                     
                                                                     




                                                                            
                                               











                                                                           
                                                 


                                                                       



















































































                                                                             
                                          
  
                                                               
   
                                              
 
                                                         







                                                       
                                                    
   
                                                                   





                                                     


                                                                             





















                                                                        
                                                                  




                                                      



                       
                                              

                                               
         





                                                       


                               
  

                                                          
                                             
   
                                                                             
 
                                     
                              
                      
 
                            

                                    




                                                                      

                                     



                               

                               



                                                                          

                             
 








                                   
                   


                       










                                                                           
                                                  

                           
                                    


                                               
                                               




















                                                                       




                                                    
                                                                               









                                                                     
                                   















                                                                               
                             







                                                                  

                                                    
                                                                     


                                         
                                        
  
                                                               



                                                                 
                                     




                                                   

                                                    


                           





                                        
                                                         






                                                             


                                                              


                                  
                                          
                                
                                                       

                                       
         

                                               
 


                    
                                


                                              
         
 
                                                     
                                            


























                                                                          


                                                                        







                                                                    

































                                                                               
                                                                            





























                                                              



                       






                                                              





                                                                          
                              
                                                     
                                            






                                       
                                                               



                                    



                       


                                                     
         





                                                             











                                                                  
                                                 










                                                      
                                                                    














































                                                                         
  















                                                                          



                                                                








                                                                             
  



                                                                              

                                                    

                                                
 
                                                  
 



                                                  

         
                                                         
                                                    
                                                 
                                        
         
 




                                                                              
 
                                 

                         




                                                                           

                                     
























                                                                             

                                     
  
                                                                








                                                                
                                                            
 

                                                                        













                                                                  


                                          




















                                                                    
  
                                                                 
   




                                                     
 
                   
                    

                                       
                    
                                                       

                                      
         



                              


                   





                                                       
  

                                                                              



                                                                       
 

                                                   
                
 
                         

                         
                                                           
            





                                                                      
                              
 


                                                                        





                                                                              
                                     
                                                             
 
                             






                                                   






                                                                     

 









                                                                           

                                                                
 


                                                           
                                                





                                                        
                                            




                                          
  








                                                                     
                                                              

                                                           


                                                                


                                                                              








                                                  



                                                             




                                                             




                                                            


                                                                          
 
                         
                              
 

                                                                       
 

                                                                 
 



                                                        
                 

                                                            
 



                                                 
 

                                             

                              

         

                          
 


                                                       
 



                                           



















                                                                                   




















                                                        

 
                                                      





                                             
                                                 

                                         








                                                  
/*
 * Public API and common code for kernel->userspace relay file support.
 *
 * See Documentation/filesystems/relay.txt for an overview.
 *
 * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
 * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
 *
 * Moved to kernel/relay.c by Paul Mundt, 2006.
 * November 2006 - CPU hotplug support by Mathieu Desnoyers
 * 	(mathieu.desnoyers@polymtl.ca)
 *
 * This file is released under the GPL.
 */
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/relay.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/cpu.h>
#include <linux/splice.h>

/* list of open channels, for cpu hotplug */
static DEFINE_MUTEX(relay_channels_mutex);
static LIST_HEAD(relay_channels);

/*
 * close() vm_op implementation for relay file mapping.
 */
static void relay_file_mmap_close(struct vm_area_struct *vma)
{
	struct rchan_buf *buf = vma->vm_private_data;
	buf->chan->cb->buf_unmapped(buf, vma->vm_file);
}

/*
 * nopage() vm_op implementation for relay file mapping.
 */
static struct page *relay_buf_nopage(struct vm_area_struct *vma,
				     unsigned long address,
				     int *type)
{
	struct page *page;
	struct rchan_buf *buf = vma->vm_private_data;
	unsigned long offset = address - vma->vm_start;

	if (address > vma->vm_end)
		return NOPAGE_SIGBUS; /* Disallow mremap */
	if (!buf)
		return NOPAGE_OOM;

	page = vmalloc_to_page(buf->start + offset);
	if (!page)
		return NOPAGE_OOM;
	get_page(page);

	if (type)
		*type = VM_FAULT_MINOR;

	return page;
}

/*
 * vm_ops for relay file mappings.
 */
static struct vm_operations_struct relay_file_mmap_ops = {
	.nopage = relay_buf_nopage,
	.close = relay_file_mmap_close,
};

/**
 *	relay_mmap_buf: - mmap channel buffer to process address space
 *	@buf: relay channel buffer
 *	@vma: vm_area_struct describing memory to be mapped
 *
 *	Returns 0 if ok, negative on error
 *
 *	Caller should already have grabbed mmap_sem.
 */
static int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
{
	unsigned long length = vma->vm_end - vma->vm_start;
	struct file *filp = vma->vm_file;

	if (!buf)
		return -EBADF;

	if (length != (unsigned long)buf->chan->alloc_size)
		return -EINVAL;

	vma->vm_ops = &relay_file_mmap_ops;
	vma->vm_private_data = buf;
	buf->chan->cb->buf_mapped(buf, filp);

	return 0;
}

/**
 *	relay_alloc_buf - allocate a channel buffer
 *	@buf: the buffer struct
 *	@size: total size of the buffer
 *
 *	Returns a pointer to the resulting buffer, %NULL if unsuccessful. The
 *	passed in size will get page aligned, if it isn't already.
 */
static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size)
{
	void *mem;
	unsigned int i, j, n_pages;

	*size = PAGE_ALIGN(*size);
	n_pages = *size >> PAGE_SHIFT;

	buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL);
	if (!buf->page_array)
		return NULL;

	for (i = 0; i < n_pages; i++) {
		buf->page_array[i] = alloc_page(GFP_KERNEL);
		if (unlikely(!buf->page_array[i]))
			goto depopulate;
		set_page_private(buf->page_array[i], (unsigned long)buf);
	}
	mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL);
	if (!mem)
		goto depopulate;

	memset(mem, 0, *size);
	buf->page_count = n_pages;
	return mem;

depopulate:
	for (j = 0; j < i; j++)
		__free_page(buf->page_array[j]);
	kfree(buf->page_array);
	return NULL;
}

/**
 *	relay_create_buf - allocate and initialize a channel buffer
 *	@chan: the relay channel
 *
 *	Returns channel buffer if successful, %NULL otherwise.
 */
static struct rchan_buf *relay_create_buf(struct rchan *chan)
{
	struct rchan_buf *buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL);
	if (!buf)
		return NULL;

	buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL);
	if (!buf->padding)
		goto free_buf;

	buf->start = relay_alloc_buf(buf, &chan->alloc_size);
	if (!buf->start)
		goto free_buf;

	buf->chan = chan;
	kref_get(&buf->chan->kref);
	return buf;

free_buf:
	kfree(buf->padding);
	kfree(buf);
	return NULL;
}

/**
 *	relay_destroy_channel - free the channel struct
 *	@kref: target kernel reference that contains the relay channel
 *
 *	Should only be called from kref_put().
 */
static void relay_destroy_channel(struct kref *kref)
{
	struct rchan *chan = container_of(kref, struct rchan, kref);
	kfree(chan);
}

/**
 *	relay_destroy_buf - destroy an rchan_buf struct and associated buffer
 *	@buf: the buffer struct
 */
static void relay_destroy_buf(struct rchan_buf *buf)
{
	struct rchan *chan = buf->chan;
	unsigned int i;

	if (likely(buf->start)) {
		vunmap(buf->start);
		for (i = 0; i < buf->page_count; i++)
			__free_page(buf->page_array[i]);
		kfree(buf->page_array);
	}
	chan->buf[buf->cpu] = NULL;
	kfree(buf->padding);
	kfree(buf);
	kref_put(&chan->kref, relay_destroy_channel);
}

/**
 *	relay_remove_buf - remove a channel buffer
 *	@kref: target kernel reference that contains the relay buffer
 *
 *	Removes the file from the fileystem, which also frees the
 *	rchan_buf_struct and the channel buffer.  Should only be called from
 *	kref_put().
 */
static void relay_remove_buf(struct kref *kref)
{
	struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
	buf->chan->cb->remove_buf_file(buf->dentry);
	relay_destroy_buf(buf);
}

/**
 *	relay_buf_empty - boolean, is the channel buffer empty?
 *	@buf: channel buffer
 *
 *	Returns 1 if the buffer is empty, 0 otherwise.
 */
static int relay_buf_empty(struct rchan_buf *buf)
{
	return (buf->subbufs_produced - buf->subbufs_consumed) ? 0 : 1;
}

/**
 *	relay_buf_full - boolean, is the channel buffer full?
 *	@buf: channel buffer
 *
 *	Returns 1 if the buffer is full, 0 otherwise.
 */
int relay_buf_full(struct rchan_buf *buf)
{
	size_t ready = buf->subbufs_produced - buf->subbufs_consumed;
	return (ready >= buf->chan->n_subbufs) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(relay_buf_full);

/*
 * High-level relay kernel API and associated functions.
 */

/*
 * rchan_callback implementations defining default channel behavior.  Used
 * in place of corresponding NULL values in client callback struct.
 */

/*
 * subbuf_start() default callback.  Does nothing.
 */
static int subbuf_start_default_callback (struct rchan_buf *buf,
					  void *subbuf,
					  void *prev_subbuf,
					  size_t prev_padding)
{
	if (relay_buf_full(buf))
		return 0;