net/core/dev.c的__netif_receive_skb函数中,函数末尾需要匹配相应的协议处理函数以此上传数据包:
-
list_for_each_entry_rcu(ptype,
-
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
-
if (ptype->type == type && (ptype->dev == null_or_orig ||
-
ptype->dev == skb->dev || ptype->dev == orig_dev ||
-
ptype->dev == orig_or_bond)) {
-
if (pt_prev)
-
ret = deliver_skb(skb, pt_prev, orig_dev);
-
pt_prev = ptype;
-
}
-
}
1 list_for_each_entry_rcu宏的实现如下:
-
/**
-
* list_for_each_entry_rcu - iterate over rcu list of given type 在指定类型的rcu链表上迭代(遍历)
-
* @pos: the type * to use as a loop cursor. 类型为type*的迭代游标
-
* @head: the head for your list.链表头
-
* @member: the name of the list_struct within the struct. type数据结构中链表结构的名字,一般指向链表结构的指针
-
*
-
* This list-traversal primitive may safely run concurrently with
-
* the _rcu list-mutation primitives such as list_add_rcu()
-
* as long as the traversal is guarded by rcu_read_lock(). 调用这个链表遍历的宏之前需要调用rcu_read_lock函数,这样这个链表遍历原语可以在并发的情况下安全地遍历通过list_add_rcu()函数构造的链表。
-
*/
-
#define list_for_each_entry_rcu(pos, head, member) \
-
for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \ list_entry_rcu宏的实现如下,相信看到container_of都不会陌生。取得链表头
-
prefetch(pos->member.next), &pos->member != (head); \ 判断是否已经遍历完。prefetch主要是告诉处理器预取指定变量的值以提高处理效率,但实际上根据实践来看可能让链表遍历更慢,可以查找相关文章看看。
-
pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) 取得下一个元素
-
/**
-
* list_entry_rcu - get the struct for this entry
-
* @ptr: the &struct list_head pointer.
-
* @type: the type of the struct this is embedded in.
-
* @member: the name of the list_struct within the struct.
-
*
-
* This primitive may safely run concurrently with the _rcu list-mutation
-
* primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
-
*/
-
#define list_entry_rcu(ptr, type, member) \
-
container_of(rcu_dereference_raw(ptr), type, member) 其实这个链表遍历之所以用rcu做后缀,主要是这儿的指针调用了rcu_dereference_raw的使用,其中调用了smp_read_barrier_depends宏,以确保取出的指针值是更新后的值,不是旧值。
继续看下面packet的type(协议类型)的匹配
-
if (ptype->type == type && (ptype->dev == null_or_orig ||
-
ptype->dev == skb->dev || ptype->dev == orig_dev ||
-
ptype->dev == orig_or_bond)) {
-
if (pt_prev)
-
ret = deliver_skb(skb, pt_prev, orig_dev);
-
pt_prev = ptype;
-
}
如果第一次找到类型匹配的协议处理结构,那么现用pt_prev存下来,然后如果找到第二个那么前面存储下来的那个结构就要先进行处理了,直到最后一个,最后一个不会调用deliver_skb函数对数据包进行处理,而是调用其自己的函数进行处理,如下:
-
if (pt_prev) {
-
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
-
} else {
-
kfree_skb(skb);
-
/* Jamal, now you will not able to escape explaining
-
* me how you were going to use this. :-)
-
*/
-
ret = NET_RX_DROP;
-
}
实际上linux网络协议栈中很少会注册多个type相同的协议处理结构,所以一般会执行pt_prev->func这个函数指针。
阅读(1448) | 评论(0) | 转发(0) |