Thursday, January 27, 2011

網路Driver 4 - Linux内核网络源码分析——接收数据

Linux内核网络源码分析——接收数据

目录:Linux内核网络源码分析
概述
接收数据分为从上往下和从下往上的两个过程
从上往下:
1、用户空间中调用read接收数据,此系统调用在内核中的函数是sys_read(fs/read_write.c)。
2、sys_read最终会调用skb_recv_datagram:
§ 如果数据还没有到达,且设置socket设为阻塞模式时,进程挂起signal_pending(current)。
§ 如果数据已到达,或data_ready通知进程资源得到满足后继续处理 (wake_up_interruptible(sk->sleep);)进入第3步
3、INET Socket接收数据
从下往上:
1、数据到达网卡,产生一个硬件中断,由网卡驱动程序处理中断。
2、网卡驱动程序将收到的数据封装成struct sk_buff,然后把它放入接收队列,并置NET_TX_SOFTIRQ软中断标志位等待CPU调度。
3、CPU进程调度schedule()检测到软中断NET_RX_SOFTIRQ,调用相应处理函数net_tx_action(该函数在中net_dev_init注册)
4、net_tx_action开始处理sk_buff,经过网络层(IP)和传输层(TCP/UDP)处理,针对发往本机的数据包找到相应的INET Socket并放入其接收队列,设置data_ready唤醒上层接收进程。





接收数据处理流程
—————————————–硬件中断处理
rtl8139_interrup
…NAPIPoll机制
—————————————–软中断处理
net_rx_action
rtl8139_poll
rt18139_rx
netif_recive_skb
—————————————–网络层处理
ip_rcv
<NF_INET_PRE_ROUTING>
ip_rcv_finish
ip_local_deliver
<NF_INET_LOCAL_IN>
ip_local_deliver_finish
—————————————–传输层处理
UDP
udp_rcv
__udp4_lib_rcv
udp_queue_rcv_skb
__udp_queue_rcv_skb
sock_queue_rcv_skb
skb_queue_tail
__skb_queue_tail
sk->sk_data_ready
TCP
tcp_v4_rcv
tcp_v4_do_rcv
tcp_rcv_established
__skb_queue_tail
sk->sk_data_ready

详细流程分析


数据包到达网卡,产生硬件中断
———————————
硬件中断处理:

rtl8139_interrupt() <8139too.c>

/* Receive packets are processed by poll routine.
If not running start it now. */
if (status & RxAckBits){
if (netif_rx_schedule_prep(dev, &tp->napi)) {
RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
__netif_rx_schedule(dev, &tp->napi);
}
}
netif_rx_schedule() <include/linux/netdevice.h>
// 这 个函数被中断服务程序调用,将设备的POLL方法添加到网络层次的POLL处理队列中去,
// 排队并且准备接收数据包,在使用之前需要调用 netif_rx_reschedule_prep,并且返回的数为 1,
// 并且触发一个NET_RX_SOFTIRQ 的软中断通知网络层接收数据包。

/* Add interface to tail of rx poll list. This assumes that _prep has
* already been called and returned 1.
*/
static inline void __netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
__napi_schedule(napi);
}
/* Try to reschedule poll. Called by irq handler. */
static inline void netif_rx_schedule(struct net_device *dev,
struct napi_struct *napi)
{
if (netif_rx_schedule_prep(dev, napi))
__netif_rx_schedule(dev, napi);
}
netif_rx_schedule_prep() <include/linux/netdevice.h>
// 确定设备处于运行,而且设备还没有被添加到网络层的POLL处理队列中。
// 在调用 netif_rx_schedule之前会调用这个函数。

/* Test if receive needs to be scheduled but only if up */
static inline int netif_rx_schedule_prep(struct net_device *dev,
struct napi_struct *napi)
{
return napi_schedule_prep(napi);
}
netif_rx_ni() <net/core/dev.c>
int netif_rx_ni(struct sk_buff *skb)

preempt_disable();// 禁止当前进程被抢占
err = netif_rx(skb);
if (local_softirq_pending()) // 检测本地处理器是否有需要处理的softirq
do_softirq();// 处理软中断
preempt_enable();
netif_rx() <net/core/dev.c>
netif_rx() <net/core/dev.c>
  1. int netif_rx(struct sk_buff *skb)   
  2. //该函数从设备驱动程序中接收数据包,并添加到上层协议的处理队列  
  3. //所有接收到的数据包都会在这里被处理发往上层  
  4. //在拥塞控制或协议层再中来决定是接收数据或将数据包丢弃  
  5. …  
  6. local_irq_save(flags);//在保存当前中断状态到 flags 之后禁止当前处理器的中断  
  7. queue = &__get_cpu_var(softnet_data);  
  8.   
  9. __get_cpu_var(netdev_rx_stat).total++;  
  10. if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {//判断接收队列是否已满  
  11.     if (queue->input_pkt_queue.qlen) {//接收队列未满,继续判断接收队列中是否为空  
  12. enqueue:  
  13.         //接收队列非空,即NET_RX_SOFTIRQ已设置  
  14.         //不必设置软中断,直接将数据包入队  
  15.         dev_hold(skb->dev);//Hold reference to device to keep it from being freed.  
  16.         __skb_queue_tail(&queue->input_pkt_queue, skb);  
  17.         local_irq_restore(flags);  
  18.         return NET_RX_SUCCESS;  
  19.     }  
  20.     //若接收队列为空,则使用NAPI设置NET_RX_SOFTIRQ软中断  
  21.     napi_schedule(&queue->backlog);  
  22.     goto enqueue;  
  23. }  
  24. //接收队列已满,数据包将被遗弃  
  25. __get_cpu_var(netdev_rx_stat).dropped++;//统计丢弃数据包数量  
  26. local_irq_restore(flags);//恢复中断  
  27. …  
__napi_schedule() <net/core/dev.c>
__napi_schedule() <net/core/dev.c>
  1. /** 
  2.  * __napi_schedule - schedule for receive 
  3.  * @n: entry to schedule 
  4.  * 
  5.  * The entry’s receive function will be scheduled to run 
  6.  */  
  7. void __napi_schedule(struct napi_struct *n)  
  8. {  
  9.     unsigned long flags;  
  10.   
  11.     local_irq_save(flags);  
  12.     list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);  
  13.     __raise_softirq_irqoff(NET_RX_SOFTIRQ);  
  14.     local_irq_restore(flags);  
  15. }  
—————————————–
软中断处理:
net_rx_action() <net/core/dev.c>
static void net_rx_action(struct softirq_action *h)

if (test_bit(NAPI_STATE_SCHED, &n->state))
work = n->poll(n, weight);
// 调用NIC设备注册的poll函数获取数据,
// 对于RTL8139网卡即rtl8139_poll(该函数在中rtl8139_init_onece注册)

rtl8139_poll() <drivers/net/8139too.c>
static int rtl8139_poll(struct napi_struct *napi, int budget)

if (likely(RTL_R16(IntrStatus) & RxAckBits))
work_done += rtl8139_rx(dev, tp, budget);
rt18139_rx() <drivers/net/8139too.c>
rt1839_rx() <drivers/net/8139too.c>
  1. static int rtl8139_rx(struct net_device *dev, 
  2.                 struct rtl8139_private *tp,int budget)  
  3. //从rx_ring中接收数据,构造sk_buff并发往上层  
  4. …  
  5. //为分配sk_buff空间,准备保存接收的数据包 
  6. skb = dev_alloc_skb (pkt_size + 2);
  7. if (likely(skb)) {  
  8.     skb_reserve (skb, 2);   /* 16 byte align the IP fields. */  
  9. #if RX_BUF_IDX == 3  
  10.     wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);  
  11. #else
        //从rx_ring中复制数据
  12.     skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size); 
  13. #endif  
  14.     skb_put (skb, pkt_size);//调整skb->tial的位置  
  15.   
  16.     skb->protocol = eth_type_trans (skb, dev);  
  17.   
  18.     dev->last_rx = jiffies;  
  19.     tp->stats.rx_bytes += pkt_size;  
  20.     tp->stats.rx_packets++;  
  21.   
  22.     netif_receive_skb (skb);//将收到的数据包发往上层  
  23. else {  
  24.     if (net_ratelimit())  
  25.         printk (KERN_WARNING  
  26.             "%s: Memory squeeze, dropping packet.\n",  
  27.             dev->name);  
  28.     tp->stats.rx_dropped++;  
  29. }  
  30. …  
netif_recive_skb() <net/core/dev.c>
// ptype_all 链用于监听从网络设备上接收的所有包,通常不使用它。
// ptype_base hash表是被协议标识符弄乱的,用于决定哪个协议将接收传入的网络包。

netif_recive_skb() <net/core/dev.c>
  1. int netif_receive_skb(struct sk_buff *skb)  
  2. …  
  3. skb_reset_network_header(skb);  
  4. skb_reset_transport_header(skb);  
  5. skb->mac_len = skb->network_header - skb->mac_header;  
  6. …  
  7. //遍历ptype_all中绑定的协议处理数据包  
  8. list_for_each_entry_rcu(ptype, &ptype_all, list) {  
  9.     if (!ptype->dev || ptype->dev == skb->dev) {  
  10.         if (pt_prev)  
  11.             ret = deliver_skb(skb, pt_prev, orig_dev);//处理数据包  
  12.         pt_prev = ptype;  
  13.     }  
  14. }  
  15. …  
  16. //遍历ptype_base[ntohs(type) & PTYPE_HASH_MASK]中绑定的协议处理数据包  
  17. type = skb->protocol;  
  18. list_for_each_entry_rcu(ptype,  
  19.         &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {  
  20.     if (ptype->type == type &&  
  21.         (!ptype->dev || ptype->dev == skb->dev)) {  
  22.         if (pt_prev)  
  23.             ret = deliver_skb(skb, pt_prev, orig_dev);//处理数据包  
  24.         pt_prev = ptype;  
  25.     }  
  26. }  
  27. if (pt_prev) {  
  28.     ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);//处理数据包  
  29. else {  
  30.     //没有找到任何上层协议处理数据包,则将数据包丢弃  
  31.     kfree_skb(skb);  
  32.     /* Jamal, now you will not able to escape explaining 
  33.      * me how you were going to use this. :-) 
  34.      */  
  35.     ret = NET_RX_Drop;  
  36. }  
deliver_skb() <net/core/dev.c>
deliver_skb() <net/core/dev.c>
  1. static inline int deliver_skb(struct sk_buff *skb,  
  2.                   struct packet_type *pt_prev,  
  3.                   struct net_device *orig_dev)  
  4. {  
  5.     atomic_inc(&skb->users);//skb->user中登记一个用户  
  6.     return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);  
  7.     //调用上层协议的处理函数,对于IP协议数据包,则pt_prev->func注册为ip_rcv()  
  8. }  
ip_rcv() <net/ipv4/ip_input.c>
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
// 对包进行基本校验:IP目标、完整性、CSUM等
//  * 1. Length at least the size of an ip header
// * 2. Version of 4
// * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
// * 4. Doesn’t have a bogus length
// 然后数据包穿过Netfilter传到达ip_rcv_finish


return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);
ip_rcv_finish() <net/ipv4/ip_input.c>

if (skb->dst == NULL) {
int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
// 查找路由,将路由信息写到skb->dst中,
// 如果是发往本地的数据包则skb->dst->input函数注册为ip_local_deliver处理。


return dst_input(skb);// 根据路由信息处理数据包
dst_input() <include/net/dst.h>
static inline int dst_input(struct sk_buff *skb)
/* Input packet from network to transport. */

err = skb->dst->input(skb);
ip_local_deliver() <net/ipv4/ip_input.c>
// 将IP数据包发往上层协议,数据包穿过Netfilter传到达ip_rcv_finish
ip_local_deliver() <net/ipv4/ip_input.c>
  1.     /* 
  2.      *  Deliver IP Packets to the higher protocol layers. 
  3.      */  
  4.     int ip_local_deliver(struct sk_buff *skb)  
  5.     {  
  6.         /* 
  7.          *  Reassemble IP fragments. 
  8.          */  
  9.       
  10.         if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {  
  11.             if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))  
  12.                 return 0;  
  13.         }  
  14.       
  15.         return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,  
  16.                    ip_local_deliver_finish);  
  17.   
  18. }  
ip_local_deliver_finish() <net/ipv4/ip_input.c>
ip_local_deliver_finish() <net/ipv4/ip_input.c>
  1. static int ip_local_deliver_finish(struct sk_buff *skb)  
  2. {  
  3.     struct net *net = dev_net(skb->dev);  
  4.   
  5.     __skb_pull(skb, ip_hdrlen(skb));  
  6.   
  7.     /* Point into the IP datagram, just past the header. */  
  8.     skb_reset_transport_header(skb);  
  9.   
  10.     rcu_read_lock();  
  11.     {  
  12.         int protocol = ip_hdr(skb)->protocol;  
  13.         int hash, raw;  
  14.         struct net_protocol *ipprot;  
  15.   
  16.     resubmit:  
  17.         raw = raw_local_deliver(skb, protocol);//原始套接字接收数据  
  18.   
  19.         hash = protocol & (MAX_INET_PROTOS - 1);  
  20.         ipprot = rcu_dereference(inet_protos[hash]);//获取协议处理结构  
  21.         if (ipprot != NULL && (net == &init_net || ipprot->netns_ok)) {  
  22.             int ret;  
  23.   
  24.             if (!ipprot->no_policy) {  
  25.                 // 对转发的数据包进行安全策略检查, 检查失败的话丢包  
  26.                 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {  
  27.                     kfree_skb(skb);  
  28.                     goto out;  
  29.                 }  
  30.                 nf_reset(skb);  
  31.             }  
  32.             ret = ipprot->handler(skb);//调用相应的处理函数  
  33.             if (ret < 0) {  
  34.                 protocol = -ret;//?  
  35.                 goto resubmit;  
  36.             }  
  37.             IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);  
  38.         } else {  
  39.             if (!raw) {  
  40.                 if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {  
  41.                     IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);  
  42.                     icmp_send(skb, ICMP_DEST_UNREACH,  
  43.                           ICMP_PROT_UNREACH, 0);  
  44.                 }  
  45.             } else  
  46.                 IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);  
  47.             kfree_skb(skb);  
  48.         }  
  49.     }  
  50.  out:  
  51.     rcu_read_unlock();  
  52.   
  53.     return 0;  
  54. }  


1 comment: