X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=kernel%2Fkfifo.c;h=6d3e9999cf763e54e3b27665f7ab086053952771;hb=refs%2Fheads%2Fb2.6.28-omap-h63xx;hp=bc41ad0f24f881845d96d77a00a3d3535968566b;hpb=b91cba52e9b7b3f1c0037908a192d93a869ca9e5;p=linux-2.6-omap-h63xx.git 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);