前言:从12年开始做无线驱动相关的工作,到13年大概做了一年半,现在歇了快一年了,以免白学那么久,最近重新整理了一下当时的资料,写一点文章,这方面的帖子比较少,当时碰到过很多问题难以解决,我是用的linux2.6的内核,将来用其他版本的朋友也可能会碰到类似的问题,可以把我的解决方案做一个参考~
测试无线性能的pktgen有专用的版本,编译atheros固件的时候会自带一个无线版本的pktgen,我一开始用pktgen官网提供的pktgen发送脚本,怎么发都是一运行内核就panic,或者开发板变得非常卡,有时候没有崩溃的情况下,dmsg查看输出,可以看到源码里这一句打印了堆栈信息:
WARN_ON(tid->ac->txq != txq);
代码位置不重要,反正可以看到是txq,也就是发送队列有问题。正巧呢经过仔细观察pktgen的统计信息:
发现里面有queue_map_min和queue_map_max这样两个参数,pktgen官方的脚本里没有设置,我从pktgen官网上下载的源码里面也没有提这两个参数,这俩就是关键,现在的2是我后来自己设置的,默认好像是0。这个发送队列在代码里对应的是这样的取值:
/** * enum ieee80211_ac_numbers - AC numbers as used in mac80211 * @IEEE80211_AC_VO: voice * @IEEE80211_AC_VI: video * @IEEE80211_AC_BE: best effort * @IEEE80211_AC_BK: background */ enum ieee80211_ac_numbers { IEEE80211_AC_VO = 0, IEEE80211_AC_VI = 1, IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, };
这是数据包对应于不同接入类别的几个发送队列,正常数据包都是使用尽力而为这种级别,只要把queue_map_min和queue_map_max都设置为2,pktgen的数据包都用这个AC来发就没问题了。
==================================分割线=========================================
关于这个发送队列,实际在使用pktgen的过程中还有一个后续问题,在这里分享一下:
虽然设置了queue_map,但是不知道为什么,生成的前两个数据包仍然是队列0和1的,后面才是我指定的2的,虽然影响不大,但是有两个弊端:一是pktgen的clone_skb域只能设置为0或其它较小的数,如果设置为10000,那么最前面的将近20000个包的发送队列都是不对的。而clone_skb的值越大,pktgen发送包的效率越高,当然,clone_skb为0时的效率已经够我们用的了,所以这一点关系不大。在这里解释一下clone_skb这个属性,pktgen调用驱动的某个函数(假设函数是tx())来进行发包,并把封装数据的sk_buff对象(skb)作为参数传进去,那么如果一共发三个包,大概就是这样的流程(下面的都是伪代码):
struct sk_buff *skb = get_new_skb();//生成一个新的skb并填充数据 tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb);
这是clone_skb=0或1的情况,也就是说,发送的这三个数据包是相互独立的,没有重复使用,如果clone_skb=2就指定两个包共用一个skb,就变成了下面这样:
struct sk_buff *skb = get_new_skb(); tx(skb); tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb);
因为没有重复生成skb,也就提高了效率。这是第一个问题,第二个是在驱动里有一开始提到的WARN_ON的那一步判断,如果发送队列不是预期的值,会打印堆栈信息,这个打印操作是很耗时,在内核中,过于频繁的printk都可以导致系统崩溃,如果太频繁的打印堆栈信息,也是会导致崩溃的(这也是不设置pktgen的发送队列时板子可能会崩溃的原因),现在clone_skb的值是0,用脚本自动切换MCS时,每次使用新的MCS之后前两个包都会打印出堆栈信息,而堆栈信息又很长,这样很影响我们观察在驱动其它地方使用printk打印的输出。
解决方法是不管queue_map_min和queue_map_max怎么设置了,统一在数据包刚进入链路层代码的地方强制把这个字段置成2,当然如果是以真实使用为目的还要改回去,现在用pktgen做实验用,就没关系了,pktgen发包是通过调用atheros驱动的
ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
这个函数,该函数在/net/mac80211/tx.c中,就在这个函数开始的位置判断当前数据包是不是pktgen的数据包(通过pktgen头部的magic number来进行判断),如果是,就调用前面提到的skb_set_queue_mapping函数把它放到2号队列里去。代码片段如下:
if (skb->len > 58){ __le32 *magic = &skb->data[42]; if(*magic == 0xbe9be955) /* magic number of pktgen */ skb_set_queue_mapping(skb, 2); /* BE */ }
pktgen的最小包长是14(MAC头)+20(IP头)+8(UDP头)+16(Pktgen头),大于该值才进行判断,pktgen头部是从第42字节开始,按小端序取出来和pktgen的magic number来进行比较,如果匹配,则设置发送队列。