- ixgbevf主要数据结构
1)struct ixgbevf_adapter
描述一个vf设备对象,主要包括qvetor、tx_ring、rx_ring以及记录io地址空间、netdev等信息;
struct ixgbevf_adapter {
struct ixgbevf_q_vector *q_vector[MAX_MSIX_Q_VECTORS];//中断关联信息
/* TX */
int num_tx_queues; //发送队列个数
struct ixgbevf_ring *tx_ring[MAX_TX_QUEUES]; /* One per active queue */
/* RX */
int num_rx_queues; //接收队列个数
struct ixgbevf_ring *rx_ring[MAX_TX_QUEUES]; /* One per active queue */
struct msix_entry *msix_entries;//msix中断信息
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
/* structs defined in ixgbe_vf.h */
struct ixgbe_hw hw;//硬件设备
struct ixgbevf_hw_stats stats;
u8 __iomem *io_addr; /* Mainly for iounmap use */ //io地址空间
u32 rss_key[IXGBEVF_VFRSSRK_REGS];
u8 rss_indir_tbl[IXGBEVF_X550_VFRETA_SIZE];
};
2)struct ixgbevf_q_vector
vf的每个中断都会申请一个q_vector对象,q_vector主要记录与其关联的tx、rx队列(在ixgbevf_map_rings_to_vectors里会做队列与q_vector的映射关系,可能存在一个q_vector对应多个队列的情况),另外比较重要的napi信息,当ixgbevf收包进入中断处理函数时,会通过改napi完成轮询批量收包,避免每个包处理都有中断;
struct ixgbevf_q_vector {
struct ixgbevf_adapter *adapter;
/* index of q_vector within array, also used for finding the bit in
* EICR and friends that represents the vector for this ring
*/
u16 v_idx;
u16 itr; /* Interrupt throttle rate written to EITR */
struct napi_struct napi;
struct ixgbevf_ring_container rx, tx;
char name[IFNAMSIZ + 9];
#ifdef CONFIG_NET_RX_BUSY_POLL
unsigned int state;
#define IXGBEVF_QV_STATE_IDLE 0
#define IXGBEVF_QV_STATE_NAPI 1 /* NAPI owns this QV */
#define IXGBEVF_QV_STATE_POLL 2 /* poll owns this QV */
#define IXGBEVF_QV_STATE_DISABLED 4 /* QV is disabled */
#define IXGBEVF_QV_OWNED (IXGBEVF_QV_STATE_NAPI | IXGBEVF_QV_STATE_POLL)
#define IXGBEVF_QV_LOCKED (IXGBEVF_QV_OWNED | IXGBEVF_QV_STATE_DISABLED)
#define IXGBEVF_QV_STATE_NAPI_YIELD 8 /* NAPI yielded this QV */
#define IXGBEVF_QV_STATE_POLL_YIELD 16 /* poll yielded this QV */
#define IXGBEVF_QV_YIELD (IXGBEVF_QV_STATE_NAPI_YIELD | \
IXGBEVF_QV_STATE_POLL_YIELD)
#define IXGBEVF_QV_USER_PEND (IXGBEVF_QV_STATE_POLL | \
IXGBEVF_QV_STATE_POLL_YIELD)
spinlock_t lock;
#endif /* CONFIG_NET_RX_BUSY_POLL */
};
3)struct ixgbevf_ring
用于描述一个ring对象,ring数据结构处理描述符desc、dma起始地址、长度外,还有两个比较重要的数据结构tx_buffer_info、rx_buffer_info,其中tx_buffer_info是在ixgbevf发送数据包是会使用到,ixgbevf每发送一个数据包,都会将数据包对应的dma信息记录到tx_buffer_info里,并且tx_buffer_info的next_to_watch设置改发送数据包所使用的描述符对象;然后在ixgbevf的中断处理函数ixgbevf_poll里会根据判断硬件是否已使用tx_buffer_info里缓存的描述符,如果已使用,则回收描述符(将对应的dma信息unmap);同理也会在ixgbevf_clean_rx_irq回收硬件已使用的描述符,将描述符的dma信息重新分配后设置给硬件下次继续使用;
struct ixgbevf_ring {
struct ixgbevf_ring *next;
struct net_device *netdev;
struct device *dev;
void *desc; /* descriptor ring memory */
dma_addr_t dma; /* phys. address of descriptor ring */
unsigned int size; /* length in bytes */
u16 count; /* amount of descriptors */
u16 next_to_use;
u16 next_to_clean;
u16 next_to_alloc;
union {
struct ixgbevf_tx_buffer *tx_buffer_info; //缓存tx_ring的描述符信息
struct ixgbevf_rx_buffer *rx_buffer_info; //缓存rx_ring的描述符信息
};
unsigned long state;
struct ixgbevf_stats stats;
struct u64_stats_sync syncp;
union {
struct ixgbevf_tx_queue_stats tx_stats;
struct ixgbevf_rx_queue_stats rx_stats;
};
u64 hw_csum_rx_error;
u8 __iomem *tail;
struct sk_buff *skb;
/* holds the special value that gets the hardware register offset
* associated with this ring, which is different for DCB and RSS modes
*/
u16 reg_idx;
int queue_index; /* needed for multiqueue queue management */
};
2、设备初始化流程
1)、设备probe阶段
ixgbevf_probe
ixgbevf_init_interrupt_scheme
ixgbevf_set_interrupt_capability
ixgbevf_acquire_msix_vectors
pci_enable_msix_range
__pci_enable_msix_range(msi.c)
__pci_enable_msix
msix_capability_init
msix_setup_entries(分配msi_desc信息,并根据msix_entry信息填充msi_desc,完成后将msi_desc添加到 dev->msi_list)
arch_setup_msi_irqs
arch_setup_msi_irqs(x86_init.c)
native_setup_msi_irqs(从dev->msi_list获取msi_desc信息,通过irq_alloc_hwirq为每个msi_desc分配 中断号,分配完成后通过setup_msi_irq-->irq_set_msi_desc_off将中断号信息 填充到msi_desc->irq里)
msix_program_entries(遍历dev->msi_list的每个msi_desc,将其中保存的irq保存到msix_entry->vector 里)
ixgbevf_alloc_q_vectors(分配qvector,为每个qvector分配一个napi,设置ixgbevf_poll)
ixgbevf_alloc_queues(分配tx、rx ring)
2)ixgbevf_open阶段
ixgbevf_open
ixgbevf_setup_all_tx_resources
ixgbevf_setup_all_rx_resources
ixgbevf_configure(将描述符信息通告给硬件)
ixgbevf_configure_tx
ixgbevf_configure_rx
ixgbevf_configure_rx_ring
ixgbevf_alloc_rx_buffers(为每个描述符预先分配好DMA内存信息供硬件使用)
ixgbevf_map_rings_to_vectors(将ring与qvector关联, 一个qvector可以匹配多个ring)
ixgbevf_request_irq
ixgbevf_request_msix_irqs(根据ixbevf_probe阶段分配的msix_entry->vector中断号信息,通过request_irq注册中断处理 函数ixgbevf_msix_clean_rings)
ixgbevf_up_complete(enable irq,enable tx、rq queue)
3、收包流程:
1)、驱动在ixgbevf_setup_all_rx_resources里为每个vring分配描述符信息以及rx_buffer_info信息;
2)、ixgbevf_configure_rx里将每个ring的描述符信息通知给硬件使用;
3)、ixgbevf_alloc_rx_buffers为每个ring的描述符信息分配内存信息(硬件使用);
4)、硬件收到报文后,通过DMA控制器,将实际报文填充到ring的描述符里指定的dma地址;
5)、硬件触发中断;
6)、ixgbevf驱动进入中断处理流程ixgbevf_msix_clean_rings;
7)、中断处理函数里将qvector的napi添加到softnet_data的pool_list里;然后触发软中断;
8)、在软中断处理函数net_rx_action里进入napi的pool函数ixgbevf_poll轮询收取报文;
9)、ixgbevf_poll里进入ixgbevf_clean_rx_irq,根据rx_ring->next_to_clean获得下一个待清理的描述符;清理前首先判断该描 述符硬件是否已经使用过;硬件如果使用了描述符,会将描述符标志置位IXGBE_RXD_STAT_DD状态;如果硬件确实已 经使用了改描述符,则通过ixgbevf_fetch_rx_buffer获取描述符里的buffer信息,并填充到skb中;
10)、rx描述符的报文取出后,判断描述指定的page信息是否可重复使用;如果可以,则将该描述符指定的DMA内存信息分配 给rx_ring->next_to_alloc标识的下一个待分配的描述符中;
11)、通过ixgbevf_rx_skb将收到的skb信息加到softnet_data的input_pkt_queue队列中;并将softnet_data的napi (sd->backlog)加到softnet_data的poll_list里,再次触发软中断;
12)、软中断处理函数net_rx_action里进入napi的pool函数process_backlog;
13)、process_backlog里获取softnet_data的input_pkt_queue队列的数据包;并通过__netif_receive_skb送到dev设备;
4、发包流程:
1)、在ixgbevf_setup_all_tx_resources里为每个ring分配tx描述符信息;
2)、协议栈有报文发送时,最终会调用到ixgbevf_xmit_frame;
3)、ixgbevf_xmit_frame获取上层协议栈选定的tx_ring信息,然后根据tx_ring->next_to_use标识获取下一个待使用tx_desc(假 设为tx_desc0)以及tx_buffer_info信息;
4)、进入ixgbevf_tx_map,首先将skb->data包数据内容做DMA映射,并将返回的DMA内存地址及长度信息先保存在 tx_buffer_info中;
5)、将数据包的DMA地址信息、长度填充到tx_desc中(当skb存在flag信息时,可能会需要使用多个描述符信息,假设最好使 用的是tx_desc_i);
6)、将tx_buffer_info的next_to_watch指定为本次填充的第一个描述符tx_desc0;
7)、更新tx_ring->next_to_use下一个待使用的描述符;
8)、将tx_ring->tail更新为tx_desc_i,并通知硬件(硬件需要将缓存的数据包发送,直到第i个描述符为止);
9)、硬件通过DMA控制器,将内存信息取走后,通过中断通知驱动,当驱动收到中断时,进入ixgbevf_poll;
10)、在ixgbevf_poll里通过ixgbevf_clean_tx_irq清理tx队列;
11)、首先根据tx_ring->next_to_clean获取待清理的tx_buffer,以及需要清理的下一个描述符tx_desc;然后根据 tx_buffer->next_to_watch获取本次待清理的最后一个tx描述符eop_desc(因为在发送的时候,skb有flag时,每次可能会 使用多个tx描述符);
12)、判断tx_desc描述符状态是否为IXGBE_TXD_STAT_DD(如果硬件已经将报文取走发送出去,则会将描述符置位DD状 态);如果是,则循环获取描述符释放对应的DMA内存释放,直到tx_desc描述符等于本次需要清理的最后一个描述符 eop_desc为止,将tx_buffer->next_to_watch置空,表示该tx_buffer没有需要清理的描述符了;