转载:linux kfifo

参考:https://blog.csdn.net/Bruno_Mars/article/details/100061793

kfifo源码:

https://elixir.bootlin.com/linux/v5.4.240/source/include/linux/kfifo.h#L118

https://elixir.bootlin.com/linux/v5.4.240/source/lib/kfifo.c#L113

kfifo smaple code:

https://elixir.bootlin.com/linux/v5.4.240/source/samples/kfifo

kfifo 特性

kfifo是内核里面的一个First In First Out数据结构

源码位于:

linux\lib\kfifo.c
linux\include\linux\kfifo.h
kfifo 实现了使用简单/性能高效的队列操作

只要满足以下要求, kfifo 操作便可以实现不加锁, 从而提高性能:

只有一个 reader 和 一个 writer,不调用 kfifo_reset(),如果有调用 kfifo_reset_out(), 只在出队线程中调用

而对于多个 writer 对应一个 reader 的情况, 只需要在 writer (入队线程)加锁即可
而对于多个 reader 对应一个 writer 的情况, 只需要在 reader (出队线程)加锁即可
源码中关于锁的相关介绍如下:

/*
 * Note about locking: There is no locking required until only one reader
 * and one writer is using the fifo and no kfifo_reset() will be called.
 * kfifo_reset_out() can be safely used, until it will be only called
 * in the reader thread.
 * For multiple writer and one reader there is only a need to lock the writer.
 * And vice versa for only one writer and multiple reader there is only a need
 * to lock the reader.
 */

 


kfifo 分为两种, 一种是普通的 kfifo, 一种是带长度记录的 kfiro_rec
对于 kfifo,内存 buffer 全部用来存放数据,如下图:

而对于 kfiro_rec,内存 buffer 在每次入队时都会用一个字节或两个字节存放数据的长度,如下图:

kfifo 适用于流数据的数据缓存, 一个线程进行数据入队, 另一个线程进行数据出队处理
kfifo_rec 适用于块数据的数据缓存, 一个线程进行一块块的数据入队, 另一个线程进行每次取一块数据进行

kfifo 结构体

struct __kfifo {
    unsigned int    in;
    unsigned int    out;
    unsigned int    mask;
    unsigned int    esize;
    void        *data;
};

#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
    union { \
        struct __kfifo    kfifo; \
        datatype    *type; \
        const datatype    *const_type; \
        char        (*rectype)[recsize]; \
        ptrtype        *ptr; \
        ptrtype const    *ptr_const; \
    }

#define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
{ \
    __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
    type        buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
}

#define STRUCT_KFIFO(type, size) \
    struct __STRUCT_KFIFO(type, size, 0, type)

#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
{ \
    __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
    type        buf[0]; \
}

#define STRUCT_KFIFO_PTR(type) \
    struct __STRUCT_KFIFO_PTR(type, 0, type)
/*
 * helper macro to distinguish between real in place fifo where the fifo
 * array is a part of the structure and the fifo type where the array is
 * outside of the fifo structure.
 */
#define    __is_kfifo_ptr(fifo) \
    (sizeof(*fifo) == sizeof(STRUCT_KFIFO_PTR(typeof(*(fifo)->type))))

/**
 * DECLARE_KFIFO_PTR - macro to declare a fifo pointer object
 * @fifo: name of the declared fifo
 * @type: type of the fifo elements
 */
#define DECLARE_KFIFO_PTR(fifo, type)    STRUCT_KFIFO_PTR(type) fifo

/**
 * DECLARE_KFIFO - macro to declare a fifo object
 * @fifo: name of the declared fifo
 * @type: type of the fifo elements
 * @size: the number of elements in the fifo, this must be a power of 2
 */
#define DECLARE_KFIFO(fifo, type, size)    STRUCT_KFIFO(type, size) fifo

/**
 * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO
 * @fifo: name of the declared fifo datatype
 */
#define INIT_KFIFO(fifo) \
(void)({ \
    typeof(&(fifo)) __tmp = &(fifo); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    __kfifo->in = 0; \
    __kfifo->out = 0; \
    __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\
    __kfifo->esize = sizeof(*__tmp->buf); \
    __kfifo->data = __is_kfifo_ptr(__tmp) ?  NULL : __tmp->buf; \
})

/**
 * DEFINE_KFIFO - macro to define and initialize a fifo
 * @fifo: name of the declared fifo datatype
 * @type: type of the fifo elements
 * @size: the number of elements in the fifo, this must be a power of 2
 *
 * Note: the macro can be used for global and local fifo data type variables.
 */
#define DEFINE_KFIFO(fifo, type, size) \
    DECLARE_KFIFO(fifo, type, size) = \
    (typeof(fifo)) { \
        { \
            { \
            .in    = 0, \
            .out    = 0, \
            .mask    = __is_kfifo_ptr(&(fifo)) ? \
                  0 : \
                  ARRAY_SIZE((fifo).buf) - 1, \
            .esize    = sizeof(*(fifo).buf), \
            .data    = __is_kfifo_ptr(&(fifo)) ? \
                NULL : \
                (fifo).buf, \
            } \
        } \
    }

 

kfifo使用了in和out两个变量分别作为入队和出队的索引:

  • 入队n个数据时,in变量就+n
  • 出队k个数据时,out变量就+k
  • out不允许大于in(out等于in时表示fifo为空)
  • in不允许比out大超过fifo空间(比如上图,in最多比out多8,此时表示fifo已满)

如果in和out大于fifo空间了,比如上图中的8,会减去8后重新开始吗?

不,这两个索引会一直往前加,不轻易回头,为出入队操作省下了几个指令周期。

那入队和出队的数据从哪里开始存储/读取呢,我们第一时间会想到,把 in/out 用“%”对fifo大小取余就行了,是吧?

不,取余这种耗费资源的运算,内核开发者怎会轻易采用呢,kfifo的办法是,把 in/out 与上fifo->mask。这个mask等于fifo的空间大小减一(其要求fifo的空间必须是2的次方大小)。这个“与”操作可比取余操作快得多了。

由此,kfifo就实现了“无锁”“环形”队列。

 

参考https://elixir.bootlin.com/linux/v5.4.240/source/samples/kfifo/inttype-example.c#L40,有两种方式定义kfifo:

DYNAMIC:使用DECLARE_KFIFO_PTR定义kfifo 后,使用kfifo_alloc 初始化struct _kfifo, 并动态申请kfifo->data的memory

STATIC:使用DEFINE_KFIF定义kfifo.

#ifdef DYNAMIC
static DECLARE_KFIFO_PTR(test, int);
#else
static DEFINE_KFIFO(test, int, FIFO_SIZE);
#endif
static int __init example_init(void)
{
#ifdef DYNAMIC
    int ret;

    ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
    if (ret) {
        printk(KERN_ERR "error kfifo_alloc\n");
        return ret;
    }
#endif
    if (testfunc() < 0) {
#ifdef DYNAMIC
        kfifo_free(&test);
#endif
        return -EIO;
    }

    if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
#ifdef DYNAMIC
        kfifo_free(&test);
#endif
        return -ENOMEM;
    }
    return 0;
}

static void __exit example_exit(void)
{
    remove_proc_entry(PROC_FIFO, NULL);
#ifdef DYNAMIC
    kfifo_free(&test);
#endif
} 

kfifo_alloc使用kmalloc_array为struct _kfifo->data动态申请size * esize大小的memory,其中size是2^N, _kfifo->mask = size - 1;

/**
 * kfifo_alloc - dynamically allocates a new fifo buffer
 * @fifo: pointer to the fifo
 * @size: the number of elements in the fifo, this must be a power of 2
 * @gfp_mask: get_free_pages mask, passed to kmalloc()
 *
 * This macro dynamically allocates a new fifo buffer.
 *
 * The number of elements will be rounded-up to a power of 2.
 * The fifo will be release with kfifo_free().
 * Return 0 if no error, otherwise an error code.
 */
#define kfifo_alloc(fifo, size, gfp_mask) \
__kfifo_int_must_check_helper( \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    __is_kfifo_ptr(__tmp) ? \
    __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
    -EINVAL; \
}) \
)

int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
        size_t esize, gfp_t gfp_mask)
{
    /*
     * round up to the next power of 2, since our 'let the indices
     * wrap' technique works only in this case.
     */
    size = roundup_pow_of_two(size);

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

    if (size < 2) {
        fifo->data = NULL;
        fifo->mask = 0;
        return -EINVAL;
    }

    fifo->data = kmalloc_array(esize, size, gfp_mask);

    if (!fifo->data) {
        fifo->mask = 0;
        return -ENOMEM;
    }
    fifo->mask = size - 1;

    return 0;
}

如果不想使用kmalloc申请memory时,比如想用vmalloc or dma_alloc_coherent, 则可用其他方式申请memory, 再使用kfifo_init来初始化kfifo

/**
 * kfifo_init - initialize a fifo using a preallocated buffer
 * @fifo: the fifo to assign the buffer
 * @buffer: the preallocated buffer to be used
 * @size: the size of the internal buffer, this have to be a power of 2
 *
 * This macro initializes a fifo using a preallocated buffer.
 *
 * The number of elements will be rounded-up to a power of 2.
 * Return 0 if no error, otherwise an error code.
 */
#define kfifo_init(fifo, buffer, size) \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    __is_kfifo_ptr(__tmp) ? \
    __kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : \
    -EINVAL; \
})
int __kfifo_init(struct __kfifo *fifo, void *buffer,
        unsigned int size, size_t esize)
{
    size /= esize;

    if (!is_power_of_2(size))
        size = rounddown_pow_of_two(size);

    fifo->in = 0;
    fifo->out = 0;
    fifo->esize = esize;
    fifo->data = buffer;

    if (size < 2) {
        fifo->mask = 0;
        return -EINVAL;
    }
    fifo->mask = size - 1;

    return 0;
}
EXPORT_SYMBOL(__kfifo_init);

使用dynamic方式的kfifo, element时放到struct _kfifo->data内。使用DEFINE_KFIFO时,element是放到type buffer[size] 内。

kfifo_put是将一个element加入kfifo(_kfifo->data or type buf), 然后将_kfifo->in++

/**
 * kfifo_put - put data into the fifo
 * @fifo: address of the fifo to be used
 * @val: the data to be added
 *
 * This macro copies the given value into the fifo.
 * It returns 0 if the fifo was full. Otherwise it returns the number
 * processed elements.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_put(fifo, val) \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    typeof(*__tmp->const_type) __val = (val); \
    unsigned int __ret; \
    size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    if (__recsize) \
        __ret = __kfifo_in_r(__kfifo, &__val, sizeof(__val), \
            __recsize); \
    else { \
        __ret = !kfifo_is_full(__tmp); \
        if (__ret) { \
            (__is_kfifo_ptr(__tmp) ? \
            ((typeof(__tmp->type))__kfifo->data) : \
            (__tmp->buf) \
            )[__kfifo->in & __tmp->kfifo.mask] = \
                *(typeof(__tmp->type))&__val; \
            smp_wmb(); \
            __kfifo->in++; \
        } \
    } \
    __ret; \
})

kfifo_get是从kfifo(_kfifo->data or type buf)获取一个element, 然后将_kfifo->out++

/**
 * kfifo_get - get data from the fifo
 * @fifo: address of the fifo to be used
 * @val: address where to store the data
 *
 * This macro reads the data from the fifo.
 * It returns 0 if the fifo was empty. Otherwise it returns the number
 * processed elements.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_get(fifo, val) \
__kfifo_uint_must_check_helper( \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    typeof(__tmp->ptr) __val = (val); \
    unsigned int __ret; \
    const size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    if (__recsize) \
        __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \
            __recsize); \
    else { \
        __ret = !kfifo_is_empty(__tmp); \
        if (__ret) { \
            *(typeof(__tmp->type))__val = \
                (__is_kfifo_ptr(__tmp) ? \
                ((typeof(__tmp->type))__kfifo->data) : \
                (__tmp->buf) \
                )[__kfifo->out & __tmp->kfifo.mask]; \
            smp_wmb(); \
            __kfifo->out++; \
        } \
    } \
    __ret; \
}) \
)

kfifo_in 适用于dynamic kfifo, 将n个element 加入kfifo (_kfifo->data), 然后将_kfifo->in += n

/**
 * kfifo_in - put data into the fifo
 * @fifo: address of the fifo to be used
 * @buf: the data to be added
 * @n: number of elements to be added
 *
 * This macro copies the given buffer into the fifo and returns the
 * number of copied elements.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_in(fifo, buf, n) \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    typeof(__tmp->ptr_const) __buf = (buf); \
    unsigned long __n = (n); \
    const size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    (__recsize) ?\
    __kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
    __kfifo_in(__kfifo, __buf, __n); \
})
unsigned int __kfifo_in(struct __kfifo *fifo,
        const void *buf, unsigned int len)
{
    unsigned int l;

    l = kfifo_unused(fifo);
    if (len > l)
        len = l;

    kfifo_copy_in(fifo, buf, len, fifo->in);
    fifo->in += len;
    return len;
}
EXPORT_SYMBOL(__kfifo_in);
static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
        unsigned int len, unsigned int off)
{
    unsigned int size = fifo->mask + 1;
    unsigned int esize = fifo->esize;
    unsigned int l;

    off &= fifo->mask;
    if (esize != 1) {
        off *= esize;
        size *= esize;
        len *= esize;
    }
    l = min(len, size - off);

    memcpy(fifo->data + off, src, l);
    memcpy(fifo->data, src + l, len - l);
    /*
     * make sure that the data in the fifo is up to date before
     * incrementing the fifo->in index counter
     */
    smp_wmb();
}

kfifo_out适用于dynamic kfifo, 从kfifo (_kfifo->data)获取将n个element , 然后将_kfifo->in += n

/**
 * kfifo_out - get data from the fifo
 * @fifo: address of the fifo to be used
 * @buf: pointer to the storage buffer
 * @n: max. number of elements to get
 *
 * This macro get some data from the fifo and return the numbers of elements
 * copied.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_out(fifo, buf, n) \
__kfifo_uint_must_check_helper( \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    typeof(__tmp->ptr) __buf = (buf); \
    unsigned long __n = (n); \
    const size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    (__recsize) ?\
    __kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
    __kfifo_out(__kfifo, __buf, __n); \
}) \
)

unsigned int __kfifo_out(struct __kfifo *fifo,
        void *buf, unsigned int len)
{
    len = __kfifo_out_peek(fifo, buf, len);
    fifo->out += len;
    return len;
}
EXPORT_SYMBOL(__kfifo_out);

unsigned int __kfifo_out_peek(struct __kfifo *fifo,
        void *buf, unsigned int len)
{
    unsigned int l;

    l = fifo->in - fifo->out;
    if (len > l)
        len = l;

    kfifo_copy_out(fifo, buf, len, fifo->out);
    return len;
}
EXPORT_SYMBOL(__kfifo_out_peek);

static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
        unsigned int len, unsigned int off)
{
    unsigned int size = fifo->mask + 1;
    unsigned int esize = fifo->esize;
    unsigned int l;

    off &= fifo->mask;
    if (esize != 1) {
        off *= esize;
        size *= esize;
        len *= esize;
    }
    l = min(len, size - off);

    memcpy(dst, fifo->data + off, l);
    memcpy(dst + l, fifo->data, len - l);
    /*
     * make sure that the data is copied before
     * incrementing the fifo->out index counter
     */
    smp_wmb();
}

kfifo_from_user 将user space的data copy到kfifo(_kfifo->data)

/**
 * kfifo_from_user - puts some data from user space into the fifo
 * @fifo: address of the fifo to be used
 * @from: pointer to the data to be added
 * @len: the length of the data to be added
 * @copied: pointer to output variable to store the number of copied bytes
 *
 * This macro copies at most @len bytes from the @from into the
 * fifo, depending of the available space and returns -EFAULT/0.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_from_user(fifo, from, len, copied) \
__kfifo_uint_must_check_helper( \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    const void __user *__from = (from); \
    unsigned int __len = (len); \
    unsigned int *__copied = (copied); \
    const size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    (__recsize) ? \
    __kfifo_from_user_r(__kfifo, __from, __len,  __copied, __recsize) : \
    __kfifo_from_user(__kfifo, __from, __len, __copied); \
}) \
)

int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
        unsigned long len, unsigned int *copied)
{
    unsigned int l;
    unsigned long ret;
    unsigned int esize = fifo->esize;
    int err;

    if (esize != 1)
        len /= esize;

    l = kfifo_unused(fifo);
    if (len > l)
        len = l;

    ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
    if (unlikely(ret)) {
        len -= ret;
        err = -EFAULT;
    } else
        err = 0;
    fifo->in += len;
    return err;
}
EXPORT_SYMBOL(__kfifo_from_user);

static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
    const void __user *from, unsigned int len, unsigned int off,
    unsigned int *copied)
{
    unsigned int size = fifo->mask + 1;
    unsigned int esize = fifo->esize;
    unsigned int l;
    unsigned long ret;

    off &= fifo->mask;
    if (esize != 1) {
        off *= esize;
        size *= esize;
        len *= esize;
    }
    l = min(len, size - off);

    ret = copy_from_user(fifo->data + off, from, l);
    if (unlikely(ret))
        ret = DIV_ROUND_UP(ret + len - l, esize);
    else {
        ret = copy_from_user(fifo->data, from + l, len - l);
        if (unlikely(ret))
            ret = DIV_ROUND_UP(ret, esize);
    }
    /*
     * make sure that the data in the fifo is up to date before
     * incrementing the fifo->in index counter
     */
    smp_wmb();
    *copied = len - ret * esize;
    /* return the number of elements which are not copied */
    return ret;
}

kfifo_to_user 将data从kfifo(_kfifo->data) copy 到user space

/**
 * kfifo_to_user - copies data from the fifo into user space
 * @fifo: address of the fifo to be used
 * @to: where the data must be copied
 * @len: the size of the destination buffer
 * @copied: pointer to output variable to store the number of copied bytes
 *
 * This macro copies at most @len bytes from the fifo into the
 * @to buffer and returns -EFAULT/0.
 *
 * Note that with only one concurrent reader and one concurrent
 * writer, you don't need extra locking to use these macro.
 */
#define    kfifo_to_user(fifo, to, len, copied) \
__kfifo_int_must_check_helper( \
({ \
    typeof((fifo) + 1) __tmp = (fifo); \
    void __user *__to = (to); \
    unsigned int __len = (len); \
    unsigned int *__copied = (copied); \
    const size_t __recsize = sizeof(*__tmp->rectype); \
    struct __kfifo *__kfifo = &__tmp->kfifo; \
    (__recsize) ? \
    __kfifo_to_user_r(__kfifo, __to, __len, __copied, __recsize) : \
    __kfifo_to_user(__kfifo, __to, __len, __copied); \
}) \
)

int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
        unsigned long len, unsigned int *copied)
{
    unsigned int l;
    unsigned long ret;
    unsigned int esize = fifo->esize;
    int err;

    if (esize != 1)
        len /= esize;

    l = fifo->in - fifo->out;
    if (len > l)
        len = l;
    ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
    if (unlikely(ret)) {
        len -= ret;
        err = -EFAULT;
    } else
        err = 0;
    fifo->out += len;
    return err;
}
EXPORT_SYMBOL(__kfifo_to_user);

static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
        unsigned int len, unsigned int off, unsigned int *copied)
{
    unsigned int l;
    unsigned long ret;
    unsigned int size = fifo->mask + 1;
    unsigned int esize = fifo->esize;

    off &= fifo->mask;
    if (esize != 1) {
        off *= esize;
        size *= esize;
        len *= esize;
    }
    l = min(len, size - off);

    ret = copy_to_user(to, fifo->data + off, l);
    if (unlikely(ret))
        ret = DIV_ROUND_UP(ret + len - l, esize);
    else {
        ret = copy_to_user(to + l, fifo->data, len - l);
        if (unlikely(ret))
            ret = DIV_ROUND_UP(ret, esize);
    }
    /*
     * make sure that the data is copied before
     * incrementing the fifo->out index counter
     */
    smp_wmb();
    *copied = len - ret * esize;
    /* return the number of elements which are not copied */
    return ret;
}

 

kfifo_size : kfifo的大小,即能装下多少个element.

/**
 * kfifo_size - returns the size of the fifo in elements
 * @fifo: address of the fifo to be used
 */
#define kfifo_size(fifo)    ((fifo)->kfifo.mask + 1)

kfifo_len: kfifo内element 个数。

注意到element 的个数为in - out, in是unsinged int,如果溢出了,这个结果依然是对的。

在32位机器上,假设N为正整数,static_cast<unsigned int>(-N) == 2^32 – N。也就是说,-N的补码,如果强制解释成正整数,其数值为2^32-N。

如果in溢出了,out没有溢出。记in没有溢出的数值为in_nofl,则in_nofl == 2^32+in。由于fifo.in < fifo.out,fifo.in – fifo.out为负数,因此static_cast<unsigned int>(fifo.in-fifo.out) == 2^32 -(fifo.out-fifo.in) == (2^32+fifo.in)-fifo.out == in_nofl – fifo.out。因此kfifo_len是正确

/**
 * kfifo_len - returns the number of used elements in the fifo
 * @fifo: address of the fifo to be used
 */
#define kfifo_len(fifo) \
({ \
    typeof((fifo) + 1) __tmpl = (fifo); \
    __tmpl->kfifo.in - __tmpl->kfifo.out; \
})

kfifo_is_empty kfifo是否为空,当kfifo.in == kfifo.out时为空

/**
 * kfifo_is_empty - returns true if the fifo is empty
 * @fifo: address of the fifo to be used
 */
#define    kfifo_is_empty(fifo) \
({ \
    typeof((fifo) + 1) __tmpq = (fifo); \
    __tmpq->kfifo.in == __tmpq->kfifo.out; \
})

kfifo_is_full, 当kfifo中element个数大于kfifo.mask,则表示kfifo满了。

/**
 * kfifo_is_full - returns true if the fifo is full
 * @fifo: address of the fifo to be used
 */
#define    kfifo_is_full(fifo) \
({ \
    typeof((fifo) + 1) __tmpq = (fifo); \
    kfifo_len(__tmpq) > __tmpq->kfifo.mask; \
})

 

kfifo_alloc例子:https://elixir.bootlin.com/linux/v5.4.240/source/drivers/dma/qcom/hidma_ll.c#L786

DECLARE_KFIFO_PTR(handoff_fifo,
        struct hidma_tre *);    /* pending TREs FIFO               */


sz = nr_tres * sizeof(struct hidma_tre *);
rc = kfifo_alloc(&lldev->handoff_fifo, sz, GFP_KERNEL);

struct hidma_tre *tre;
tre->err_info = err_info;
tre->err_code = err_code;
tre->queued = 0;

kfifo_put(&lldev->handoff_fifo, tre);

kfifo_out(&lldev->handoff_fifo, &tre, 1)

DEFINE_KFIFO例子:https://elixir.bootlin.com/linux/v5.4.240/source/drivers/pci/pcie/aer.c#L1020

static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
            AER_RECOVER_RING_SIZE);

struct aer_recover_entry entry;
kfifo_get(&aer_recover_ring, &entry)

static DEFINE_SPINLOCK(aer_recover_ring_lock);
struct aer_recover_entry entry = {
        .bus        = bus,
        .devfn        = devfn,
        .domain        = domain,
        .severity    = severity,
        .regs        = aer_regs,
    };

if (kfifo_in_spinlocked(&aer_recover_ring, &entry, 1,
                 &aer_recover_ring_lock))
        schedule_work(&aer_recover_work);

kfifo_from_user例子:https://elixir.bootlin.com/linux/v5.4.240/source/samples/kfifo/bytestream-example.c#L122

static ssize_t fifo_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    int ret;
    unsigned int copied;

    if (mutex_lock_interruptible(&write_lock))
        return -ERESTARTSYS;

    ret = kfifo_from_user(&test, buf, count, &copied);

    mutex_unlock(&write_lock);
    if (ret)
        return ret;

    return copied;
}

static ssize_t fifo_read(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
{
    int ret;
    unsigned int copied;

    if (mutex_lock_interruptible(&read_lock))
        return -ERESTARTSYS;

    ret = kfifo_to_user(&test, buf, count, &copied);

    mutex_unlock(&read_lock);
    if (ret)
        return ret;

    return copied;
}

 

posted @ 2023-04-09 23:30  fellow_jing  阅读(185)  评论(0编辑  收藏  举报