All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nelson Castillo <nelsoneci@gmail.com>
To: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Cc: Stelian Pop <stelian@popies.net>
Subject: [RFC][PATCH 1/1] support for user-space buffers in kfifo
Date: Mon, 11 Jun 2007 16:26:14 -0500	[thread overview]
Message-ID: <20070611212614.GB21025@emqbit.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 7876 bytes --]

Hi.

I just added support for user space buffers in kfifo. I found useful
__kfifo_get_user to copy data to a user buffer in a read call. I didn't
like the idea of having an extra buffer.

* Is it ok to add this support?
* Am I missing something?

I expect to fill the fifo using __kfifo_put in interrupt context.
If I understand correctly, I won't need extra locking if I have a
single interrupt handler filling this buffer and all the readers use a
semaphore.

This is how I'm using it now:

static ssize_t device_read(struct file *filep, char __user *buffer,
			   size_t length, loff_t * offset)
{
	struct sarpic_dev *dev = filep->private_data;
	int ret_val;

	if (down_interruptible (&dev->sem))
		return -ERESTARTSYS;

	while (__kfifo_len(dev->fifo) == 0) {
		up(&dev->sem);

		if (filep->f_flags & O_NONBLOCK)
			return -EAGAIN;

		if (wait_event_interruptible(dev->readq, (__kfifo_len(dev->fifo) != 0)))
			return -ERESTARTSYS;

		if (down_interruptible(&dev->sem))
			return -ERESTARTSYS;
	}

	ret_val = __kfifo_get_user(dev->fifo, buffer, length);

	up(&dev->sem);

	return ret_val;
}

static ssize_t
device_write(struct file *filep,
             const char __user *buffer, size_t length, loff_t *offset)
{
	struct sarpic_dev *dev = filep->private_data;
	int ret_val;

  if (down_interruptible (&dev->sem))
    return -ERESTARTSYS;

  ret_val = __kfifo_put_user(dev->fifo, buffer, length);

  up(&dev->sem);

  wake_up_interruptible(&dev->readq);

  return ret_val;
}

Regards,
Nelson.-


Signed-off-by: Nelson Castillo <nelson@emqbit.com>
---
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 404f446..4568285 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -41,8 +41,13 @@ extern struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask,
 extern void kfifo_free(struct kfifo *fifo);
 extern unsigned int __kfifo_put(struct kfifo *fifo,
 				unsigned char *buffer, unsigned int len);
+extern int __kfifo_put_user(struct kfifo *fifo,
+			    const unsigned char __user *buffer,
+			    unsigned int len);
 extern unsigned int __kfifo_get(struct kfifo *fifo,
 				unsigned char *buffer, unsigned int len);
+extern int __kfifo_get_user(struct kfifo *fifo,  unsigned char __user *buffer,
+			    unsigned int len);
 
 /**
  * __kfifo_reset - removes the entire FIFO contents, no locking version
@@ -94,6 +99,32 @@ static inline unsigned int kfifo_put(struct kfifo *fifo,
 }
 
 /**
+ * kfifo_put_user - puts some user data into the FIFO
+ * @fifo: the fifo to be used.
+ * @buffer: the user-space 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.
+ */
+static inline int kfifo_put_user(struct kfifo *fifo,
+				 const unsigned char __user *buffer,
+			         unsigned int len)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(fifo->lock, flags);
+
+	ret = __kfifo_put_user(fifo, buffer, len);
+
+	spin_unlock_irqrestore(fifo->lock, flags);
+
+	return ret;
+}
+
+/**
  * kfifo_get - gets some data from the FIFO
  * @fifo: the fifo to be used.
  * @buffer: where the data must be copied.
@@ -125,6 +156,34 @@ static inline unsigned int kfifo_get(struct kfifo *fifo,
 }
 
 /**
+ * kfifo_get_user - gets some data from the FIFO an user buffer
+ * @fifo: the fifo to be used.
+ * @buffer: user buffer where the data must be copied
+ * @len: the size of the destination buffer.
+ *
+ * This function copies at most @len bytes from the FIFO into the
+ * @buffer and returns the number of copied bytes.
+ */
+
+static inline int kfifo_get_user(struct kfifo *fifo,
+				     unsigned char __user *buffer, unsigned int len)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(fifo->lock, flags);
+
+	ret = __kfifo_get_user(fifo, buffer, len);
+
+	if (fifo->in == fifo->out)
+		fifo->in = fifo->out = 0;
+
+	spin_unlock_irqrestore(fifo->lock, flags);
+
+	return ret;
+}
+
+/**
  * __kfifo_len - returns the number of bytes available in the FIFO, no locking version
  * @fifo: the fifo to be used.
  */
diff --git a/kernel/kfifo.c b/kernel/kfifo.c
index cee4191..e95ab37 100644
--- a/kernel/kfifo.c
+++ b/kernel/kfifo.c
@@ -23,6 +23,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <asm/uaccess.h>
 #include <linux/kfifo.h>
 
 /**
@@ -150,6 +151,59 @@ unsigned int __kfifo_put(struct kfifo *fifo,
 EXPORT_SYMBOL(__kfifo_put);
 
 /**
+ * __kfifo_put_user - puts some data into the FIFO, from userspace
+ *		       no locking version
+ * @fifo: the fifo to be used.
+ * @buffer: the user space 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.
+ */
+int __kfifo_put_user(struct kfifo *fifo, const unsigned char __user *buffer,
+	        unsigned int len)
+{
+	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.
+	 */
+
+	smp_mb();
+
+	/* first put the data starting from fifo->in to buffer end */
+	l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
+
+	if(copy_from_user(fifo->buffer + (fifo->in & (fifo->size - 1)),
+			  buffer, l))
+		return  -EFAULT;
+
+
+	/* then put the rest (if any) at the beginning of the buffer */
+	if (copy_from_user(fifo->buffer, buffer + l, len - l))
+		return  -EFAULT;
+
+	/*
+	 * Ensure that we add the bytes to the kfifo -before-
+	 * we update the fifo->in index.
+	 */
+
+	smp_wmb();
+
+	fifo->in += len;
+
+	return (int)len;
+}
+EXPORT_SYMBOL(__kfifo_put_user);
+
+/**
  * __kfifo_get - gets some data from the FIFO, no locking version
  * @fifo: the fifo to be used.
  * @buffer: where the data must be copied.
@@ -193,4 +247,58 @@ unsigned int __kfifo_get(struct kfifo *fifo,
 
 	return len;
 }
+
 EXPORT_SYMBOL(__kfifo_get);
+
+/**
+ * __kfifo_get_user - gets some data from the FIFO to userspace
+ *		      no blocking version
+ * @fifo: the fifo to be used.
+ * @buffer: user space buffer where the data must be copied.
+ * @len: the size of the destination buffer.
+ *
+ * This function copies at most @len bytes from the FIFO into the
+ * @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.
+ */
+
+int __kfifo_get_user(struct kfifo *fifo,
+			 unsigned char __user *buffer, unsigned int len)
+{
+	unsigned int l;
+
+	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 */
+	l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
+
+	if (copy_to_user(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l))
+		return -EFAULT;
+
+	/* then get the rest (if any) from the beginning of the buffer */
+	if (copy_to_user(buffer + l, fifo->buffer, len - l))
+		return -EFAULT;
+
+	/*
+	 * Ensure that we remove the bytes from the kfifo -before-
+	 * we update the fifo->out index.
+	 */
+
+	smp_mb();
+
+	fifo->out += len;
+
+	return (int)len;
+}
+
+EXPORT_SYMBOL(__kfifo_get_user);
+

-- 

http://arhuaco.org
http://emQbit.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

             reply	other threads:[~2007-06-11 21:26 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-06-11 21:26 Nelson Castillo [this message]
2007-06-12 13:43 ` [RFC][PATCH 1/1] support for user-space buffers in kfifo Stelian Pop
2007-06-12 14:55   ` Nelson Castillo
  -- strict thread matches above, loose matches on Subject: below --
2007-06-12 16:24 Nelson Castillo
2007-06-12 22:12 ` Stelian Pop
2007-06-13  2:21 Nelson Castillo
2007-06-13  6:00 ` Stelian Pop

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070611212614.GB21025@emqbit.com \
    --to=nelsoneci@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stelian@popies.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.