netmap源码阅读笔记

来源:互联网 发布:gcp网络培训 编辑:程序博客网 时间:2024/04/29 09:48

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包含多少个slotconst uint32_tnum_slots;// 每一个slot的内存大小const uint32_tnr_buf_size;// 当前netmap_ring索引(可能分配多个netmap_ring)const uint16_tringid;// 标识此ring是用于发送还是接受,0: tx, 1: rxconst 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_slotstruct 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_PIPESstruct 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 0
原创粉丝点击