linux内核之门之kfifo源码分析(2)

时间:2021-01-19 03:17:35
kfifo的源代码主要是<linux/kfifo.h>和<kernel/kfifo.c>文件

kfifo结构定义:
struct kfifo {
        unsigned char *buffer;/*保存数据的缓冲空间*/
        unsigend int size;/*分配的空间大小*/
        unsigned int in;/*入队数据的offset(in % size)*/
        unsigned int out;/*出队数据的offset(out %size)*/
};

kfifo_alloc函数:
/**
 * kfifo_alloc - 分配一个内部的buffer
 * @fifo;赋予新buffer的fifo
 * @size: 分配buffer的大小,必须是2的幂
 * @gfp_mask: get_free_pages掩码,传递给kmalloc()
 *
 * 这个函数动态地分配一个新的内部buffer.
 *
 * size会四舍五入为2的幂.
 * buffer由kfifo_free()来释放.
 * 返回值:成功则为0,否则返error code.
 */
int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
{
        unsigned char *buffer;

        /*将size向上舍入为最近2的幂*/
        if (!is_power_of_2(size)) {
                BUG_ON(size > 0x80000000);
                size = roundup_pws_of_two(size);
        }

        buffer = kmalloc(size, gfp_mask);
        if (!buffer) {/*分配失败*/
                _kfifo_init(fifo, NULL, 0);
                return -ENOMEM;
        }

        _kfifo_init(fifo, buffer, size);/*初始化fifo成员*/

        return 0;
}

_kfifo_init函数:
/*
 * _kfifo_init - 初始化fifo结构体成员
 */
static void _kfifo_init(struct kfifo *fifo, void *buffer,
                unsigned int size)
{
        fifo->buffer = buffer;
        fifo->size = size;

        kfifo_reset(fifo);/*重置fifo的in和out为0*/
}

kfifo_reset函数:
/**
 * kfifo_reset - 移除FIFO所有的内容
 * @fifo:将被清空的fifo
 */
static inline void kfifo_reset(struct kfifo *fifo)
{
        fifo->in = fifo->out = 0;
}

kfifo_free函数:
/*
 * kfifo_free - 释放FIFO内部的buffer
 * @fifo: 被释放的fifo
 */
void kfifo_free(struct kfifo *fifo)
{
        kfree(fifo->buffer);
        __kfifo_init(fifo, NULL, 0);
}

kfifo_init函数:
/*
 * kfifo_init - 用一个已分配的buffer来初始化FIFO
 *
 * @fifo: 赋予buffer的fifo
 * @buffer: 预先分配的buffer
 * @size: buffer的大小,必须为2的幂
 */
void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
{
        BUG_ON(!is_power_of_2(size));

        _kfifo_init(fifo, buffer, size);
}

DECLARE_KFIFO宏:
#define DECLARE_KFIFO(name, size) \
union { \
        struct kfifo name; \
        unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
}

INIT_KFIFO宏:
#define INIT_KFIFO(name) \
        name = __kfifo_initializer(sizeof(name##kfifo_buffer) - \
                                sizeof(struct kfifo), \
                                name##kfifo_buffer + sizeof(struct kfifo))

__kfifo_initializer宏:
#define __kfifo_initializer(s, b) \
        (struct kfifo) {
                .size   = s, \
                .in     = 0, \
                .out    = 0, \
                .buffer = b \
        }

这三个宏完成的功能示意图:
        +---------------+<---name##kfifo_buffer                                              
      / |               | \                                                
      | |   fifo.buffer | |                                               
      | |   fifo.size   | sizeof(struct kfifo)                                               
      | |   fifo.in=0   | |                                               
      | |   fifo.out=0  | /                                               
      | +---------------+<---fifo.buffer=name##kfifo_buffer+sizeof(struct kfifo)                                            
      | |               | \                                               
      | |               | |                                               
size+siz|               | |                                               
of(struc|               | |                                               
t kfifo)|               | fifo.size=sizeof(name##kfifo_buffer)-sizeof(struct kfifo)
      | |               | |                                               
      | |               | |                                               
      | |               | |                                               
      | |               | |                                               
      | |               | |                                               
      \ |               | /                                               
        +---------------+                                                 

kfifo_init函数:
/**
 * kfifo_in - 把数据放置到FIFO中
 * @fifo: 存放数据的fifo
 * @from: 数据来源
 * @len: 数据的长度(字节)
 *
 * 从@from缓存中至多复制len字节的数据到@fifo中,且受限于@fifo的空闲空间.
 * 返回值:已复制的数据长度(字节)
 */
unsigned int kfifo_in(struct kfifo *fifo, const void *from,
                                unsigned int len)
{
        len = min(kfifo_avail(fifo), len);

        __kfifo_in_data(fifo, from, len, 0);
        __kfifo_add_in(fifo, len);
        return len;
}

kfifo_avail函数:
/**
 * kfifo_avail - 获取fifo的空闲空间字节数
 */
static inline unsigned int kfifo_avail(struct kfifo *fifo)
{
        return kfifo_size(fifo) - kfifo_len(fifo);
}

kfifo_size函数:
/**
 * kfifo_size - 返回fifo的总空间字节大小
 * @fifo:
 */
static inline unsigned int kfifo_size(struct kfifo *fifo)
{
        return fifo->size;
}

kfifo_len函数:
/**
 * kfifo_len - 返回FIFO已使用空间的字节数
 * @fifo:
 */
static inline unsigned int kfifo_len(struct kfifo *fifo)
{
        register unsigned int out;

        out = fifo->out;
        smp_rmb();
        return fifo->in - out;
}


__kfifo_in_data函数:
/**
 * __kfifo_in_data - 从from缓存中将len字节的数据复制到从off开始位置的fifo中
 * @off: 相对in的偏移量
 */
static inline void __kfifo_in_data(struct kfifo *fifo,
                const void *from, unsigned int len, unsigned int off)
{
        unsigned int l;

        smp_mb();

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

        l = min(len, fifo->size - off);/*从偏移量到fifo buffer的尾部的空闲长度*/
        memcpy(fifo->buffer + off, from, l);/*先填充尾部空闲*/

        memcpy(fifo->buffer, from + l, len - l);/*再填充头部空闲*/
}

__kfifo_off函数:
/**
 * __kfifo_off - 计算给定offset在fifo中的索引值
 */
static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
{
        return off & (fifo->size - 1);/*等价于(off % fifi->size)*/
}

__kfifo_add_in函数:
/**
 * __kfifo_add_in - 更新fifo的in值
 */
static inline void __kfifo_add_in(struct kfifo *fifo, unsigned int off)
{
        smp_wmb();
        fifo->in += off;
}

kfifo_out函数:
/**
 * kfifo_out - 从FIFO取出一些数据
 *
 * @fifo:
 * @to: 存放取出后的数据的地方
 * @len: 目的缓存的大小
 *
 * 这个函数从@fifo最多复制@len字节数据到@to缓存中.
 * 返回值:已复制的字节数.
 */
unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)
{
        len = min(kfifo_len(fifo), len);

        __kfifo_out_data(fifo, to, len, 0);
        __kfifo_add_out(fifo, len);

        return len;
}

__kfifo_out_data函数:
/**
 * __kfifo_out_data - 从@fifo复制len字节的数据到to缓存中
 *
 * @off: 相当out的偏移量
 */
static inline void __kfifo_out_data(struct kfifo *fifo,
                void *to, unsigned int len, unsigned int off)
{
        unsigned int l;

        smp_rmb();

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

        l = min(len, fifo->size - off);
        memcpy(to, fifo->buffer + off, l);/*先获取尾部的数据*/

        memcpy(to + l, fifo->buffer, len - l);/*先获取头部的数据*/
}

__kfifo_add_out函数:
/**
 * __kfifo_add_out - 更新fifo的out值
 */
static inline void __kfifo_add_out(struct kfifo *fifo,
                                unsigned int off)
{
        smp_mb();
        fifo->out += off;
}

kfifo_out_peek函数:
/**
 * kfifo_out_peek - 从fifo复制一些数据,但并不移除它们
 *
 * @fifo:
 * @to: 存储复制数据的目的缓存
 * @len: 目的缓冲大小
 * @offset:
 *
 * 该函数从fifo的offset开始位置复制len字节的数据到to缓存中
 * 返回值:已复制的字节数
 */
unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len,
                                unsigned int offset)
{
        len = min(kfifo_len(fifo), len + offset);

        __kfifo_out_data(fifo, to, len, offset);
        return len;
}
这个函数与kfifo_out的不同在于没有更新fifo的out值

kfifo_is_empty函数:
/**
 * kfifo_is_empty - 检测fifo是否为空
 */
static inline int kfifo_is_empty(struct kfifo *fifo)
{
        return fifo->in == fifo->out;
}

kfifo_is_full函数:
/**
 * kfifo_is_full - 检测fifo是否已满
 */
static inline int kfifo_is_full(struct kfifo *fifo)
{
        return kfifo_len(fifo) == kfifo_size(fifo);
}

对in和out操作数据示意分析:
初始:
        size=1024
        in=out=0
        len=in-out=0(已有数据)
        avail=size-len=1024(空闲空间)
插入100字节数据:kfifo_in(fifo, from, 100)
        size=1024
        off=(更新之前的in%size)=(0%1024)=0(数据插入位置)
        in=100
        out=0
        len=in-out=100
        avail=size-len=924
再插入1000字节数据:kfifo_in(fifo, from, 1000)
        size=1024
        off=(更新之前的in%size)=100%1024=100(数据插入位置)
        in=100+min(avail, 1000)=100+924=1024
        out=0
        len=in-out=1024
        avail=size-len=0
获取300字节数据:kfifo_out(fifo, to, 300)
        size=1024
        off=(更新之前的out%size)=(0%1024)=0(数据获取位置)
        in=1024
        out=0+min(len,300)=300
        len=in-out=724
        avail=size-len=300
再插入100字节数据:
        size=1024
        off=(更新之间的in%size)=1024%1024=0(数据插入位置)
        in=1124
        out=300
        len=in-out=824
        avail=size-len=200
获取800字节数据:
        size=1024
        off=(更新之前的300%size)=(300%1024)=300
        in=1124
        out=300+min(len,800)=1100
        len=in-out=24
        avail=size-len=1000
插入20字节数据:
        size=1024
        off=(更新之前的1124%size)=(1124%1024)=100
        in=1144
        out=1100
        len=in-out=44
        avail=size-len=980