All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefani Seibold <stefani@seibold.net>
To: linux-kernel <linux-kernel@vger.kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Subject: [RFC 2/2] new kfifo API
Date: Mon, 03 Aug 2009 15:39:23 +0200	[thread overview]
Message-ID: <1249306763.8358.11.camel@wall-e> (raw)

/*
 * A generic kernel FIFO implementation.
 *
 * Copyright (C) 2009 Stefani Seibold/Munich/Germany <stefani@seibold.net>
 *
 * base on work:
 * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/kfifo.h>

struct kfifo *kfifo_alloc(unsigned long size, gfp_t gfp_mask)
{
	struct kfifo *fifo;

	BUG_ON(size > 0x80000000);

	fifo = kmalloc(sizeof(struct kfifo) + size, gfp_mask);
	if (!fifo)
		return ERR_PTR(-ENOMEM);

	fifo->size = size;
	fifo->in = fifo->out = 0;

	return fifo;
}
EXPORT_SYMBOL(kfifo_alloc);

void kfifo_free(struct kfifo *fifo)
{
	kfree(fifo);
}
EXPORT_SYMBOL(kfifo_free);

static inline unsigned long __kfifo_put_data(struct kfifo *fifo,
		const unsigned char *from, unsigned long n, unsigned long off)
{
	unsigned long	l;

	/*
	 * Ensure that we sample the fifo->out index -before- we
	 * start putting bytes into the kfifo.
	 */

	smp_mb();

	off += fifo->in;

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

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

	return 0;
}

/**
 * kfifo_put_notrim - puts data into the FIFO without cut off a record
 * @fifo: the fifo to be used.
 * @from: the data to be added.
 * @n: the size of the destination buffer.
 * @recsize: size of record field
 * @total: pointer where the total number of copied bytes should stored
 *
 * This function copies @n bytes from the @from into the FIFO
 * 
 * In case of an error, the function returns the number of bytes which cannot
 * be copied.
 */
unsigned long __kfifo_put_notrim(struct kfifo *fifo,
	const unsigned char *from, unsigned long n, unsigned long recsize,
	unsigned long *total)
{
	if (kfifo_avail(fifo) < n + recsize) {
		*total = n + 1;
		return *total;
	}

	*total = n;

	return __kfifo_put_data(fifo, from, n, recsize);
}
EXPORT_SYMBOL(__kfifo_put_notrim);

unsigned long __kfifo_put_trim(struct kfifo *fifo,
	const unsigned char *from, unsigned long n, unsigned long recsize,
	unsigned long *total)
{
	unsigned long	l;

	l = min(kfifo_avail(fifo), n + recsize);

	if (l < recsize) {
		*total = n + 1;
		return *total;
	}

	*total = l - recsize;

	return __kfifo_put_data(fifo, from, l - recsize, recsize);
}
EXPORT_SYMBOL(__kfifo_put_trim);

unsigned long __kfifo_put_generic(struct kfifo *fifo,
	const unsigned char *from, unsigned long n, unsigned long recsize,
	unsigned long flags, unsigned long *total)
{
	if (!recsize)
		return __kfifo_put(fifo, from, n, flags, total);
	return __kfifo_put_n(fifo, from, n, recsize, flags, total);
}
EXPORT_SYMBOL(__kfifo_put_generic);

static inline unsigned long __kfifo_get_data(struct kfifo *fifo,
		unsigned char *to, unsigned long n, unsigned long off)
{
	unsigned long l;

	/*
	 * Ensure that we sample the fifo->in index -before- we
	 * start removing bytes from the kfifo.
	 */

	smp_rmb();

	off += fifo->out;

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

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

	return 0;
}

unsigned long __kfifo_get_notrim(struct kfifo *fifo,
	unsigned char *to, unsigned long n, unsigned long reclen,
	unsigned long recsize, unsigned long *total)
{
	if (kfifo_used(fifo) < recsize) {
		*total = 0;
		return 0;
	}

	*total = n;

	if (n < reclen)
		return reclen;

	return __kfifo_get_data(fifo, to, n, recsize);
}
EXPORT_SYMBOL(__kfifo_get_notrim);

unsigned long __kfifo_get_trim(struct kfifo *fifo,
	unsigned char *to, unsigned long n, unsigned long reclen,
	unsigned long recsize, unsigned long *total)
{
	if (kfifo_used(fifo) < recsize) {
		*total = 0;
		return 0;
	}

	n = min(n, reclen);

	*total = n;

	return __kfifo_get_data(fifo, to, n, recsize);
}
EXPORT_SYMBOL(__kfifo_get_trim);

unsigned long __kfifo_get_generic(struct kfifo *fifo,
	unsigned char *to, unsigned long n, unsigned long recsize,
	unsigned long flags, unsigned long *total)
{
	if (!recsize)
		return __kfifo_get(fifo, to, n, flags, total);
	return __kfifo_get_n(fifo, to, n, recsize, flags, total);
}
EXPORT_SYMBOL(__kfifo_get_generic);

static inline unsigned long __kfifo_from_user_data(struct kfifo *fifo,
	 const void __user *from, unsigned long n, unsigned long off)
{
	unsigned long l;
	int ret;

	/*
	 * Ensure that we sample the fifo->out index -before- we
	 * start putting bytes into the kfifo.
	 */

	smp_mb();

	off += fifo->in;

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

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

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

	if (unlikely(ret))
		return ret;

	return 0;
}

unsigned long __kfifo_from_user_notrim(struct kfifo *fifo,
	const void __user *from, unsigned long n, unsigned long recsize,
	unsigned long *total)
{
	if (kfifo_avail(fifo) < n + recsize) {
		*total = n + 1;
		return *total;
	}

	*total = n;

	return __kfifo_from_user_data(fifo, from, n, recsize);
}
EXPORT_SYMBOL(__kfifo_from_user_notrim);

unsigned long __kfifo_from_user_trim(struct kfifo *fifo,
	const void __user *from, unsigned long n, unsigned long recsize,
	unsigned long *total)
{
	unsigned long	l;

	l = min(kfifo_avail(fifo), n + recsize);

	if (l < recsize) {
		*total = n + 1;
		return *total;
	}

	*total = l - recsize;

	return __kfifo_from_user_data(fifo, from, l - recsize, recsize);
}
EXPORT_SYMBOL(__kfifo_from_user_trim);

unsigned long __kfifo_from_user_generic(struct kfifo *fifo,
	const void __user *from, unsigned long n, unsigned long recsize,
	unsigned long flags, unsigned long *total)
{
	if (!recsize)
		return __kfifo_from_user(fifo, from, n, flags, total);
	return __kfifo_from_user_n(fifo, from, n, recsize, flags, total);
}
EXPORT_SYMBOL(__kfifo_from_user_generic);

static inline unsigned long __kfifo_to_user_data(struct kfifo *fifo,
		void __user *to, unsigned long n, unsigned long off)
{
	unsigned long l;
	int ret;

	/*
	 * Ensure that we sample the fifo->in index -before- we
	 * start removing bytes from the kfifo.
	 */

	smp_rmb();

	off += fifo->out;

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

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

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

	if (unlikely(ret))
		return ret;

	return 0;
}

unsigned long __kfifo_to_user_notrim(struct kfifo *fifo,
	void __user *to, unsigned long n, unsigned long reclen,
	unsigned long recsize, unsigned long *total)
{
	if (kfifo_used(fifo) < recsize) {
		*total = 0;
		return 0;
	}

	*total = n;

	if (n < reclen)
		return reclen;

	return __kfifo_to_user_data(fifo, to, n, recsize);
}
EXPORT_SYMBOL(__kfifo_to_user_notrim);

unsigned long __kfifo_to_user_trim(struct kfifo *fifo,
	void __user *to, unsigned long n, unsigned long reclen,
	unsigned long recsize, unsigned long *total)
{
	if (kfifo_used(fifo) < recsize) {
		*total = 0;
		return 0;
	}

	n = min(n, reclen);

	*total = n;

	return __kfifo_to_user_data(fifo, to, n, recsize);
}
EXPORT_SYMBOL(__kfifo_to_user_trim);

unsigned long __kfifo_to_user_generic(struct kfifo *fifo,
	void __user *to, unsigned long n, unsigned long recsize,
	unsigned long flags, unsigned long *total)
{
	if (!recsize)
		return __kfifo_to_user(fifo, to, n, flags, total);
	return __kfifo_to_user_n(fifo, to, n, recsize, flags, total);
}
EXPORT_SYMBOL(__kfifo_to_user_generic);

unsigned long __kfifo_peek_generic(struct kfifo *fifo, unsigned long recsize)
{
	if (recsize == 0)
		return kfifo_avail(fifo);

	return __kfifo_peek_n(fifo, recsize);
}
EXPORT_SYMBOL(__kfifo_peek_generic);




                 reply	other threads:[~2009-08-03 13:39 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=1249306763.8358.11.camel@wall-e \
    --to=stefani@seibold.net \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    /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.