上文分析到了linux内核接收流程将数据传到了__netif_receive_skb里,由此开始进入第二站,内核协议栈的网络协议接口层。
__netif_receive_skb函数定义如下:
static int __netif_receive_skb(struct sk_buff *skb)
{
int ret;
if (sk_memalloc_socks() && skb_pfmemalloc(skb)) {//if判断内容见下方解析
unsigned int noreclaim_flag;
/*
* PFMEMALLOC skbs are special, they should
* - be delivered to SOCK_MEMALLOC sockets only
* - stay away from userspace
* - have bounded memory usage
*
* Use PF_MEMALLOC as this saves us from propagating the allocation
* context down to all allocation sites.
*/
noreclaim_flag = memalloc_noreclaim_save();//将当前进程置上PF_MEMALLOC标识,表示允许使用reserved内存不用考虑水位问题
ret = __netif_receive_skb_one_core(skb, true);
memalloc_noreclaim_restore(noreclaim_flag);//撤销当前进程的PF_MEMALLOC标识
} else
ret = __netif_receive_skb_one_core(skb, false);
return ret;
}
先看if条件红的第一个函数sk_memalloc_socks,此函数判断memalloc_socks_key是否为0,如果为0 函数返回0,如果为1 函数返回1(不太可能的情况)。
#ifdef CONFIG_NET
DECLARE_STATIC_KEY_FALSE(memalloc_socks_key);//初始值为0
static inline int sk_memalloc_socks(void)
{
return static_branch_unlikely(&memalloc_socks_key);
}
#else
static inline int sk_memalloc_socks(void)
{
return 0;
}
#endif
memalloc_socks_key的功能可以从它被使用的场景分析出来,它的值在下面两个函数内被加减:
void sk_set_memalloc(struct sock *sk)
{
sock_set_flag(sk, SOCK_MEMALLOC);//在socket被设置了SOCK_MEMALLOC标志后key值加一
sk->sk_allocation |= __GFP_MEMALLOC;//设置后,此socket申请的skb会设置GFP_MEMALLOC标识
static_branch_inc(&memalloc_socks_key);//设置GFP_MEMALLOC后,skb可使用系统reserved的内存。
}
EXPORT_SYMBOL_GPL(sk_set_memalloc);
void sk_clear_memalloc(struct sock *sk)
{
sock_reset_flag(sk, SOCK_MEMALLOC);
sk->sk_allocation &= ~__GFP_MEMALLOC;
static_branch_dec(&memalloc_socks_key);//在socket连接清理缓存时key减一
sk_mem_reclaim(sk);//会对socket占用的内存进行缓存回收操作
}
EXPORT_SYMBOL_GPL(sk_clear_memalloc);
由上方的操作代码,可知memalloc_socks_key代表着当前系统内有权使用reserved内存区的socket连接数。
来看if判断条件涉及到的另一个函数skb_pfmemalloc:
static inline bool skb_pfmemalloc(const struct sk_buff *skb)//判断skb是否被置上了PFMEMALLOC标识,置上了返回true,未置上返回false
{
return unlikely(skb->pfmemalloc);//置上PFMEMALLOC代表着可使用系统保留的紧急内存部分。
}
所以__netif_receive_skb内的if判断的是当前socket连接是否拥有能使用系统保留区内存的权利并且当前skb使用了系统保留内存,因为置上了PFMEMALLOC的skb只能在设置了SOCK_MEMALLOC的socket连接上传递。
继续跟踪代码,__netif_receive_skb-> __netif_receive_skb_one_core
static int __netif_receive_skb_one_core(struct sk_buff *skb, bool pfmemalloc)
{
struct net_device *orig_dev = skb->dev;
struct packet_type *pt_prev = NULL;
int ret;
ret = __netif_receive_skb_core(skb, pfmemalloc, &pt_prev);//将pt_prev指针地址传进去
if (pt_prev)//如果__netif_receive_skb_core内对pt_prev指针赋了非空的值
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);//调用pt_prev->func,对应的是skb协议的处理函数
return ret;
}
__netif_receive_skb-> __netif_receive_skb_one_core -> __netif_receive_skb_core
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc,
struct packet_type **ppt_prev)
{
struct packet_type *ptype, *pt_prev;
rx_handler_func_t *rx_handler;
struct net_device *orig_dev;
bool deliver_exact = false;
int ret = NET_RX_DROP;
__be16 type;
net_timestamp_check(!netdev_tstamp_prequeue, skb);
trace_netif_receive_skb(skb);
orig_dev = skb->dev;//将接收此skb的设备指针记录到orig_dev
skb_reset_network_header(skb);
if (!skb_transport_header_was_set(skb))//skb传输层header是否有置好位置
skb_reset_transport_header(skb);
skb_reset_mac_len(skb);//这部分没有看懂为什么收上来的skb要reset一遍各层头部。
pt_prev = NULL;//此函数内创建的指针pt_prev初始为NULL
another_round:
skb->skb_iif = skb->dev->ifindex;
__this_cpu_inc(softnet_data.processed);//增加当前cpu处理包的计数
... ...
list_for_each_entry_rcu(ptype, &ptype_all, list) {//这里遍历ptype_all链表的每个对象,关于ptype_all见下方讲解链接
if (pt_prev)//第一次走到此句,pt_prev为空,直接跳过此if,pt_prev被ptype赋值,第二次及往后才会进入下方if流程
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
//到此处时,pt_prev指向的是上个循环ptype_all的最后一个对象,无论后文是进第二个list循环或是goto out或是rx_handler,都会将此包通过pt_prev递给上层
list_for_each_entry_rcu(ptype, &skb->dev->ptype_all, list) {//这里不太理解dev->ptype_all与ptype_all有什么差别,好像是和tcpdump抓包配置相关,不太确定
if (pt_prev)//第一次走到此句,将尚未递交的pt_prev送入deliver_skb
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;//这里将pt_prev置为skb->dev->ptype_all的第一个对象
}
skip_taps:
#ifdef CONFIG_NET_INGRESS//ingress流控相关,ingress是针对输入数据进行流控处理的
... ...
#endif
skb_reset_tc(skb);
skip_classify:
if (pfmemalloc && !skb_pfmemalloc_protocol(skb))//pfmemalloc标识与skb的memalloc必须同时置true才说明支持使用reserved内存
goto drop;
... ...//vlan相关
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (rx_handler) {//有的模块(例如网桥)会注册rx_handler,注册就在此调用
if (pt_prev) {
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
switch (rx_handler(&skb)) {
case RX_HANDLER_CONSUMED:
ret = NET_RX_SUCCESS;
goto out;
case RX_HANDLER_ANOTHER:
goto another_round;
case RX_HANDLER_EXACT:
deliver_exact = true;
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
... ...//vlan相关
type = skb->protocol;
/* deliver only exact match when indicated */
if (likely(!deliver_exact)) {//初始化为false,只有函数上方的rx_handler里的RX_HANDLER_EXACT情况下才会为true
deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type, &ptype_base[ntohs(type) & PTYPE_HASH_MASK]);//deliver_exact为false代表不需要向指定设备发送,则向ptype_base内的当前skb所属协议传递此包。
}
deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type, &orig_dev->ptype_specific);//对当前设备指定的ptype特定协议传递此包
if (unlikely(skb->dev != orig_dev)) {//orig_dev初始化成skb->dev,所以这两个值不相等是不太可能出现的情况
deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
&skb->dev->ptype_specific);
}
if (pt_prev) {
if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC)))//skb_orphan_frags_rx没太看懂函数为了做啥
goto drop;
*ppt_prev = pt_prev;
} else {
drop:
if (!deliver_exact)//如果deliver_exact为false时,且pt_prev为NULL,说明当前skb没有调用deliver_skb传递给上层
atomic_long_inc(&skb->dev->rx_dropped);//那么此skb将被丢掉
else//deliver_exact为true时,且pt_prev为NULL,说明当前设备没有找到对应的rx_handler函数,skb将被丢掉
atomic_long_inc(&skb->dev->rx_nohandler);
kfree_skb(skb);
/* Jamal, now you will not able to escape explaining
* me how you were going to use this. :-)
*/
ret = NET_RX_DROP;
}
out:
return ret;
}
来看函数deliver_ptype_list_skb:
static inline void deliver_ptype_list_skb(struct sk_buff *skb, struct packet_type **pt,
struct net_device *orig_dev, __be16 type, struct list_head *ptype_list)
{
struct packet_type *ptype, *pt_prev = *pt;
list_for_each_entry_rcu(ptype, ptype_list, list) {//遍历ptype_list链表
if (ptype->type != type)//找到链表中匹配type值的对象
continue;
if (pt_prev)//如果pt_prev不为空,则将skb传递给deliver_skb
deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
*pt = pt_prev;//如果此时pt_prev是空,就会把空值继续赋给pt指针。如果不为空代表着待处理的对象,同样赋值给pt指针。
}
综合来看,函数__netif_receive_skb_core中的所有直接调用deliver_skb或是调用deliver_ptype_list_skb再传进deliver_skb的地方,所用的传参均是这三个参数:
deliver_skb(skb, pt_prev, orig_dev)
其中orig_dev初始化为skb->dev,skb是当前待传递的数据,pt_prev是指定的packet_type。
skb与orig_dev不用说了,pt_prev永远指向待传递skb处理的packet_type,如果值为NULL就说明没能将skb传递出去。
这部分有点容易看乱,可以参考下面概括的简化后的函数内容:
__netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc, struct packet_type **ppt_prev){
struct packet_type *pt_prev=NULL;//初始化为空
list_for_each_entry_rcu(ptype, &ptype_all, list) {//第n次走进这处循环时,pt_prev记录着链表中第n-1个对象,循环内处理(第n-1个对象),离开这处循环时,pt_prev记录着链表中第n个对象
if (pt_prev)//第一次走到这里为空,不进if内容。
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;//pt_prev代表着下一个待处理的ptype
}//最后一次离开这个循环时,pt_prev记录着链表中最后一个对象,该对象等待处理
if (判断条件){
if (pt_prev) {//如果为空,说明前面的流程未找到skb关于packet_type的处理函数,从未成功通过deliver_skb递交过skb(未进上方循环)
ret = deliver_skb(skb, pt_prev, orig_dev);//不为空,则处理掉当前流程内找到的最后一个packet_type
pt_prev = NULL;//置为空是为了告知后续流程,暂时没有待处理的packet_type
}
}
if (判断条件){
deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type, &ptype_base[ntohs(type) &PTYPE_HASH_MASK]);//翻到上方该函数的内容,可以看到此函数内对于pt_prev指针的操作可总结为如果能找到匹配的packet_type就赋值给pt_prev(无论传进去时pt_prev是否有值逻辑都正确),如果找不到就保持传进来的原值
}
if (pt_prev) {//如果pt_prev不为空代表还有尚未处理的packet_type
*ppt_prev = pt_prev;//需要传递给上层函数做处理
}
return;//在这里ppt_prev指向了最后一个待处理的packet_type
}
__netif_receive_skb_one_core -> __netif_receive_skb_core
static int __netif_receive_skb_one_core(struct sk_buff *skb, bool pfmemalloc)
{
struct net_device *orig_dev = skb->dev;
struct packet_type *pt_prev = NULL;
int ret;
ret = __netif_receive_skb_core(skb, pfmemalloc, &pt_prev);//回到上层调用函数处,pt_prev指向的是__netif_receive_skb_core函数走到最后时的pt_prev,等于__netif_receive_skb_core的参数ppt_prev
if (pt_prev)//这里会直接调用最后一个待处理的packet_type的func回调函数,处理了__netif_receive_skb_core退出时遗留的那个packet_type
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
return ret;
}
经过上文分析,所有的路径最终都会到deliver_skb函数内:
__netif_receive_skb-> __netif_receive_skb_one_core -> __netif_receive_skb_core -> deliver_skb
static inline int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev,
struct net_device *orig_dev)
{
if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC)))
return -ENOMEM;
refcount_inc(&skb->users);//增加users计数
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);//调用当前数据包对应的协议处理函数
}
对于tcp、udp数据包,在这里会调ip协议的处理函数ip_rcv,从这里正式进入ip协议层。
至于上文分析的pt_prev为什么要这么设计,下一篇文章会分析到这一点。