]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Add kfifo_get_to_user to copy data to userland directly
authorHiroshi DOYU <Hiroshi.DOYU@nokia.com>
Fri, 10 Aug 2007 09:17:43 +0000 (02:17 -0700)
committerTony Lindgren <tony@atomide.com>
Fri, 10 Aug 2007 09:17:43 +0000 (02:17 -0700)
Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
include/linux/kfifo.h
kernel/kfifo.c

index 404f4464cb1aaf255b58c818a865ae4de2e103c7..2dd610d6fdd38fe646aa0585444dff76ee7b6d69 100644 (file)
@@ -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__ */
index bc41ad0f24f881845d96d77a00a3d3535968566b..6d3e9999cf763e54e3b27665f7ab086053952771 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/err.h>
 #include <linux/kfifo.h>
 #include <linux/log2.h>
+#include <linux/uaccess.h>
 
 /**
  * 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);