2005-04-17 02:20:36 +04:00
/*
* A simple kernel FIFO implementation .
*
* Copyright ( C ) 2004 Stelian Pop < stelian @ popies . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/kfifo.h>
2007-07-16 10:41:34 +04:00
# include <linux/log2.h>
2005-04-17 02:20:36 +04:00
/**
* kfifo_init - allocates a new FIFO using a preallocated buffer
* @ buffer : the preallocated buffer to be used .
* @ size : the size of the internal buffer , this have to be a power of 2.
* @ gfp_mask : get_free_pages mask , passed to kmalloc ( )
* @ lock : the lock to be used to protect the fifo buffer
*
2007-02-10 12:45:59 +03:00
* Do NOT pass the kfifo to kfifo_free ( ) after use ! Simply free the
* & struct kfifo with kfree ( ) .
2005-04-17 02:20:36 +04:00
*/
struct kfifo * kfifo_init ( unsigned char * buffer , unsigned int size ,
2005-10-07 10:46:04 +04:00
gfp_t gfp_mask , spinlock_t * lock )
2005-04-17 02:20:36 +04:00
{
struct kfifo * fifo ;
/* size must be a power of 2 */
2007-07-16 10:41:34 +04:00
BUG_ON ( ! is_power_of_2 ( size ) ) ;
2005-04-17 02:20:36 +04:00
fifo = kmalloc ( sizeof ( struct kfifo ) , gfp_mask ) ;
if ( ! fifo )
return ERR_PTR ( - ENOMEM ) ;
fifo - > buffer = buffer ;
fifo - > size = size ;
fifo - > in = fifo - > out = 0 ;
fifo - > lock = lock ;
return fifo ;
}
EXPORT_SYMBOL ( kfifo_init ) ;
/**
* kfifo_alloc - allocates a new FIFO and its internal buffer
* @ size : the size of the internal buffer to be allocated .
* @ gfp_mask : get_free_pages mask , passed to kmalloc ( )
* @ lock : the lock to be used to protect the fifo buffer
*
* The size will be rounded - up to a power of 2.
*/
2005-10-07 10:46:04 +04:00
struct kfifo * kfifo_alloc ( unsigned int size , gfp_t gfp_mask , spinlock_t * lock )
2005-04-17 02:20:36 +04:00
{
unsigned char * buffer ;
struct kfifo * ret ;
/*
* round up to the next power of 2 , since our ' let the indices
* wrap ' tachnique works only in this case .
*/
if ( size & ( size - 1 ) ) {
BUG_ON ( size > 0x80000000 ) ;
size = roundup_pow_of_two ( size ) ;
}
buffer = kmalloc ( size , gfp_mask ) ;
if ( ! buffer )
return ERR_PTR ( - ENOMEM ) ;
ret = kfifo_init ( buffer , size , gfp_mask , lock ) ;
if ( IS_ERR ( ret ) )
kfree ( buffer ) ;
return ret ;
}
EXPORT_SYMBOL ( kfifo_alloc ) ;
/**
* kfifo_free - frees the FIFO
* @ fifo : the fifo to be freed .
*/
void kfifo_free ( struct kfifo * fifo )
{
kfree ( fifo - > buffer ) ;
kfree ( fifo ) ;
}
EXPORT_SYMBOL ( kfifo_free ) ;
/**
* __kfifo_put - puts some data into the FIFO , no locking version
* @ fifo : the fifo to be used .
* @ buffer : the data to be added .
* @ len : the length of the data to be added .
*
2007-02-10 12:45:59 +03:00
* This function copies at most @ len bytes from the @ buffer into
2005-04-17 02:20:36 +04:00
* the FIFO depending on the free space , and returns the number of
* bytes copied .
*
* Note that with only one concurrent reader and one concurrent
* writer , you don ' t need extra locking to use these functions .
*/
unsigned int __kfifo_put ( struct kfifo * fifo ,
unsigned char * buffer , unsigned int len )
{
unsigned int l ;
len = min ( len , fifo - > size - fifo - > in + fifo - > out ) ;
2006-09-29 13:00:11 +04:00
/*
* Ensure that we sample the fifo - > out index - before - we
* start putting bytes into the kfifo .
*/
smp_mb ( ) ;
2005-04-17 02:20:36 +04:00
/* first put the data starting from fifo->in to buffer end */
l = min ( len , fifo - > size - ( fifo - > in & ( fifo - > size - 1 ) ) ) ;
memcpy ( fifo - > buffer + ( fifo - > in & ( fifo - > size - 1 ) ) , buffer , l ) ;
/* then put the rest (if any) at the beginning of the buffer */
memcpy ( fifo - > buffer , buffer + l , len - l ) ;
2006-09-29 13:00:11 +04:00
/*
* Ensure that we add the bytes to the kfifo - before -
* we update the fifo - > in index .
*/
smp_wmb ( ) ;
2005-04-17 02:20:36 +04:00
fifo - > in + = len ;
return len ;
}
EXPORT_SYMBOL ( __kfifo_put ) ;
/**
* __kfifo_get - gets some data from the FIFO , no locking version
* @ fifo : the fifo to be used .
* @ buffer : where the data must be copied .
* @ len : the size of the destination buffer .
*
2007-02-10 12:45:59 +03:00
* This function copies at most @ len bytes from the FIFO into the
* @ buffer and returns the number of copied bytes .
2005-04-17 02:20:36 +04:00
*
* Note that with only one concurrent reader and one concurrent
* writer , you don ' t need extra locking to use these functions .
*/
unsigned int __kfifo_get ( struct kfifo * fifo ,
unsigned char * buffer , unsigned int len )
{
unsigned int l ;
len = min ( len , fifo - > in - fifo - > out ) ;
2006-09-29 13:00:11 +04:00
/*
* Ensure that we sample the fifo - > in index - before - we
* start removing bytes from the kfifo .
*/
smp_rmb ( ) ;
2005-04-17 02:20:36 +04:00
/* first get the data from fifo->out until the end of the buffer */
l = min ( len , fifo - > size - ( fifo - > out & ( fifo - > size - 1 ) ) ) ;
memcpy ( buffer , fifo - > buffer + ( fifo - > out & ( fifo - > size - 1 ) ) , l ) ;
/* then get the rest (if any) from the beginning of the buffer */
memcpy ( buffer + l , fifo - > buffer , len - l ) ;
2006-09-29 13:00:11 +04:00
/*
* Ensure that we remove the bytes from the kfifo - before -
* we update the fifo - > out index .
*/
smp_mb ( ) ;
2005-04-17 02:20:36 +04:00
fifo - > out + = len ;
return len ;
}
EXPORT_SYMBOL ( __kfifo_get ) ;