From 1a8db66853a9020e1632dfa9fc2410b64418ea92 Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU Date: Fri, 10 Aug 2007 02:17:43 -0700 Subject: [PATCH] Add kfifo_get_to_user to copy data to userland directly Signed-off-by: Hiroshi DOYU Signed-off-by: Tony Lindgren --- include/linux/kfifo.h | 35 ++++++++++++++++++++++++++ kernel/kfifo.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 404f4464cb1..2dd610d6fdd 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -43,6 +43,9 @@ extern unsigned int __kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len); extern unsigned int __kfifo_get(struct kfifo *fifo, 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 @@ -151,6 +154,38 @@ static inline unsigned int kfifo_len(struct kfifo *fifo) 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 #warning "don't include kernel headers in userspace" #endif /* __KERNEL__ */ diff --git a/kernel/kfifo.c b/kernel/kfifo.c index bc41ad0f24f..6d3e9999cf7 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c @@ -25,6 +25,7 @@ #include #include #include +#include /** * kfifo_init - allocates a new FIFO using a preallocated buffer @@ -195,3 +196,59 @@ unsigned int __kfifo_get(struct kfifo *fifo, return len; } 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); -- 2.41.0