[PATCH 7/7] new kfifo API

From: Stefani Seibold
Date: Tue Aug 11 2009 - 18:32:39 EST


include/linux/kfifo.h | 381 ++++++++++++++++++++++++++++++++++++++++++++++++--
kernel/kfifo.c | 275 ++++++++++++++++++++++++------------
2 files changed, 551 insertions(+), 105 deletions(-)

Signed-off-by: Stefani Seibold <stefani@xxxxxxxxxxx>

diff -u -N -r linux-2.6.31-rc4-kfifo6/include/linux/kfifo.h linux-2.6.31-rc4-kfifo7/include/linux/kfifo.h
--- linux-2.6.31-rc4-kfifo6/include/linux/kfifo.h 2009-08-11 22:53:41.000000000 +0200
+++ linux-2.6.31-rc4-kfifo7/include/linux/kfifo.h 2009-08-12 00:02:51.000000000 +0200
@@ -60,9 +60,9 @@
unsigned int size, gfp_t gfp_mask);
extern void kfifo_free(struct kfifo *fifo);
extern __must_check unsigned int kfifo_in(struct kfifo *fifo,
- const unsigned char *buffer, unsigned int len);
+ const unsigned char *from, unsigned int len);
extern __must_check unsigned int kfifo_out(struct kfifo *fifo,
- unsigned char *buffer, unsigned int len);
+ unsigned char *to, unsigned int len);

/**
* kfifo_reset - removes the entire FIFO contents
@@ -83,7 +83,7 @@
}

/**
- * kfifo_reset_in - skip output FIFO contents
+ * kfifo_reset_out - skip output FIFO contents
* @fifo: the fifo to be emptied.
*/
static inline void kfifo_reset_out(struct kfifo *fifo)
@@ -92,6 +92,15 @@
}

/**
+ * kfifo_is_empty - returns true if the fifo is empty
+ * @fifo: the fifo to be used.
+ */
+static inline __must_check int kfifo_is_empty(struct kfifo *fifo)
+{
+ return fifo->in == fifo->out;
+}
+
+/**
* kfifo_in_locked - puts some data into the FIFO using a spinlock for locking
* @fifo: the fifo to be used.
* @buffer: the data to be added.
@@ -141,8 +150,8 @@
* 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;
+ if (kfifo_is_empty(fifo))
+ kfifo_reset(fifo);

spin_unlock_irqrestore(lock, flags);

@@ -172,15 +181,6 @@
}

/**
- * kfifo_is_empty - returns true if the fifo is empty
- * @fifo: the fifo to be used.
- */
-static inline __must_check int kfifo_is_empty(struct kfifo *fifo)
-{
- return fifo->in == fifo->out;
-}
-
-/**
* kfifo_is_full - returns true if the fifo is full
* @fifo: the fifo to be used.
*/
@@ -204,4 +204,357 @@
extern __must_check unsigned int kfifo_to_user(struct kfifo *fifo,
void __user *to, unsigned int n);

+/**
+ * __kfifo_add_out internal helper function for updating the out offset
+ */
+static inline void __kfifo_add_out(struct kfifo *fifo,
+ unsigned int off)
+{
+ smp_mb();
+ fifo->out += off;
+}
+
+/**
+ * __kfifo_add_in internal helper function for updating the in offset
+ */
+static inline void __kfifo_add_in(struct kfifo *fifo,
+ unsigned int off)
+{
+ smp_wmb();
+ fifo->in += off;
+}
+
+/**
+ * __kfifo_off internal helper function for calculating the index of a
+ * given offeset
+ */
+static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
+{
+ return off & (fifo->size-1);
+}
+
+/**
+ * __kfifo_peek_n internal helper function for determinate the length of
+ * the next record in the fifo
+ */
+static inline unsigned int __kfifo_peek_n(struct kfifo *fifo,
+ unsigned int recsize)
+{
+#define KFIFO_GET(fifo, off, shift) \
+ ((fifo)->buffer[__kfifo_off((fifo), (fifo)->out+(off))] << (shift))
+
+ unsigned int l;
+
+ l = KFIFO_GET(fifo, 0, 0);
+
+ if (unlikely(--recsize))
+ l |= KFIFO_GET(fifo, 1, 8);
+
+ return l;
+}
+
+/**
+ * __kfifo_poke_n internal helper function for storing the length of
+ * the next record into the fifo
+ */
+static inline void __kfifo_poke_n(struct kfifo *fifo,
+ unsigned int recsize, unsigned int n)
+{
+#define KFIFO_PUT(fifo, off, val, shift) \
+ ( \
+ (fifo)->buffer[__kfifo_off((fifo), (fifo)->in+(off))] = \
+ (unsigned char)((val) >> (shift)) \
+ )
+
+ KFIFO_PUT(fifo, 0, n, 0);
+
+ if (unlikely(--recsize))
+ KFIFO_PUT(fifo, 1, n, 8);
+}
+
+/**
+ * __kfifo_in_... internal functions for put date into the fifo
+ * do not call it directly, use kfifo_in_rec() instead
+ */
+extern unsigned int __kfifo_in_n(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int recsize);
+
+extern unsigned int __kfifo_in_generic(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int recsize);
+
+static inline unsigned int __kfifo_in_rec(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int recsize)
+{
+ unsigned int ret;
+
+ ret = __kfifo_in_n(fifo, from, n, recsize);
+
+ if (ret > n)
+ return ret;
+
+ if (likely(ret == 0)) {
+ if (recsize)
+ __kfifo_poke_n(fifo, recsize, n);
+ __kfifo_add_in(fifo, n + recsize);
+ }
+ return ret;
+}
+
+/**
+ * kfifo_in_rec - puts some record data into the FIFO
+ * @fifo: the fifo to be used.
+ * @from: the data to be added.
+ * @n: the length of the data to be added.
+ * @recsize: size of record field
+ *
+ * This function copies @n bytes from the @from into
+ * the FIFO and returns the number of bytes which cannot be copied.
+ * A returned value greater than the @n value means that the record doesn't
+ * fit into the buffer.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+static inline __must_check unsigned int kfifo_in_rec(struct kfifo *fifo,
+ unsigned char *from, unsigned int n, unsigned int recsize)
+{
+ if (!__builtin_constant_p(recsize))
+ return __kfifo_in_generic(fifo, from, n, recsize);
+
+ return __kfifo_in_rec(fifo, from, n, recsize);
+}
+
+/**
+ * __kfifo_out_... internal functions for get date from the fifo
+ * do not call it directly, use kfifo_out_rec() instead
+ */
+extern unsigned int __kfifo_out_n(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int reclen,
+ unsigned int recsize);
+
+extern unsigned int __kfifo_out_generic(struct kfifo *fifo,
+ unsigned char *to, unsigned int n,
+ unsigned int recsize, unsigned int *total);
+
+static inline unsigned int __kfifo_out_rec(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int recsize,
+ unsigned int *total)
+{
+ unsigned int l;
+
+ if (!recsize) {
+ l = n;
+ if (total)
+ *total = l;
+ } else {
+ l = __kfifo_peek_n(fifo, recsize);
+ if (total)
+ *total = l;
+ if (n < l)
+ return l;
+ }
+
+ return __kfifo_out_n(fifo, to, n, l, recsize);
+}
+
+/**
+ * kfifo_out_rec - gets some record data from the FIFO
+ * @fifo: the fifo to be used.
+ * @to: where the data must be copied.
+ * @n: the size of the destination buffer.
+ * @recsize: size of record field
+ * @total: pointer where the total number of to copied bytes should stored
+ *
+ * This function copies at most @n bytes from the @to into
+ * the FIFO and returns the number of bytes which cannot be copied.
+ * A returned value greater than the @n value means that the record doesn't
+ * fit into the @to buffer.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+static inline __must_check unsigned int kfifo_out_rec(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int recsize,
+ unsigned int *total)
+
+{
+ if (!__builtin_constant_p(recsize))
+ return __kfifo_out_generic(fifo, to, n, recsize, total);
+
+ return __kfifo_out_rec(fifo, to, n, recsize, total);
+}
+
+/**
+ * __kfifo_from_user_... internal functions for transfer from user space into
+ * the fifo. do not call it directly, use kfifo_from_user_rec() instead
+ */
+extern unsigned int __kfifo_from_user_n(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize);
+
+extern unsigned int __kfifo_from_user_generic(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize);
+
+static inline unsigned int __kfifo_from_user_rec(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize)
+{
+ unsigned int ret;
+
+ ret = __kfifo_from_user_n(fifo, from, n, recsize);
+
+ if (ret > n)
+ return ret;
+
+ if (likely(ret == 0)) {
+ if (recsize)
+ __kfifo_poke_n(fifo, recsize, n);
+ __kfifo_add_in(fifo, n + recsize);
+ }
+ return ret;
+}
+
+/**
+ * kfifo_from_user_rec - puts some data from user space into the FIFO
+ * @fifo: the fifo to be used.
+ * @from: pointer to the data to be added.
+ * @n: the length of the data to be added.
+ * @recsize: size of record field
+ *
+ * This function copies @n bytes from the @from into the
+ * FIFO and returns the number of bytes which cannot be copied.
+ *
+ * If the returned value is equal or less the @n value, the copy_from_user()
+ * functions has failed. Otherwise the record doesn't fit into the buffer.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+static inline __must_check unsigned int kfifo_from_user_rec(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize)
+{
+ if (__builtin_constant_p(recsize))
+ return __kfifo_from_user_rec(fifo, from, n, recsize);
+ return __kfifo_from_user_generic(fifo, from, n, recsize);
+}
+
+/**
+ * __kfifo_to_user_... internal functions for transfer fifo data into user space
+ * do not call it directly, use kfifo_to_user_rec() instead
+ */
+extern unsigned int __kfifo_to_user_n(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int reclen,
+ unsigned int recsize);
+
+extern unsigned int __kfifo_to_user_generic(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int recsize,
+ unsigned int *total);
+
+static inline unsigned int __kfifo_to_user_rec(struct kfifo *fifo,
+ void __user *to, unsigned int n,
+ unsigned int recsize, unsigned int *total)
+{
+ unsigned int l;
+
+ if (!recsize) {
+ l = n;
+ if (total)
+ *total = l;
+ } else {
+ l = __kfifo_peek_n(fifo, recsize);
+ if (total)
+ *total = l;
+ if (n < l)
+ return l;
+ }
+
+ return __kfifo_to_user_n(fifo, to, n, l, recsize);
+}
+
+/**
+ * kfifo_to_user_rec - gets data from the FIFO and write it to user space
+ * @fifo: the fifo to be used.
+ * @to: where the data must be copied.
+ * @n: the size of the destination buffer.
+ * @recsize: size of record field
+ * @total: pointer where the total number of to copied bytes should stored
+ *
+ * This function copies at most @n bytes from the FIFO into the
+ * @to.
+ * In case of an error, the function returns the number of bytes which cannot
+ * be copied.
+ * If the returned value is equal or less the @n value, the copy_to_user()
+ * functions has failed. Otherwise the record doesn't fit into the @to buffer.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+static inline __must_check unsigned int kfifo_to_user_rec(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int recsize,
+ unsigned int *total)
+{
+ if (__builtin_constant_p(recsize))
+ return __kfifo_to_user_rec(fifo, to, n, recsize, total);
+ return __kfifo_to_user_generic(fifo, to, n, recsize, total);
+}
+
+/**
+ * __kfifo_peek_... internal functions for peek into the next fifo record
+ * do not call it directly, use kfifo_peek_rec() instead
+ */
+extern unsigned int __kfifo_peek_generic(struct kfifo *fifo,
+ unsigned int recsize);
+
+/**
+ * kfifo_peek_rec - gets the size of the next FIFO record data
+ * @fifo: the fifo to be used.
+ * @recsize: size of record field
+ *
+ * This function returns the size of the next FIFO record in number of bytes
+ */
+static inline __must_check unsigned int kfifo_peek_rec(struct kfifo *fifo,
+ unsigned int recsize)
+{
+ if (__builtin_constant_p(recsize)) {
+ if (!recsize)
+ return kfifo_len(fifo);
+ return __kfifo_peek_n(fifo, recsize);
+ }
+ return __kfifo_peek_generic(fifo, recsize);
+}
+
+/**
+ * __kfifo_skip_... internal functions for peek into the next fifo record
+ * do not call it directly, use kfifo_skip_rec() instead
+ */
+extern unsigned int __kfifo_peek_generic(struct kfifo *fifo,
+ unsigned int recsize);
+
+static inline __must_check void __kfifo_skip_rec(struct kfifo *fifo,
+ unsigned int recsize)
+{
+ if (!recsize)
+ kfifo_reset_out(fifo);
+
+ l = __kfifo_peek_n(fifo, recsize);
+
+ if (l + recsize > kfifo_len())
+ kfifo_reset_out(fifo);
+
+ __kfifo_add_out(fifo, l + recsize);
+}
+
+/**
+ * kfifo_skip_rec - skip the next output record
+ * @fifo: the fifo to be used.
+ * @recsize: size of record field
+ *
+ * This function skips the next FIFO record
+ */
+static inline __must_check unsigned int kfifo_skip_rec(struct kfifo *fifo,
+ unsigned int recsize)
+{
+ if (__builtin_constant_p(recsize))
+ return __kfifo_skip_rec(fifo, recsize);
+ return __kfifo_skip_generic(fifo, recsize);
+}
+
#endif
diff -u -N -r linux-2.6.31-rc4-kfifo6/kernel/kfifo.c linux-2.6.31-rc4-kfifo7/kernel/kfifo.c
--- linux-2.6.31-rc4-kfifo6/kernel/kfifo.c 2009-08-11 22:33:15.000000000 +0200
+++ linux-2.6.31-rc4-kfifo7/kernel/kfifo.c 2009-08-11 23:44:27.000000000 +0200
@@ -99,26 +99,11 @@
}
EXPORT_SYMBOL(kfifo_free);

-/**
- * kfifo_in - puts some data into the FIFO, no locking version
- * @fifo: the fifo to be used.
- * @buffer: the data to be added.
- * @len: the length of the data to be added.
- *
- * This function copies at most @len bytes from the @buffer into
- * the FIFO depending on the free space, and returns the number of
- * bytes copied.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-unsigned int kfifo_in(struct kfifo *fifo,
- const unsigned char *buffer, unsigned int len)
+static inline void __kfifo_in_data(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int off)
{
unsigned int l;

- len = min(len, fifo->size - fifo->in + fifo->out);
-
/*
* Ensure that we sample the fifo->out index -before- we
* start putting bytes into the kfifo.
@@ -126,44 +111,62 @@

smp_mb();

+ off += __kfifo_off(fifo, fifo->in+off);
+
/* first put the data starting from fifo->in to buffer end */
- l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
- memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
+ l = min(n, fifo->size - off);
+ memcpy(fifo->buffer + off, from, l);

/* then put the rest (if any) at the beginning of the buffer */
- memcpy(fifo->buffer, buffer + l, len - l);
-
- /*
- * Ensure that we add the bytes to the kfifo -before-
- * we update the fifo->in index.
- */
-
- smp_wmb();
+ memcpy(fifo->buffer, from + l, n - l);
+}

- fifo->in += len;
+unsigned int __kfifo_in_n(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int recsize)
+{
+ if (kfifo_avail(fifo) < n + recsize)
+ return n + 1;

- return len;
+ __kfifo_in_data(fifo, from, n, recsize);
+ return 0;
}
-EXPORT_SYMBOL(kfifo_in);
+EXPORT_SYMBOL(__kfifo_in_n);

/**
- * kfifo_out - gets some data from the FIFO, no locking version
+ * kfifo_in - puts some data into the FIFO
* @fifo: the fifo to be used.
- * @buffer: where the data must be copied.
- * @len: the size of the destination buffer.
+ * @from: the data to be added.
+ * @n: the length of the data to be added.
*
- * This function copies at most @len bytes from the FIFO into the
- * @buffer and returns the number of copied bytes.
+ * This function copies at most @n bytes from @from into
+ * the FIFO depending on the free space, and returns the number of
+ * bytes copied.
*
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these functions.
*/
-unsigned int kfifo_out(struct kfifo *fifo,
- unsigned char *buffer, unsigned int len)
+unsigned int kfifo_in(struct kfifo *fifo, const unsigned char *from,
+ unsigned int n)
{
- unsigned int l;
+ n = min(kfifo_avail(fifo), n);

- len = min(len, fifo->in - fifo->out);
+ __kfifo_in_data(fifo, from, n, 0);
+ __kfifo_add_in(fifo, n);
+ return n;
+}
+EXPORT_SYMBOL(kfifo_in);
+
+unsigned int __kfifo_in_generic(struct kfifo *fifo,
+ const unsigned char *from, unsigned int n, unsigned int recsize)
+{
+ return __kfifo_in_rec(fifo, from, n, recsize);
+}
+EXPORT_SYMBOL(__kfifo_in_generic);
+
+static inline void __kfifo_out_data(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int off)
+{
+ unsigned int l;

/*
* Ensure that we sample the fifo->in index -before- we
@@ -172,47 +175,66 @@

smp_rmb();

+ off += __kfifo_off(fifo, fifo->out+off);
+
/* first get the data from fifo->out until the end of the buffer */
- l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
- memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
+ l = min(n, fifo->size - off);
+ memcpy(to, fifo->buffer + off, l);

/* then get the rest (if any) from the beginning of the buffer */
- memcpy(buffer + l, fifo->buffer, len - l);
-
- /*
- * Ensure that we remove the bytes from the kfifo -before-
- * we update the fifo->out index.
- */
-
- smp_mb();
+ memcpy(to + l, fifo->buffer, n - l);
+}

- fifo->out += len;
+unsigned int __kfifo_out_n(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int reclen,
+ unsigned int recsize)
+{
+ if (kfifo_len(fifo) < reclen + recsize)
+ return 0;

- return len;
+ __kfifo_out_data(fifo, to, n, recsize);
+ __kfifo_add_out(fifo, reclen + recsize);
+ return 0;
}
-EXPORT_SYMBOL(kfifo_out);
+EXPORT_SYMBOL(__kfifo_out_n);

/**
- * kfifo_from_user - puts some data from user space into the FIFO
+ * kfifo_out - gets some data from the FIFO
* @fifo: the fifo to be used.
- * @from: pointer to the data to be added.
- * @n: the length of the data to be added.
+ * @to: where the data must be copied.
+ * @len: the size of the destination buffer.
*
- * This function copies at most @n bytes from the @from into the
- * FIFO depending and returns the number of copied bytes.
+ * This function copies at most @n bytes from the FIFO into
+ * @to 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_from_user(struct kfifo *fifo,
- const void __user *from, unsigned int n)
+unsigned int kfifo_out(struct kfifo *fifo, unsigned char *to, unsigned int n)
+{
+ n = min(kfifo_len(fifo), n);
+
+ __kfifo_out_data(fifo, to, n, 0);
+ __kfifo_add_out(fifo, n);
+
+ return n;
+}
+EXPORT_SYMBOL(kfifo_out);
+
+unsigned int __kfifo_out_generic(struct kfifo *fifo,
+ unsigned char *to, unsigned int n, unsigned int recsize,
+ unsigned int *total)
+{
+ return __kfifo_out_rec(fifo, to, n, recsize, total);
+}
+EXPORT_SYMBOL(__kfifo_out_generic);
+
+static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int off)
{
unsigned int l;
- unsigned int off;
int ret;

- n = min(n, fifo->size - fifo->in + fifo->out);
-
/*
* Ensure that we sample the fifo->out index -before- we
* start putting bytes into the kfifo.
@@ -220,56 +242,69 @@

smp_mb();

- off = fifo->in & (fifo->size - 1);
+ off = __kfifo_off(fifo, fifo->in+off);

/* first put the data starting from fifo->in to buffer end */
l = min(n, fifo->size - off);
-
ret = copy_from_user(fifo->buffer + off, from, l);

if (unlikely(ret))
- return l - ret;
+ return ret + n - l;

/* then put the rest (if any) at the beginning of the buffer */
ret = copy_from_user(fifo->buffer, from + l, n - l);

if (unlikely(ret))
- return n - ret;
+ return ret;

- /*
- * Ensure that we add the bytes to the kfifo -before-
- * we update the fifo->in index.
- */
-
- smp_wmb();
+ return 0;
+}

- fifo->in += n;
+unsigned int __kfifo_from_user_n(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize)
+{
+ if (kfifo_avail(fifo) < n + recsize)
+ return n + 1;

- return n;
+ return __kfifo_from_user_data(fifo, from, n, recsize);
}
-EXPORT_SYMBOL(kfifo_from_user);
+EXPORT_SYMBOL(__kfifo_from_user_n);

/**
- * kfifo_to_user - gets data from the FIFO and write it to user space
+ * kfifo_from_user - puts some data from user space into the FIFO
* @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @n: the size of the destination buffer.
+ * @from: pointer to the data to be added.
+ * @n: the length of the data to be added.
*
- * This function copies at most @n bytes from the FIFO into the
- * @to and returns the number of copied bytes.
+ * This function copies at most @n bytes from the @from into the
+ * FIFO 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_to_user(struct kfifo *fifo,
- void __user *to, unsigned int n)
+unsigned int kfifo_from_user(struct kfifo *fifo,
+ const void __user *from, unsigned int n)
+{
+ n = min(kfifo_avail(fifo), n);
+ n -= __kfifo_from_user_data(fifo, from, n, 0);
+ __kfifo_add_in(fifo, n);
+ return n;
+}
+EXPORT_SYMBOL(kfifo_from_user);
+
+unsigned int __kfifo_from_user_generic(struct kfifo *fifo,
+ const void __user *from, unsigned int n, unsigned int recsize)
+{
+ return __kfifo_from_user_rec(fifo, from, n, recsize);
+}
+EXPORT_SYMBOL(__kfifo_from_user_generic);
+
+static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int off)
{
unsigned int l;
- unsigned int off;
int ret;

- n = min(n, fifo->in - fifo->out);
-
/*
* Ensure that we sample the fifo->in index -before- we
* start removing bytes from the kfifo.
@@ -277,27 +312,85 @@

smp_rmb();

- off = fifo->out & (fifo->size - 1);
+ off = __kfifo_off(fifo, fifo->out+off);

/* first get the data from fifo->out until the end of the buffer */
l = min(n, fifo->size - off);
-
ret = copy_to_user(to, fifo->buffer + off, l);

if (unlikely(ret))
- return l - ret;
+ return ret + n - l;

/* then get the rest (if any) from the beginning of the buffer */
ret = copy_to_user(to + l, fifo->buffer, n - l);

if (unlikely(ret))
- return n - ret;
+ return ret;

- smp_mb();
+ return 0;
+}

- fifo->out += n;
+unsigned int __kfifo_to_user_n(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int reclen,
+ unsigned int recsize)
+{
+ unsigned int ret;
+
+ if (kfifo_len(fifo) < reclen + recsize)
+ return 0;
+
+ ret = __kfifo_to_user_data(fifo, to, reclen, recsize);
+
+ if (likely(ret == 0))
+ __kfifo_add_out(fifo, reclen + recsize);

+ return ret;
+}
+EXPORT_SYMBOL(__kfifo_to_user_n);
+
+/**
+ * kfifo_to_user - gets data from the FIFO and write it to user space
+ * @fifo: the fifo to be used.
+ * @to: where the data must be copied.
+ * @n: the size of the destination buffer.
+ *
+ * This function copies at most @n bytes from the FIFO into the
+ * @to 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_to_user(struct kfifo *fifo,
+ void __user *to, unsigned int n)
+{
+ n = min(kfifo_len(fifo), n);
+ n -= __kfifo_to_user_data(fifo, to, n, 0);
+ __kfifo_add_out(fifo, n);
return n;
}
EXPORT_SYMBOL(kfifo_to_user);

+unsigned int __kfifo_to_user_generic(struct kfifo *fifo,
+ void __user *to, unsigned int n, unsigned int recsize,
+ unsigned int *total)
+{
+ return __kfifo_to_user_rec(fifo, to, n, recsize, total);
+}
+EXPORT_SYMBOL(__kfifo_to_user_generic);
+
+unsigned int __kfifo_peek_generic(struct kfifo *fifo, unsigned int recsize)
+{
+ if (recsize == 0)
+ return kfifo_avail(fifo);
+
+ return __kfifo_peek_n(fifo, recsize);
+}
+EXPORT_SYMBOL(__kfifo_peek_generic);
+
+void __kfifo_skip_generic(struct kfifo *fifo, unsigned int recsize)
+{
+ __kfifo_skip_rec(fifo, recsize);
+}
+EXPORT_SYMBOL(__kfifo_skip_generic);
+
+


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/