2005-04-17 02:20:36 +04:00
/*
2010-08-11 05:03:38 +04:00
* A generic kernel FIFO implementation
2005-04-17 02:20:36 +04:00
*
2010-08-11 05:03:38 +04:00
* Copyright ( C ) 2009 / 2010 Stefani Seibold < stefani @ seibold . net >
2005-04-17 02:20:36 +04:00
*
* 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
2010-08-12 01:17:27 +04:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2005-04-17 02:20:36 +04:00
* 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>
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/err.h>
2007-07-16 10:41:34 +04:00
# include <linux/log2.h>
2009-12-22 01:37:31 +03:00
# include <linux/uaccess.h>
2010-08-11 05:03:38 +04:00
# include <linux/kfifo.h>
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
/*
* internal helper to calculate the unused elements in a fifo
2005-04-17 02:20:36 +04:00
*/
2010-08-11 05:03:38 +04:00
static inline unsigned int kfifo_unused ( struct __kfifo * fifo )
2005-04-17 02:20:36 +04:00
{
2010-08-11 05:03:38 +04:00
return ( fifo - > mask + 1 ) - ( fifo - > in - fifo - > out ) ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
int __kfifo_alloc ( struct __kfifo * fifo , unsigned int size ,
size_t esize , gfp_t gfp_mask )
2005-04-17 02:20:36 +04:00
{
/*
2018-03-26 21:18:23 +03:00
* round up to the next power of 2 , since our ' let the indices
2009-06-17 02:33:34 +04:00
* wrap ' technique works only in this case .
2005-04-17 02:20:36 +04:00
*/
2013-02-28 05:05:51 +04:00
size = roundup_pow_of_two ( size ) ;
2010-08-11 05:03:38 +04:00
fifo - > in = 0 ;
fifo - > out = 0 ;
fifo - > esize = esize ;
if ( size < 2 ) {
fifo - > data = NULL ;
fifo - > mask = 0 ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
fifo - > data = kmalloc ( size * esize , gfp_mask ) ;
if ( ! fifo - > data ) {
fifo - > mask = 0 ;
kfifo: move struct kfifo in place
This is a new generic kernel FIFO implementation.
The current kernel fifo API is not very widely used, because it has to
many constrains. Only 17 files in the current 2.6.31-rc5 used it.
FIFO's are like list's a very basic thing and a kfifo API which handles
the most use case would save a lot of development time and memory
resources.
I think this are the reasons why kfifo is not in use:
- The API is to simple, important functions are missing
- A fifo can be only allocated dynamically
- There is a requirement of a spinlock whether you need it or not
- There is no support for data records inside a fifo
So I decided to extend the kfifo in a more generic way without blowing up
the API to much. The new API has the following benefits:
- Generic usage: For kernel internal use and/or device driver.
- Provide an API for the most use case.
- Slim API: The whole API provides 25 functions.
- Linux style habit.
- DECLARE_KFIFO, DEFINE_KFIFO and INIT_KFIFO Macros
- Direct copy_to_user from the fifo and copy_from_user into the fifo.
- The kfifo itself is an in place member of the using data structure, this save an
indirection access and does not waste the kernel allocator.
- Lockless access: if only one reader and one writer is active on the fifo,
which is the common use case, no additional locking is necessary.
- Remove spinlock - give the user the freedom of choice what kind of locking to use if
one is required.
- Ability to handle records. Three type of records are supported:
- Variable length records between 0-255 bytes, with a record size
field of 1 bytes.
- Variable length records between 0-65535 bytes, with a record size
field of 2 bytes.
- Fixed size records, which no record size field.
- Preserve memory resource.
- Performance!
- Easy to use!
This patch:
Since most users want to have the kfifo as part of another object,
reorganize the code to allow including struct kfifo in another data
structure. This requires changing the kfifo_alloc and kfifo_init
prototypes so that we pass an existing kfifo pointer into them. This
patch changes the implementation and all existing users.
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Stefani Seibold <stefani@seibold.net>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-12-22 01:37:26 +03:00
return - ENOMEM ;
}
2010-08-11 05:03:38 +04:00
fifo - > mask = size - 1 ;
2005-04-17 02:20:36 +04:00
kfifo: move struct kfifo in place
This is a new generic kernel FIFO implementation.
The current kernel fifo API is not very widely used, because it has to
many constrains. Only 17 files in the current 2.6.31-rc5 used it.
FIFO's are like list's a very basic thing and a kfifo API which handles
the most use case would save a lot of development time and memory
resources.
I think this are the reasons why kfifo is not in use:
- The API is to simple, important functions are missing
- A fifo can be only allocated dynamically
- There is a requirement of a spinlock whether you need it or not
- There is no support for data records inside a fifo
So I decided to extend the kfifo in a more generic way without blowing up
the API to much. The new API has the following benefits:
- Generic usage: For kernel internal use and/or device driver.
- Provide an API for the most use case.
- Slim API: The whole API provides 25 functions.
- Linux style habit.
- DECLARE_KFIFO, DEFINE_KFIFO and INIT_KFIFO Macros
- Direct copy_to_user from the fifo and copy_from_user into the fifo.
- The kfifo itself is an in place member of the using data structure, this save an
indirection access and does not waste the kernel allocator.
- Lockless access: if only one reader and one writer is active on the fifo,
which is the common use case, no additional locking is necessary.
- Remove spinlock - give the user the freedom of choice what kind of locking to use if
one is required.
- Ability to handle records. Three type of records are supported:
- Variable length records between 0-255 bytes, with a record size
field of 1 bytes.
- Variable length records between 0-65535 bytes, with a record size
field of 2 bytes.
- Fixed size records, which no record size field.
- Preserve memory resource.
- Performance!
- Easy to use!
This patch:
Since most users want to have the kfifo as part of another object,
reorganize the code to allow including struct kfifo in another data
structure. This requires changing the kfifo_alloc and kfifo_init
prototypes so that we pass an existing kfifo pointer into them. This
patch changes the implementation and all existing users.
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Stefani Seibold <stefani@seibold.net>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-12-22 01:37:26 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_alloc ) ;
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
void __kfifo_free ( struct __kfifo * fifo )
2005-04-17 02:20:36 +04:00
{
2010-08-11 05:03:38 +04:00
kfree ( fifo - > data ) ;
fifo - > in = 0 ;
fifo - > out = 0 ;
fifo - > esize = 0 ;
fifo - > data = NULL ;
fifo - > mask = 0 ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_free ) ;
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
int __kfifo_init ( struct __kfifo * fifo , void * buffer ,
unsigned int size , size_t esize )
2009-12-22 01:37:31 +03:00
{
2010-08-11 05:03:38 +04:00
size / = esize ;
2013-02-28 05:05:51 +04:00
size = roundup_pow_of_two ( size ) ;
2010-08-11 05:03:38 +04:00
fifo - > in = 0 ;
fifo - > out = 0 ;
fifo - > esize = esize ;
fifo - > data = buffer ;
if ( size < 2 ) {
fifo - > mask = 0 ;
return - EINVAL ;
2009-12-22 01:37:31 +03:00
}
2010-08-11 05:03:38 +04:00
fifo - > mask = size - 1 ;
return 0 ;
2009-12-22 01:37:31 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_init ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
static void kfifo_copy_in ( struct __kfifo * fifo , const void * src ,
unsigned int len , unsigned int off )
2005-04-17 02:20:36 +04:00
{
2010-08-11 05:03:38 +04:00
unsigned int size = fifo - > mask + 1 ;
unsigned int esize = fifo - > esize ;
2005-04-17 02:20:36 +04:00
unsigned int l ;
2010-08-11 05:03:38 +04:00
off & = fifo - > mask ;
if ( esize ! = 1 ) {
off * = esize ;
size * = esize ;
len * = esize ;
}
l = min ( len , size - off ) ;
memcpy ( fifo - > data + off , src , l ) ;
memcpy ( fifo - > data , src + l , len - l ) ;
2006-09-29 13:00:11 +04:00
/*
2010-08-11 05:03:38 +04:00
* make sure that the data in the fifo is up to date before
* incrementing the fifo - > in index counter
2006-09-29 13:00:11 +04:00
*/
2010-08-11 05:03:38 +04:00
smp_wmb ( ) ;
}
2006-09-29 13:00:11 +04:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_in ( struct __kfifo * fifo ,
const void * buf , unsigned int len )
{
unsigned int l ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
l = kfifo_unused ( fifo ) ;
if ( len > l )
len = l ;
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
kfifo_copy_in ( fifo , buf , len , fifo - > in ) ;
fifo - > in + = len ;
return len ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_in ) ;
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
static void kfifo_copy_out ( struct __kfifo * fifo , void * dst ,
unsigned int len , unsigned int off )
2005-04-17 02:20:36 +04:00
{
2010-08-11 05:03:38 +04:00
unsigned int size = fifo - > mask + 1 ;
unsigned int esize = fifo - > esize ;
2005-04-17 02:20:36 +04:00
unsigned int l ;
2010-08-11 05:03:38 +04:00
off & = fifo - > mask ;
if ( esize ! = 1 ) {
off * = esize ;
size * = esize ;
len * = esize ;
}
l = min ( len , size - off ) ;
memcpy ( dst , fifo - > data + off , l ) ;
memcpy ( dst + l , fifo - > data , len - l ) ;
2006-09-29 13:00:11 +04:00
/*
2010-08-11 05:03:38 +04:00
* make sure that the data is copied before
* incrementing the fifo - > out index counter
2006-09-29 13:00:11 +04:00
*/
2010-08-11 05:03:38 +04:00
smp_wmb ( ) ;
}
2006-09-29 13:00:11 +04:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_out_peek ( struct __kfifo * fifo ,
void * buf , unsigned int len )
{
unsigned int l ;
2006-09-29 13:00:11 +04:00
2010-08-11 05:03:38 +04:00
l = fifo - > in - fifo - > out ;
if ( len > l )
len = l ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
kfifo_copy_out ( fifo , buf , len , fifo - > out ) ;
return len ;
}
EXPORT_SYMBOL ( __kfifo_out_peek ) ;
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_out ( struct __kfifo * fifo ,
void * buf , unsigned int len )
{
len = __kfifo_out_peek ( fifo , buf , len ) ;
fifo - > out + = len ;
return len ;
2009-12-22 01:37:31 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_out ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
static unsigned long kfifo_copy_from_user ( struct __kfifo * fifo ,
const void __user * from , unsigned int len , unsigned int off ,
unsigned int * copied )
2009-12-22 01:37:31 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int size = fifo - > mask + 1 ;
unsigned int esize = fifo - > esize ;
2009-12-22 01:37:31 +03:00
unsigned int l ;
2010-08-11 05:03:38 +04:00
unsigned long ret ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
off & = fifo - > mask ;
if ( esize ! = 1 ) {
off * = esize ;
size * = esize ;
len * = esize ;
}
l = min ( len , size - off ) ;
ret = copy_from_user ( fifo - > data + off , from , l ) ;
if ( unlikely ( ret ) )
ret = DIV_ROUND_UP ( ret + len - l , esize ) ;
else {
ret = copy_from_user ( fifo - > data , from + l , len - l ) ;
if ( unlikely ( ret ) )
ret = DIV_ROUND_UP ( ret , esize ) ;
}
2006-09-29 13:00:11 +04:00
/*
2010-08-11 05:03:38 +04:00
* make sure that the data in the fifo is up to date before
* incrementing the fifo - > in index counter
2006-09-29 13:00:11 +04:00
*/
2010-08-11 05:03:38 +04:00
smp_wmb ( ) ;
2013-11-15 02:32:16 +04:00
* copied = len - ret * esize ;
2010-08-11 05:03:38 +04:00
/* return the number of elements which are not copied */
return ret ;
}
2006-09-29 13:00:11 +04:00
2010-08-11 05:03:38 +04:00
int __kfifo_from_user ( struct __kfifo * fifo , const void __user * from ,
unsigned long len , unsigned int * copied )
{
unsigned int l ;
unsigned long ret ;
unsigned int esize = fifo - > esize ;
int err ;
2006-09-29 13:00:11 +04:00
2010-08-11 05:03:38 +04:00
if ( esize ! = 1 )
len / = esize ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
l = kfifo_unused ( fifo ) ;
if ( len > l )
len = l ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
ret = kfifo_copy_from_user ( fifo , from , len , fifo - > in , copied ) ;
if ( unlikely ( ret ) ) {
len - = ret ;
err = - EFAULT ;
} else
err = 0 ;
fifo - > in + = len ;
return err ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_from_user ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
static unsigned long kfifo_copy_to_user ( struct __kfifo * fifo , void __user * to ,
unsigned int len , unsigned int off , unsigned int * copied )
2009-12-22 01:37:32 +03:00
{
unsigned int l ;
2010-08-11 05:03:38 +04:00
unsigned long ret ;
unsigned int size = fifo - > mask + 1 ;
unsigned int esize = fifo - > esize ;
off & = fifo - > mask ;
if ( esize ! = 1 ) {
off * = esize ;
size * = esize ;
len * = esize ;
}
l = min ( len , size - off ) ;
ret = copy_to_user ( to , fifo - > data + off , l ) ;
if ( unlikely ( ret ) )
ret = DIV_ROUND_UP ( ret + len - l , esize ) ;
else {
ret = copy_to_user ( to + l , fifo - > data , len - l ) ;
if ( unlikely ( ret ) )
ret = DIV_ROUND_UP ( ret , esize ) ;
}
2009-12-22 01:37:32 +03:00
/*
2010-08-11 05:03:38 +04:00
* make sure that the data is copied before
* incrementing the fifo - > out index counter
2009-12-22 01:37:32 +03:00
*/
2010-08-11 05:03:38 +04:00
smp_wmb ( ) ;
2013-11-15 02:32:16 +04:00
* copied = len - ret * esize ;
2010-08-11 05:03:38 +04:00
/* return the number of elements which are not copied */
return ret ;
}
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
int __kfifo_to_user ( struct __kfifo * fifo , void __user * to ,
unsigned long len , unsigned int * copied )
{
unsigned int l ;
unsigned long ret ;
unsigned int esize = fifo - > esize ;
int err ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
if ( esize ! = 1 )
len / = esize ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
l = fifo - > in - fifo - > out ;
if ( len > l )
len = l ;
ret = kfifo_copy_to_user ( fifo , to , len , fifo - > out , copied ) ;
2010-01-16 04:01:15 +03:00
if ( unlikely ( ret ) ) {
2010-08-11 05:03:38 +04:00
len - = ret ;
err = - EFAULT ;
} else
err = 0 ;
fifo - > out + = len ;
return err ;
}
EXPORT_SYMBOL ( __kfifo_to_user ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
static int setup_sgl_buf ( struct scatterlist * sgl , void * buf ,
int nents , unsigned int len )
{
int n ;
unsigned int l ;
unsigned int off ;
struct page * page ;
if ( ! nents )
return 0 ;
if ( ! len )
return 0 ;
n = 0 ;
page = virt_to_page ( buf ) ;
off = offset_in_page ( buf ) ;
l = 0 ;
while ( len > = l + PAGE_SIZE - off ) {
struct page * npage ;
l + = PAGE_SIZE ;
buf + = PAGE_SIZE ;
npage = virt_to_page ( buf ) ;
if ( page_to_phys ( page ) ! = page_to_phys ( npage ) - l ) {
2010-08-12 01:17:27 +04:00
sg_set_page ( sgl , page , l - off , off ) ;
sgl = sg_next ( sgl ) ;
if ( + + n = = nents | | sgl = = NULL )
2010-08-11 05:03:38 +04:00
return n ;
page = npage ;
len - = l - off ;
l = off = 0 ;
}
2010-01-16 04:01:15 +03:00
}
2010-08-12 01:17:27 +04:00
sg_set_page ( sgl , page , len , off ) ;
2010-08-11 05:03:38 +04:00
return n + 1 ;
2009-12-22 01:37:32 +03:00
}
2005-04-17 02:20:36 +04:00
2010-08-11 05:03:38 +04:00
static unsigned int setup_sgl ( struct __kfifo * fifo , struct scatterlist * sgl ,
int nents , unsigned int len , unsigned int off )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int size = fifo - > mask + 1 ;
unsigned int esize = fifo - > esize ;
unsigned int l ;
unsigned int n ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
off & = fifo - > mask ;
if ( esize ! = 1 ) {
off * = esize ;
size * = esize ;
len * = esize ;
}
l = min ( len , size - off ) ;
2010-08-12 01:17:27 +04:00
n = setup_sgl_buf ( sgl , fifo - > data + off , nents , l ) ;
2010-08-11 05:03:38 +04:00
n + = setup_sgl_buf ( sgl + n , fifo - > data , nents - n , len - l ) ;
return n ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_dma_in_prepare ( struct __kfifo * fifo ,
struct scatterlist * sgl , int nents , unsigned int len )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int l ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
l = kfifo_unused ( fifo ) ;
if ( len > l )
len = l ;
return setup_sgl ( fifo , sgl , nents , len , fifo - > in ) ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_dma_in_prepare ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_dma_out_prepare ( struct __kfifo * fifo ,
struct scatterlist * sgl , int nents , unsigned int len )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int l ;
l = fifo - > in - fifo - > out ;
if ( len > l )
len = l ;
return setup_sgl ( fifo , sgl , nents , len , fifo - > out ) ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_dma_out_prepare ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_max_r ( unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int max = ( 1 < < ( recsize < < 3 ) ) - 1 ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
if ( len > max )
return max ;
return len ;
2009-12-22 01:37:32 +03:00
}
2012-04-11 12:01:56 +04:00
EXPORT_SYMBOL ( __kfifo_max_r ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
# define __KFIFO_PEEK(data, out, mask) \
( ( data ) [ ( out ) & ( mask ) ] )
/*
* __kfifo_peek_n internal helper function for determinate the length of
* the next record in the fifo
2009-12-22 01:37:31 +03:00
*/
2010-08-11 05:03:38 +04:00
static unsigned int __kfifo_peek_n ( struct __kfifo * fifo , size_t recsize )
2009-12-22 01:37:31 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int l ;
unsigned int mask = fifo - > mask ;
unsigned char * data = fifo - > data ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
l = __KFIFO_PEEK ( data , fifo - > out , mask ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
if ( - - recsize )
l | = __KFIFO_PEEK ( data , fifo - > out + 1 , mask ) < < 8 ;
return l ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
# define __KFIFO_POKE(data, in, mask, val) \
( \
( data ) [ ( in ) & ( mask ) ] = ( unsigned char ) ( val ) \
)
/*
* __kfifo_poke_n internal helper function for storeing the length of
* the record into the fifo
2010-01-16 04:01:16 +03:00
*/
2010-08-11 05:03:38 +04:00
static void __kfifo_poke_n ( struct __kfifo * fifo , unsigned int n , size_t recsize )
2010-01-16 04:01:16 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int mask = fifo - > mask ;
unsigned char * data = fifo - > data ;
2010-01-16 04:01:16 +03:00
2010-08-11 05:03:38 +04:00
__KFIFO_POKE ( data , fifo - > in , mask , n ) ;
if ( recsize > 1 )
__KFIFO_POKE ( data , fifo - > in + 1 , mask , n > > 8 ) ;
2010-01-16 04:01:16 +03:00
}
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_len_r ( struct __kfifo * fifo , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
return __kfifo_peek_n ( fifo , recsize ) ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_len_r ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_in_r ( struct __kfifo * fifo , const void * buf ,
unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
if ( len + recsize > kfifo_unused ( fifo ) )
return 0 ;
2010-01-16 04:01:15 +03:00
2010-08-11 05:03:38 +04:00
__kfifo_poke_n ( fifo , len , recsize ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
kfifo_copy_in ( fifo , buf , len , fifo - > in + recsize ) ;
fifo - > in + = len + recsize ;
return len ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_in_r ) ;
static unsigned int kfifo_out_copy_r ( struct __kfifo * fifo ,
void * buf , unsigned int len , size_t recsize , unsigned int * n )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
* n = __kfifo_peek_n ( fifo , recsize ) ;
if ( len > * n )
len = * n ;
kfifo_copy_out ( fifo , buf , len , fifo - > out + recsize ) ;
return len ;
}
unsigned int __kfifo_out_peek_r ( struct __kfifo * fifo , void * buf ,
unsigned int len , size_t recsize )
{
unsigned int n ;
if ( fifo - > in = = fifo - > out )
return 0 ;
return kfifo_out_copy_r ( fifo , buf , len , recsize , & n ) ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_out_peek_r ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_out_r ( struct __kfifo * fifo , void * buf ,
unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned int n ;
if ( fifo - > in = = fifo - > out )
return 0 ;
len = kfifo_out_copy_r ( fifo , buf , len , recsize , & n ) ;
fifo - > out + = n + recsize ;
return len ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_out_r ) ;
2009-12-22 01:37:31 +03:00
2010-08-20 01:13:27 +04:00
void __kfifo_skip_r ( struct __kfifo * fifo , size_t recsize )
{
unsigned int n ;
n = __kfifo_peek_n ( fifo , recsize ) ;
fifo - > out + = n + recsize ;
}
EXPORT_SYMBOL ( __kfifo_skip_r ) ;
2010-08-11 05:03:38 +04:00
int __kfifo_from_user_r ( struct __kfifo * fifo , const void __user * from ,
unsigned long len , unsigned int * copied , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned long ret ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
len = __kfifo_max_r ( len , recsize ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
if ( len + recsize > kfifo_unused ( fifo ) ) {
* copied = 0 ;
return 0 ;
}
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
__kfifo_poke_n ( fifo , len , recsize ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
ret = kfifo_copy_from_user ( fifo , from , len , fifo - > in + recsize , copied ) ;
if ( unlikely ( ret ) ) {
* copied = 0 ;
return - EFAULT ;
}
fifo - > in + = len + recsize ;
return 0 ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_from_user_r ) ;
int __kfifo_to_user_r ( struct __kfifo * fifo , void __user * to ,
unsigned long len , unsigned int * copied , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
unsigned long ret ;
unsigned int n ;
if ( fifo - > in = = fifo - > out ) {
* copied = 0 ;
return 0 ;
}
n = __kfifo_peek_n ( fifo , recsize ) ;
if ( len > n )
len = n ;
ret = kfifo_copy_to_user ( fifo , to , len , fifo - > out + recsize , copied ) ;
if ( unlikely ( ret ) ) {
* copied = 0 ;
return - EFAULT ;
}
fifo - > out + = n + recsize ;
return 0 ;
2009-12-22 01:37:31 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_to_user_r ) ;
2009-12-22 01:37:31 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_dma_in_prepare_r ( struct __kfifo * fifo ,
struct scatterlist * sgl , int nents , unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2014-08-09 01:22:44 +04:00
BUG_ON ( ! nents ) ;
2010-08-11 05:03:38 +04:00
len = __kfifo_max_r ( len , recsize ) ;
if ( len + recsize > kfifo_unused ( fifo ) )
return 0 ;
return setup_sgl ( fifo , sgl , nents , len , fifo - > in + recsize ) ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_dma_in_prepare_r ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
void __kfifo_dma_in_finish_r ( struct __kfifo * fifo ,
unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2010-08-11 05:03:38 +04:00
len = __kfifo_max_r ( len , recsize ) ;
__kfifo_poke_n ( fifo , len , recsize ) ;
fifo - > in + = len + recsize ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_dma_in_finish_r ) ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
unsigned int __kfifo_dma_out_prepare_r ( struct __kfifo * fifo ,
struct scatterlist * sgl , int nents , unsigned int len , size_t recsize )
2009-12-22 01:37:32 +03:00
{
2014-08-09 01:22:44 +04:00
BUG_ON ( ! nents ) ;
2010-08-11 05:03:38 +04:00
len = __kfifo_max_r ( len , recsize ) ;
if ( len + recsize > fifo - > in - fifo - > out )
return 0 ;
return setup_sgl ( fifo , sgl , nents , len , fifo - > out + recsize ) ;
2009-12-22 01:37:32 +03:00
}
2010-08-11 05:03:38 +04:00
EXPORT_SYMBOL ( __kfifo_dma_out_prepare_r ) ;
void __kfifo_dma_out_finish_r ( struct __kfifo * fifo , size_t recsize )
{
unsigned int len ;
2009-12-22 01:37:32 +03:00
2010-08-11 05:03:38 +04:00
len = __kfifo_peek_n ( fifo , recsize ) ;
fifo - > out + = len + recsize ;
}
EXPORT_SYMBOL ( __kfifo_dma_out_finish_r ) ;