网卡是用结构体net_device表述的,通过函数register_netdev来注册具体的网卡。
mini2440的的网卡放在driver/net/dm9000.c中。
在
net_device->netdev_ops中定义。
net_device->netdev_ops也是一个结构体(const struct net_device_ops *netdev_ops),其定义了如下操作:
struct net_device_ops {
int (*ndo_open)(struct net_device *dev);
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev); //发送hanshu
};
1)ndo_open说明
这里是用dm9000_open实现的,里面比较关键的是定义了数据包接收处理函数dm9000_interrupt,网卡接收到数据报文后触发dm9000_interrupt,进而一路调用到netif_rx:
dm9000_interrupt() -> dm9000_rx() -> netif_rx() 。
这里要特别强调一下,dm9000_interrupt是会关闭中断的,所以,这里处理要越快越好,在调用到 netif_rx()时,会把报文存到一个queue中(queue的长度可以用cat /proc/sys/net/core/netdev_max_backlog查看),函数就返回了,报文的处理交给下半段处理。
这里需要特别说明的是,在函数enqueue_to_backlog中,如果queue本来长度为0,netif_rx会调用napi_schedule,进而调用__raise_softirq_irqoff(NET_RX_SOFTIRQ),这里触发了NET_RX_SOFTIRQ软中断,软中断的执行就是下半段执行了;如果queue 长度不为0,直接把报文挂到queue中即可(__skb_queue_tail),因为下半段那边原来就在运行,这边硬中断返回后,软中断会把新加入的报文继续处理完成的。
这个帖子的作者,没有用linux系统,自己在中断里面处理了报文的所有处理流程,导致中断时间过长,丢包不断。(后续他也用了类似上下半段的处理方式解决此问题)。
详细过程:
进入处理程序后,首先从物理设备中将数据分组拷贝到内存中,然后分组是否合法,之后分配一个skb组包,然后调用netif_rx(skb)将包放入到softnet_data的input_queue中。
netif_rx有两个返回值:NET_RX_SUCCESS和NET_RX_DROP。input_pkt_queue的类型是sk_buff_head。注意到该数据结构并没有使用内核常用的list_head作为保存skb的链表,用的是sk_buff_head结构体。softnet_data使用该数据结构存储与收包队列相关的元素,如skb、队列大小、lock等。如果使用list_head,那么必然要将qlen和lock放到softnet_data中,不够整洁。所以如果qlen比netdev_max_backlog还要大的时候,就会直接丢弃该包。netdev_max_backlog的值默认为1000,可以在/proc/sys/net/core/ netdev_max_backlog中设置。
上半部的实现
接收数据包的上半部处理流程为:
dm9000_interrupt() // 网卡驱动
|--> dm9000_rx() // 网卡驱动
|--> netif_rx() // 内核接口
|--> enqueue_to_backlog() // 内核接口
这里问题来了,存在queue中的报文,是由谁来做下半段的处理呢?——我们留在下篇文章分析。
2)关于ndo_start_xmit的说明:
网络协议栈决定发送数据时,调用驱动中的.ndo_start_xmit(skb,ndev)发送函数向硬件发送数据
ndo_start_xmit(mini2440中是dm9000_start_xmit)通过内核api——netif_stop_queue (ndev)禁止网络协议栈再度调用ndo_start_xmit
ndo_start_xmit向硬件提交数据后立即返回
硬件在完成发送后,以中断的方式通知OS,中断服务程序通过内核api——netif_wake_queue (ndev)通知协议栈可再度调用ndo_start_xmit
参考:
https://blog.csdn.net/zhangskd/article/details/22079509
阅读(2736) | 评论(0) | 转发(0) |