|
|
Subscribe / Log in / New account

A new API for kfifo?

By Jake Edge
August 19, 2009

The kernel FIFO implementation, kfifo, is not that widely used and Stefani Seibold would like to see that change. To that end, she has proposed a new kfifo API that embodies many of the design patterns for data structures that were described by Neil Brown. The new interface is simpler in many ways, so it should be easier to use, which, in turn, could lead to more use throughout the kernel.

The basic problems with the current kfifo interface stem from the constraints it places on its users. A spinlock is required, though it is not needed by the majority of current kfifo users. Also, the kfifo structure can only be allocated dynamically, so users cannot put FIFOs inside of other structures—only pointers to FIFOs. Moreover, according to Seibold, the current API is too simple and doesn't provide a number of important features.

The new API has 23 separate functions, while the current has 9, but there are quite a few variants that increase the total. Those variants include copying from or to user space, supporting fixed-length records, as well as being able to use spinlocks to synchronize adding or removing data from the FIFO. It supports many different use cases in the style of Brown's "Broad Interfaces" pattern.

A kfifo is declared using the DECLARE_KFIFO() macro which can be used inside of a struct or union declaration. FIFOs declared with with DECLARE_KFIFO() must be initialized using INIT_KFIFO(). Alternatively, DEFINE_KFIFO() handles both the declaration and initialization for FIFOs outside of struct/union declarations. The macros take name (to name the variable or struct/union element) and size (in bytes) parameters:

    DECLARE_KFIFO(name, size)
    INIT_KFIFO(name)

    DEFINE_KFIFO(name, size)

There are two functions to support dynamic buffer allocation:

    int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
which allocates a buffer of size bytes using the gfp_mask as flags to pass to kmalloc(). Or, a pre-allocated buffer can be attached to a kfifo using:
    void kfifo_init(struct kfifo *fifo, unsigned char *buffer, unsigned int size)
Any buffer allocated by kfifo_alloc() should later be freed by calling kfifo_free().

To put data into the FIFO, kfifo_in() is used:

    unsigned int kfifo_in(struct kfifo *fifo, 
        unsigned char *from, unsigned int n)
which returns the number of bytes stored. As mentioned above there are variants for getting the data from user space, as well as for locking:
    unsigned int kfifo_from_user(struct kfifo *fifo, 
        const void __user *from, unsigned int n)

    unsigned int kfifo_in_locked(struct kfifo *fifo,
	const unsigned char *from, unsigned int n, spinlock_t *lock)

As one might guess, the calls to remove data from the FIFO look similar—if reversed:

    unsigned int kfifo_out(struct kfifo *fifo, 
        unsigned char *to, unsigned int n)

    unsigned int kfifo_to_user(struct kfifo *fifo, 
        void __user *to, unsigned int n)

    unsigned int kfifo_out_locked(struct kfifo *fifo,
	unsigned char *to, unsigned int n, spinlock_t *lock)
In the common case, with only one reader and one writer, extra locks are not required to add or remove data from the FIFO, but for more complicated scenarios, the *_locked() variants allow the caller to use the appropriate lock.

The expected tests for FIFO full and empty (kfifo_is_full() and kfifo_is_empty()) are present, as are ways to get FIFO size and available space (kfifo_size(), kfifo_len(), and kfifo_avail()). One can also consume some FIFO bytes without returning them using kfifo_skip() or clear the entire FIFO with kfifo_reset().

There is also support for fixed-length records, with either 1- or 2-byte lengths stored with each entry. Each call must pass a recsize parameter that specifies the size of the length field (i.e. 1 or 2, though 0 is supported for no length) stored with each entry:

    unsigned int kfifo_in_rec(struct kfifo *fifo,
	void *from, unsigned int n, unsigned int recsize)

    unsigned int kfifo_from_user_rec(struct kfifo *fifo,
	const void __user *from, unsigned int n, unsigned int recsize)

    unsigned int kfifo_out_rec(struct kfifo *fifo,
	void *to, unsigned int n, unsigned int recsize,
	unsigned int *total)

    unsigned int kfifo_to_user_rec(struct kfifo *fifo,
	void __user *to, unsigned int n, unsigned int recsize,
	unsigned int *total)

These functions return the number of bytes not copied, which is a bit strange. For the functions that remove data from the FIFO, *total stores the number of bytes actually copied. This part of the interface seems a little dubious, and may not survive in its present form, though no comments along those lines have been seen.

Overall, the interface has been fairly well-received. There were a few comments on an earlier version of the API, which Seibold has mostly addressed. The only comments on the most recent version (v0.4) were a disagreement between Alan Cox and Andrew Morton over naming conventions.

Cox would rather not see the current kfifo_put() and kfifo_get() calls get removed quite yet, noting "We are about to set fifo loose through all the USB and some other char/serial drivers all of which will use the spinlock facility." The current calls use the spinlock, so the kind of change Seibold is proposing would need to be reflected in the USB and char/serial driver code. But Morton thinks that this is the right time to make those changes, because "kfifo has no business assuming that the caller wants to use spin_lock() locking".

The basic problem is that Morton would like to see a convention followed that things like kfifo_put() (or, kfifo_in() as Seibold's API defines it) do not assume locking. He agrees with Seibold's decision to add a separate kfifo_*_locked() variants. Cox points out that the convention is very inconsistently followed, but Morton is adamant:

Plus, as I've said enty en times and keep getting ignored: the current naming is wrong. The generic kfifo_get() should not be assuming that the caller wants spinlock-based locking.

After initially NAK-ing part of the patch, Cox seems to have relented, so that particular barrier is gone. It would seem far too late in the 2.6.31 process for this kind of change to go in, but unless some other major issues crop up, it is quite possible that a new kfifo API could show up in 2.6.32.


Index entries for this article
Kernelkfifo


to post comments

A new API for kfifo?

Posted Aug 20, 2009 9:29 UTC (Thu) by stefani (guest, #5846) [Link] (1 responses)

About the record handling:

The record functions supports three kinds of record types:

- Records with a fixed size of bytes
- Records with a variable length between 0 and 255 bytes
- Records with a variable length between 0 and 65535 bytes

The return value of the record handling functions is well thought, because it makes it much easier to determinate if the function fails:

- 0 means everything is okay
- a return value lower or equal then the passed length means that the copy_*_user() function has failed
- a value greater the passed length means the record does not fit into the destination buffer/fifo.

It makes no sense to cut off a record, so a return value not equal 0 signals a failure.

Add the *total pointer to the record out functions makes sense, if using variable length records. There must be a way to return the length of the processed record, otherwise the caller don't know the size of the variable length record. You can set the *total pointer to NULL if this value is not requiered.

BTW: Stefani is a girls name, so he is she ;-)

A new API for kfifo?

Posted Aug 20, 2009 13:24 UTC (Thu) by jake (editor, #205) [Link]

> BTW: Stefani is a girls name, so he is she ;-)

Oh my. I am terribly sorry about that. Fixed now.

Thanks!

jake


Copyright © 2009, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds