Linux内核中网络数据的接收

1. Linux网络数据的接收始于中断,本文用wifi驱动ath10k进行分析,中断处理函数为
ath10k_pci_interrupt_handler,那么中断处理函数是如何与网卡关联?要了解这个
问题就得了解pcie接口网卡的注册流程:
->ath10k_pci_init drivers/net/wireless/ath/ath10k/pci.c
->ath10k_pci_probe
->ath10k_pci_request_irq(ar); 
->ath10k_pci_request_irq_msi(ar);
->request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, ...);
至此我们将中断处理函数ath10k_pci_interrupt_handler和网卡的device结构体关联起来了。

2. 当接收数据唤醒网络中断时,中断处理函数ath10k_pci_interrupt_handler做了些什么事情?
1. 唤醒pcie设备,ath10k_pci_force_wake(ar);

2. 关pcie中断,ath10k_pci_irq_msi_fw_mask(ar),关中断的作用是为了使用NAPI polling
数据。为什么使用NAPI呢?在NAPI之前网络数据的接收模式有两种, polling方式和中
断方式, 如果只使用中断模式的话,在数据量比较大的情况下, CPU不停的进出中断,
基本上无法响应其他进程了。这种情况下使用polling模式比较好,但是polling模式不
适合数据量低的情况。所以NAPI应运而生,在高数据率的情况下使用polling模式,低数
据量的时候使用中断模式。

3. 触发软中断用来接收数据, napi_schedule(&ar->napi);
->__napi_schedule() net/core/dev.c
->__raise_softirq_irqoff(NET_RX_SOFTIRQ); kernel/softirq.c
->or_softirq_pending(1UL << nr);

3.  软中断的action的触发过程? 我们知道在2.3中设置软中断,那么软中断是如何触发的。
通过对GIC framework的分析,我们知道任何硬件中断都会触发软中断。分析过程如下:
gic_handle_irq为gic的isr, 任何一个硬件中断都会调用gic_handle_irq:
->gic_handle_irq drivers/irqchip/irq-gic.c
->handle_domain_irq
->irq_enter:arch/arm/kernel/smp.c
->irq_exit():kernel/softirq.c 
->invoke_softirq();
->__do_softirq  include/linux/interrupt.h
->while ((softirq_bit = ffs(pending)))  h->action(h);
->net_rx_action

4. 为什么会调用的net_rx_action()?而不是其他的软中断处理函数?这个就得从network
framework分析了,网络驱动在net/core/dev.c指定了net的rx和tx对应的action。 
net_dev_init() net/core/dev.c
->open_softirq(NET_TX_SOFTIRQ, net_tx_action);
->open_softirq(NET_RX_SOFTIRQ, net_rx_action);
->softirq_vec[nr].action = action;  softirq_vec[nr].action = action;
而在上文中,我们知道__napi_schedule()会设置软件中断的flag,
or_softirq_pending(1UL << nr),arm在响应gic_handle_irq()时发现net_rx_action()
对应的flag置位,因此会触发net_rx_action()


5. net_rx_action()是怎么实现接受网络数据的? 具体的code在net/core/dev.c中: 
1. 每个CPU都有一个softnet_data, softnet_data有结构体成员poll_list,
____napi_schedule的时候将napi->poll_list,挂在sd->poll_list上, code为
list_add_tail(&napi->poll_list, &sd->poll_list);

2.  遍历sd->poll_list, sd->poll_list的一个节点代表一个网卡设备,每遍历一个节点,
取出节点的内容赋值给napi_struct结构体, 并调用对应的poll函数
for (;;) 
{
struct napi_struct *n;

if (list_empty(&list)) 
{
if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
return;

break;
}

n = list_first_entry(&list, struct napi_struct, poll_list);

budget -= napi_poll(n, &repoll);

if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) {
sd->time_squeeze++;
break;
}
        }



3. 如果满足以下两个条件,则打破循环5.2中的循环, 否则继续循环知道sd->poll_list为空
case1:budget <= 0
case2:time_after_eq(jiffies, time_limit)

4. 如果还有节点就表示当前CPU还有未接收完的数据,继续设置软中断接受数据
if (!list_empty(&sd->poll_list))
__raise_softirq_irqoff(NET_RX_SOFTIRQ);

6. 从5.2可知接受数据的主要函数为napi_poll(n, &repoll),而napi_poll()的关键点为n->poll(n, weight),
所以这里需要分析n->poll()。带着这个问题又得朔本追源,分析wifi的probe过程:
ath10k_pci_probe drivers/net/wireless/ath/ath10k/pci.c
->ath10k_pci_init_irq
->ath10k_pci_init_napi(ar);
->netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll, ATH10K_NAPI_BUDGET);
->napi->poll = ath10k_pci_napi_poll;

7. 由6可知napi->poll的具体实现为ath10k_pci_napi_poll:
ath10k_pci_napi_poll drivers/net/wireless/ath/ath10k/pci.c
{
1.  调用wifi driver的数据接收接口读取packet
->done = ath10k_htt_txrx_compl_task(ar, budget);

2. 如果done的数量小于budget, 则说明当前网卡已经没有多余的packet了,接收过程完成
{
1. napi_complete(ctx), 初始化当前网卡的n->poll_list, 清NAPI_STATE_SCHED
->list_del_init(&n->poll_list);
->clear_bit(NAPI_STATE_SCHED, &n->state);

2. 使能pcie中断, 用中断来监听网卡的接收数据
->ath10k_pci_enable_legacy_irq(ar);
                ->ath10k_pci_irq_msi_fw_unmask(ar);
}
}

8. wifi驱动的接收函数ath10k_htt_txrx_compl_task(ar, budget),刚开始看到这个函数感觉好复杂,
但是想要弄懂wifi是如何接收数据的, 还得啃这个硬骨头:
1 wifi驱动层:
ath10k_htt_rx_handle_amsdu drivers/net/wireless/ath/ath10k/htt_rx.c 
->ath10k_htt_rx_h_deliver
->ath10k_process_rx
->ieee80211_rx_napi

2 mac层:
__ieee80211_rx_handle_packet net/mac80211/rx.c
->ieee80211_prepare_and_rx_handle(&rx, skb, false);  
->ieee80211_invoke_rx_handlers(rx);
->ieee80211_rx_handlers(rx, &reorder_release);
->CALL_RXH(ieee80211_rx_h_data);
->ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
->ieee80211_deliver_skb(rx);
->netif_receive_skb(skb);

3. netcore层
netif_receive_skb_internal(skb) net/core/dev.c
->__netif_receive_skb(skb)
->__netif_receive_skb_core(skb, true)
{
struct packet_type *ptype, *pt_prev;

type = skb->protocol; 获取ethernet数据包的类型
deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type, &ptype_base[ntohs(type) & PTYPE_HASH_MASK]);
{
struct packet_type *ptype, *pt_prev = *pt;

由下文中第4步可知ptype_list[]中的成员有arp_packet_type和ip_packet_type,对应的ptype->type
的类型为ETH_P_ARP和ETH_P_IP, 此处根据skb->protocol来查找对应的ptype。
list_for_each_entry_rcu(ptype, ptype_list, list) 
{
if (ptype->type != type)
continue;
if (pt_prev)

                                                deliver_skb(skb, pt_prev, orig_dev);

pt_prev = ptype;
}

*pt = pt_prev;
}
}

->deliver_skb(skb, pt_prev, orig_dev)
->pt_prev->func(skb, skb->dev, pt_prev, orig_dev)

想要了解pt_prev->func就得分析i网络对应的initcall,inet_init() 
inet_init() net/ipv4/af_inet.c
{
arp_init()
{
static struct packet_type arp_packet_type = 
{
.type = cpu_to_be16(ETH_P_ARP),
.func = arp_rcv,
};

dev_add_pack(&arp_packet_type);
{
struct list_head *head = ptype_head(arp_packet_type);
list_add_rcu(&pt->list, head);
->ptype_base[ETH_P_ARP] = arp_packet_type;
}

全局数组ptype_base[]的第ETH_P_ARP个成员为arp_packet_type
}

dev_add_pack(&ip_packet_type)
{
static struct packet_type ip_packet_type  = 
{
.type = cpu_to_be16(ETH_P_IP),
.func = ip_rcv,
};

全局数组ptype_base[]的第ETH_P_IP个成员为ip_packet_type
}
}

5. IP/ARP协议层,对应的接收API为ip_rcv/arp_rcv, 这里主要分析ip_rcv
ip_rcv() net/ipv4/ip_input.c
->ip_rcv_finish
{
if (!skb_valid_dst(skb))
{
ip_route_input_noref(skb, iph->daddr, iph->saddr, iph->tos, dev);
ip_route_input_slow(skb, daddr, saddr, tos, dev);
{
struct rtable *rth = rt_dst_alloc(l3mdev_master_dev_rcu(dev) ? : net->loopback_dev,
flags | RTCF_LOCAL, res.type,
IN_DEV_CONF_GET(in_dev, NOPOLICY), false, do_cache);
{
...............
rt->dst.input = ip_local_deliver;
}


skb_dst_set(skb, &rth->dst);
{
skb->_skb_refdst = (unsigned long)dst;
}
}
以上code中分析到的两个结论分别为skb->_skb_refdst = dst,rt->dst.input = ip_local_deliver;
一下code会用到这里的两个结论。

dst_input(skb);
skb_dst(skb)->input(skb); include/net/dst.h
ip_local_deliver net/ipv4/route.c
{
ip_local_deliver_finish net/ipv4/ip_input.c 
ipprot = rcu_dereference(inet_protos[protocol]);
ipprot->handler(skb);
tcp_v4_rcv(skb);


ip_local_deliver最终调到的协议处理函数为ipprot->handler(), 那么ipprot又是在哪里定义的呢?
带着这个问题再来分析fs_initcall(inet_init) net/ipv4/af_inet.c 
{
inet_add_protocol(&icmp_protocol, IPPROTO_ICMP);
inet_add_protocol(&udp_protocol, IPPROTO_UDP);
inet_add_protocol(&tcp_protocol, IPPROTO_TCP);
inet_add_protocol(&igmp_protocol, IPPROTO_IGMP)
{
inet_protos[IPPROTO_IGMP] = igmp_protocol;
}
}

上文中的ipprot = inet_protos[protocol],可见ipprot是和协议类型protocol有关系,如果protocol
为IPPROTO_TCP,则对应的ipprot为tcp_protocol,tcp_protocol的数据结构如下:

static const struct net_protocol tcp_protocol = 
{
.early_demux    =       tcp_v4_early_demux,
.handler        =       tcp_v4_rcv,
.err_handler    =       tcp_v4_err,
.no_policy      =       1,
.netns_ok       =       1,
.icmp_strict_tag_validation = 1,
};
}

6. TCP层,tcp_v4_rcv(skb)
{
tcp_v4_do_rcv(sk, skb);
tcp_rcv_established() net/ipv4/tcp_input.c
{
slow path:
tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT)
tcp_data_queue(sk, skb)
{
tcp_queue_rcv()
__skb_queue_tail(&sk->sk_receive_queue, skb);
最终tcp_v4_rcv会将将skb放入&sk->sk_receive_queue中,sys_recvmsg会从sk_receive_queue取packet。
}

tcp_data_snd_check(sk);
tcp_ack_snd_check(sk);
}
}

9. 应用层是如何获取TCP/UDP层的packet。
recv(sockfd, buff, sizeof(buff), 0);
SYSCALL_DEFINE3(recvmsg, int, fd, ...) net/socket.c
___sys_recvmsg
->sock_recvmsg
->sock_recvmsg_nosec
->sock->ops->recvmsg
{
inet_create() net/ipv4/af_inet.c
{
1. 根据protocol参数在inetsw[sock->type]数组中查找对应的inet_protosw, protocol参数是由sock = socket()
中的第二个参数, 参数类型有SOCK_STREAM,SOCK_DGRAM,SOCK_RAW。对应的code为: 
list_for_each_entry_rcu(answer, &inetsw[sock->type], list)
if (protocol == answer->protocol)
break;

这里出现inetsw如下,代表了IPV4不同的协议簇, 对应的文件为net/ipv4/af_inet.c中
static struct inet_protosw inetsw_array[] =  
{
{
.type SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot =     &tcp_prot,
.ops =      &inet_stream_ops,
},
{
.type =     SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot =     &udp_prot,
.ops =      &inet_dgram_ops,
},
..............................
}

2. sock->ops = answer->ops = inet_stream_ops;

3. sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);
sk->sk_prot = answer->prot = tcp_prot;
}
}
}
有inet_create()的分析可知sock->ops=inet_stream_ops,sk->sk_prot = tcp_prot
->inet_recvmsg()
->sk->sk_prot->recvmsg()
->tcp_recvmsg() net/ipv4/tcp.c
{
遍历sk->sk_receive_queue, 我们在上文分析过tcp_v4_rcv()会将接收到的skb放入sk->sk_receive_queue中
skb_queue_walk(&sk->sk_receive_queue, skb)  
...............
}

以上过程可归纳为:
ath10k_pci_interrupt_handler drivers/net/wireless/ath/ath10k/pci.c
__napi_schedule() net/core/dev.c
__raise_softirq_irqoff(NET_RX_SOFTIRQ); kernel/softirq.c
net_rx_action net/core/dev.c 
napi_poll(n, &repoll); net/core/dev.c
ath10k_pci_napi_poll drivers/net/wireless/ath/ath10k/pci.c
done = ath10k_htt_txrx_compl_task(ar, budget); drivers/net/wireless/ath/ath10k/pci.c
ath10k_htt_rx_in_ord_ind(ar, skb); drivers/net/wireless/ath/ath10k/htt_rx.c
ath10k_htt_rx_h_deliver(ar, &amsdu, status); drivers/net/wireless/ath/ath10k/htt_rx.c
ath10k_process_rx(ar, status, msdu); drivers/net/wireless/ath/ath10k/htt_rx.c
ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi); drivers/net/wireless/ath/ath10k/htt_rx.c
__ieee80211_rx_handle_packet(hw, pubsta, skb, napi); net/mac80211/rx.c
ieee80211_prepare_and_rx_handle(&rx, skb, false); net/mac80211/rx.c 
ieee80211_invoke_rx_handlers(rx); net/mac80211/rx.c
ieee80211_rx_handlers(rx, &reorder_release); net/mac80211/rx.c 
CALL_RXH(ieee80211_rx_h_data); net/mac80211/rx.c
ieee80211_rx_h_data(struct ieee80211_rx_data *rx) net/mac80211/rx.c
ieee80211_deliver_skb(rx); net/mac80211/rx.c
netif_receive_skb(skb); net/mac80211/rx.c 
netif_receive_skb_internal(skb); net/core/dev.c
__netif_receive_skb(skb); net/core/dev.c
__netif_receive_skb_core(skb, true); net/core/dev.c
deliver_skb(skb, pt_prev, orig_dev); net/core/dev.c
pt_prev->func(skb, skb->dev, pt_prev, orig_dev); net/core/dev.c

你可能感兴趣的:(Linux)