netmap源码阅读笔记

netmap源码涉及的技术非常多,本人以前从未做过驱动开发或者内核开发,阅读代码的过程中深感吃力,不过,不管怎样,还是有些收获,这些知识如果不加整理记录,后面再回忆起来可能又要重新思考一次,很不明智;况且,目前只是梳理了个框架,细节方面还需要日后再继续研究整理,是以为记。


首先要说明的是,netmap的代码质量很高,在FreeBSD中已经与内核代码一起发布,且注释非常清晰,里面有大段的注释,相当于设计文档,大家要细细研究。

其次,我个人觉得,要彻底弄清楚实现机理,还需要对内核原理、驱动开发有相当的了解,否则看起来会比较吃力,我就属于这种。


以下是我的一部分阅读笔记:

/

1. 数据结构

文件: netmap.h
描述: netmap把一个netmap_slot结构称为"buffer descriptor",这点一定要记住,netmap
      源码中,有很多处出现descriptor,大家要能够明白所指之物。当然,作者这样称呼
      的原因是因为netmap_slot结构本身并不包含slot内存,它只是"描述"slot的属性。
      我们最关心的slot所指内存使用buf_idx表示,其真实所在其实是在内存池中。
作用: 报文(收/发)存储的场所
创建: when - 注册netmap设备时。
      who   - 由内核创建,并且mmap到用户空间。
      where - 使用kmalloc申请,处于内核空间。

struct netmap_slot {
uint32_t buf_idx;/* buffer index */
uint16_t len; /* length for this slot */
uint16_t flags;/* buf changed, etc. */
uint64_t ptr; /* pointer for indirect buffers */
};

 

文件: netmap.h

描述: 顾名思义,此结构代表的是netmap的一个ring,简单来讲就是一个环形缓冲区,基本上可以这样认为。slot[0]成员指示的就是ring的组成成分——netmap_slot,这只是一 个占位符,真正申请内存的时候,这后面会跟着一堆netmap_slot。提醒一下,前面我们讲过,netmap_slot只是一个描述符,里面并不包含真正拷贝报文所需内存。

作用: 表示一个环形缓冲区

创建: when - 注册netmap设备时。      

who  - 由内核创建,并且mmap到用户空间。      

where - 使用kmalloc申请,处于内核空间。

struct netmap_ring 
{
  // buffer在内存池中的偏移,主要用于宏计算
const int64_t buf_ofs;

// 此ring包含多少个slot
const uint32_tnum_slots;

// 每一个slot的内存大小
const uint32_tnr_buf_size;

// 当前netmap_ring索引(可能分配多个netmap_ring)
const uint16_tringid;

// 标识此ring是用于发送还是接受,0: tx, 1: rx
const uint16_tdir; 

// 
uint32_t        head;/* (u) first user slot */

uint32_t        cur;/* (u) wakeup point */

uint32_t tail; /* (k) first kernel slot */

uint32_t flags;
struct timevalts; /* (k) time of last *sync() */

uint8_t sem[128] __attribute__((__aligned__(NM_CACHE_ALIGN)));

  // 一堆netmap_slot
struct netmap_slot slot[0];/* array of slots. */
};

文件: netmap.h
描述: 代表一个网络接口及接口上的队列。此结构在绑定一个文件描述符到
      一个端口的时候进行初始化,对于用户程序来说是只读的,内核不使用
      此结构。
作用: 
创建: when - 注册netmap设备时。
      who   - 内核模块创建。
      where - 用户空间使用。

struct netmap_if {
char ni_name[IFNAMSIZ];/* name of the interface. */
const uint32_tni_version; /* API version, currently unused */
const uint32_tni_flags; /* properties */

const uint32_tni_tx_rings; /* number of HW tx rings */
const uint32_tni_rx_rings; /* number of HW rx rings */

uint32_t ni_bufs_head; /* head index for extra bufs */
uint32_t ni_spare1[5];

const ssize_t ring_ofs[0];
};

所在文件: netmap.h
struct nmreq {
char nr_name[IFNAMSIZ];
uint32_t nr_version; /* API version */

uint32_t nr_offset; /* nifp offset in the shared region */
uint32_t nr_memsize; /* size of the shared region */

uint32_t nr_tx_slots; /* slots in tx rings */
uint32_t nr_rx_slots; /* slots in rx rings */

uint16_t nr_tx_rings; /* number of tx rings */
uint16_t nr_rx_rings; /* number of rx rings */

uint16_t nr_ringid; /* ring(s) we care about */

uint16_t nr_cmd;

uint16_t nr_arg1; /* reserve extra rings in NIOCREGIF */
uint16_t nr_arg2;
uint32_t nr_arg3; /* req. extra buffers in NIOCREGIF */

uint32_t nr_flags;
uint32_t spare2[1]; /* various modes, extends nr_ringid */
};

文件: netmap.h
描述: 与ixgbe_adapter对应,代表逻辑上的一个netmap适配器。
作用: 
创建: when - 加载驱动时创建。
      who   - 内核模块创建。
      where - 内核使用。
struct netmap_adapter {
uint32_t magic;
uint32_t na_flags;/* enabled, and other flags */

int active_fds; 

u_int num_rx_rings; /* number of adapter receive rings */
u_int num_tx_rings; /* number of adapter transmit rings */

u_int num_tx_desc; /* number of descriptor in each queue */
u_int num_rx_desc;

struct netmap_kring *tx_rings; /* array of TX rings. */
struct netmap_kring *rx_rings; /* array of RX rings. */

void *tailroom;      /* space below the rings array */

NM_SELINFO_T tx_si, rx_si;/* global wait queues */

int tx_si_users, rx_si_users;

int (*if_transmit)(struct ifnet *, struct mbuf *);
void (*if_input)(struct ifnet *, struct mbuf *);

struct ifnet *ifp; /* adapter is ifp->if_softc */

/*---- callbacks for this netmap adapter -----*/

void (*nm_dtor)(struct netmap_adapter *);
int  (*nm_register)(struct netmap_adapter *, int onoff);
int  (*nm_txsync)(struct netmap_kring *kring, int flags);
int  (*nm_rxsync)(struct netmap_kring *kring, int flags);
int  (*nm_config)(struct netmap_adapter *, u_int *txr, u_int *txd, u_int *rxr, u_int *rxd);
int  (*nm_krings_create)(struct netmap_adapter *);
void (*nm_krings_delete)(struct netmap_adapter *);
int  (*nm_notify)(struct netmap_adapter *, u_int ring, enum txrx, int flags);

int na_refcount;

struct netmap_mem_d *nm_mem;

struct lut_entry *na_lut;

uint32_t na_lut_objtotal;/* max buffer index */

void *na_private;

#ifdef WITH_PIPES
struct netmap_pipe_adapter **na_pipes;
int na_next_pipe;
int na_max_pipes;
#endif /* WITH_PIPES */
};

1) 用户空间可见的数据结构,都是由内核申请内存,然后mmap到用户空间的。
2) netmap中大量使用内存偏移或者索引来代替指针,这样更容易在内核与用户之间使用。(?)

2. 处理流程

1) 加载netmap_lin.ko
a) 初始化全局锁(全局锁干啥用的?)
b) 初始化内存池的信号量(?)
c) 使用misc_register创建/dev/netmap设备。这个操作同时也注册了netmap支持的设备操作接口,
  后续,对netmap的操作就可以像访问普通设备一样了。

static struct file_operations netmap_fops = {
       .owner = THIS_MODULE,
     .open = linux_netmap_open,
     .mmap = linux_netmap_mmap,
     .ioctl = linux_netmap_ioctl,
     .poll = linux_netmap_poll,
     .release = linux_netmap_release,
};
d) VALE初始化

2) 加载ixgbe.ko

module_init -> ixgbe_init_module -> pci_register_driver(dca_register_notify)

static struct pci_driver ixgbe_driver = {
.name     = ixgbe_driver_name,
.id_table = ixgbe_pci_tbl,
.probe    = ixgbe_probe,
.remove   = __devexit_p(ixgbe_remove),
#ifdef CONFIG_PM
.suspend  = ixgbe_suspend,
.resume   = ixgbe_resume,
#endif
.shutdown = ixgbe_shutdown,
.err_handler = &ixgbe_err_handler
};

简单来讲就是注册PCI设备,不过,这里需要注意,由于ixgbe驱动被修改适配netmap框架,
ixgbe.ko依赖于netmap_lin.ko,换句话说ixgbe.ko需要使用netmap_lin.ko中的导出符号,
所以,加载顺序必须是先加载netmap_lin.ko再加载ixgbe.ko。

内核注册完驱动后,就会调用其初始化函数ixgbe_probe,此函数会做一堆初始化,这里
摘取出几项比较重要的分析下:
a) 设置ixgbe netdev的回调函数:netdev->netdev_ops = &ixgbe_netdev_ops;
b) 设置ixgbe ethtool的回调函数:ixgbe_set_ethtool_ops(netdev);
c) ixgbe_init_interrupt_scheme:设置RSS等的队列个数和申请中断向量。
这里面还会设置NAPI的回调接口为ixgbe_poll,这样报文接收通道就构建好了。
d) ixgbe_netmap_attach:创建netmap_adapter赋值到net_device的ax25_ptr成员,
  这样,netmap与ixgbe就建立一种联系,可以说是netmap attach到ixgbe了。
  
3) up/open事件(以RX为例)

ixgbe_up/ixgbe_open --> ixgbe_configure -> ixgbe_configure_rx 
-> ixgbe_configure_rx_ring -> ixgbe_netmap_configure_rx_ring
  
  a) DMA资源和中断资源都是在这个流程申请的。而加载驱动时只是设置DMA工作参数等。
  b) 每一个ring都需要申请独立的dma资源
  
  
4) 使用netmap设备

nm_open -> open("/dev/netmap", O_RDWR) -> ioctl(d->fd, NIOCREGIF, &d->req) -> mmap

a) 用户使用的API都在netmap_user.h中声明。
b) 调用nm_open会以netmap方式打开网卡设备,内部包含一系列动作。
c) open操作最终会调用之前加载的内核模块netmap_lin.ko中的linux_netmap_open。
d) ioctl操作会在内核申请
e) mmap操作将内核空间映射到用户空间来,

5) 接收报文
NAPI -> ixgbe_poll -> ixgbe_clean_rx_irq -> netmap_rx_irq -> netmap_common_irq -> netmap_notify

a) netmap底层也是使用NAPI来接收报文的。
b) ixgbe_poll是遍历所有ring来逐个通知上层的。
c) netmap_rx_irq劫持了ixgbe驱动的报文接收,从这个函数往下,报文接收就完全由netmap接管了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值