对于L1来说,接收工作比发送复杂一些,在初始化时注册一个中断处理函数,request_irq(fip->fc_interrupt, fcc_enet_interrupt, 0, "fenet", dev) 当收到一个新的报文,会触发一个软中端SIU_INT_FCC1,调用fcc_enet_interrupt函数,进而调用fcc_enet_rx进入接收处理。Rx函数先分配一个sk_buffer,将从寄存器取来的报文填入该buffer中。
接下来就是将buffer中的以太网头部剥离,目前8032的按协议划分vlan的接收部分就是在此完成,剥离以太网头部时发现proto为8021Q,则暴力修改以太网帧,剥离vlan层,重置以太网层的proto。
数据结构变动图
上面的流程图是内核中常用的去除某个报文头的方法,Sk_buff的总大小是不变的,只是针对指针位置将数据作了必要的位移,甚至连14bit的以太网头部的最后2bit协议类型都因为已经赋值了skb->proto而不做memmov。
之后就可以通过netif_rx进入协议栈了。netif_rx调用napi_schedule调度,挂接当前napi_struct到处理CPU的napi_struct中,发送NET_RX_SOFTIRQ软中断,触发net_rx_action(/net/core/dev.c)。(NET_RX_SOFTIRQ的注册在net_dev_init中)[code]open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);[/code] net_rx_action这里会历遍所有的softnet_data结构,执行其poll操作process_backlog(/net/core/dev.c),调用netif_receive_skb(skb)来处理收到的skb数据,最后唤醒recvfrom调用。
而内核中的vlan模块就是工作在协议栈里,通过注册dev_add_pack注册了8021Q的协议类型,netif_receive_skb遍历所有的协议类型时发现了它,于是进入了vlan的接收函数vlan_skb_recv(vlan_dev.c),这个函数里剥离vlan层,重置以太网层的proto,重新调用netif_rx进入真正的协议栈处理流程,例如,L3是IP协议,则进入ip_rcv(/net/ipv4/ip_input.c)