Commit 1a8db668 authored by Hiroshi DOYU's avatar Hiroshi DOYU Committed by Tony Lindgren

Add kfifo_get_to_user to copy data to userland directly

Signed-off-by: default avatarHiroshi DOYU <Hiroshi.DOYU@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent a5d546c0
...@@ -43,6 +43,9 @@ extern unsigned int __kfifo_put(struct kfifo *fifo, ...@@ -43,6 +43,9 @@ extern unsigned int __kfifo_put(struct kfifo *fifo,
unsigned char *buffer, unsigned int len); unsigned char *buffer, unsigned int len);
extern unsigned int __kfifo_get(struct kfifo *fifo, extern unsigned int __kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len); unsigned char *buffer, unsigned int len);
extern unsigned int __kfifo_get_to_user(struct kfifo *fifo,
unsigned char __user *buffer,
unsigned int len);
/** /**
* __kfifo_reset - removes the entire FIFO contents, no locking version * __kfifo_reset - removes the entire FIFO contents, no locking version
...@@ -151,6 +154,38 @@ static inline unsigned int kfifo_len(struct kfifo *fifo) ...@@ -151,6 +154,38 @@ static inline unsigned int kfifo_len(struct kfifo *fifo)
return ret; return ret;
} }
/**
* kfifo_get_to_user - gets some data from the FIFO
* @fifo: the fifo to be used.
* @buffer: where the data must be copied. user buffer
* @len: the size of the destination buffer.
*
* This function copies at most @len bytes from the FIFO into the
* user @buffer and returns the number of copied bytes.
*/
static inline unsigned int kfifo_get_to_user(struct kfifo *fifo,
unsigned char __user *buffer,
unsigned int len)
{
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(fifo->lock, flags);
ret = __kfifo_get_to_user(fifo, buffer, len);
/*
* optimization: if the FIFO is empty, set the indices to 0
* so we don't wrap the next time
*/
if (fifo->in == fifo->out)
fifo->in = fifo->out = 0;
spin_unlock_irqrestore(fifo->lock, flags);
return ret;
}
#else #else
#warning "don't include kernel headers in userspace" #warning "don't include kernel headers in userspace"
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/uaccess.h>
/** /**
* kfifo_init - allocates a new FIFO using a preallocated buffer * kfifo_init - allocates a new FIFO using a preallocated buffer
...@@ -195,3 +196,59 @@ unsigned int __kfifo_get(struct kfifo *fifo, ...@@ -195,3 +196,59 @@ unsigned int __kfifo_get(struct kfifo *fifo,
return len; return len;
} }
EXPORT_SYMBOL(__kfifo_get); EXPORT_SYMBOL(__kfifo_get);
/**
* __kfifo_get_to_user - gets some data from the FIFO, no locking version
* @fifo: the fifo to be used.
* @buffer: where the data must be copied. user buffer.
* @len: the size of the destination buffer.
*
* This function copies at most @len bytes from the FIFO into the
* user @buffer and returns the number of copied bytes.
*
* 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_to_user(struct kfifo *fifo,
unsigned char __user *buffer,
unsigned int len)
{
unsigned int n1, n2;
int ret;
len = min(len, fifo->in - fifo->out);
/*
* Ensure that we sample the fifo->in index -before- we
* start removing bytes from the kfifo.
*/
smp_rmb();
/* first get the data from fifo->out until the end of the buffer */
n1 = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
n2 = len -n1;
ret = copy_to_user(buffer,
fifo->buffer + (fifo->out & (fifo->size - 1)), n1);
if (ret) {
len = n1 - ret;
goto out;
}
/* then get the rest (if any) from the beginning of the buffer */
ret = copy_to_user(buffer + n1, fifo->buffer, n2);
if (ret)
len = n1 + n2 - ret;
/*
* Ensure that we remove the bytes from the kfifo -before-
* we update the fifo->out index.
*/
out:
smp_mb();
fifo->out += len;
return len;
}
EXPORT_SYMBOL(__kfifo_get_to_user);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment