无线网络上发包之前都要进行载波侦听,如果未收到ACK,就指数级退避之后重传。
有时候我们为了观察网卡在全力发送数据包时的性能,要禁用CSMA和ACK,这一篇文章先来讨论怎么禁用CSMA,这里提供两种思路,一是通过设置几个寄存器来完成禁CSMA和退避,,在drivers/net/wireless/ath/ath9k/hw.c的ath9k_hw_reset函数最后设置几个寄存器:
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_IGNORE_VIRT_CS);
REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF);
我以前搜到过一个说法,设置AR_DIAG_SW寄存器的AR_DIAG_IGNORE_VIRT_CS来禁用虚拟载波侦听,设置AR_DIAG_SW寄存器的AR_DIAG_FORCE_RX_CLEAR来禁止物理载波侦听,,不过我设置FORCE_RX_CLEAR这个的时候有一定几率崩溃,所以最终没有采用。关于虚拟和物理载波侦听,参考《下一代无线局域网-802.11n的吞吐率、强健性和可靠性》一书的解释是:
CSMA/CA的基础在于载波侦听。DCF同时通过物理和虚拟的载波侦听功能来判断媒体状态。物理载波侦听功能位于PHY中,其通过能量检测与带帧长后延的前导码检测来判断媒体是否繁忙。虚拟载波侦听功能位于MAC中,其使用在MAC头的“时长”字段中所承载的预订信息,该信息声明了对媒体的独占接入。虚拟载波侦听功能被称为网络分配向量(NAV)。只有在物理和虚拟载波侦听机制都给出同样结果的情况下,媒体才被判断为空闲。
这个VIRT_CS位应该就是虚拟载波侦听,而物理载波侦听应该怎么来禁止,对于网上说的AR_DIAG_FORCE_RX_CLEAR这个标志,不了解具体作用。AR_DIAG_FORCE_CH_IDLE_HIGH而这个位则表明信道空闲,我觉得应该是和这个物理载波侦听相对应的。
但是,本文下面分割线以后,提出了一个问题,那就是只用上面的前两行来禁CSMA没有问题,但是用第三行禁退避,就会出问题,用下文的设置skb->priority的方法能够保证可以正常发送,系统不再panic,但是速率会很低,所以我用另外的方法来禁用退避,驱动有很多的发送队列,针对每一个发送队列,要设置自己的最大退避窗口,最小退避窗口,把这两个值都设置成0就是禁用退避了,而且针对每个队列,也有单独禁用退避(backoff)的方式,在drivers/net/wireless/ath/ath9k/mac.c的ath9k_hw_resettxqueue函数里,随便找个快结束的地方,加上:
REG_WRITE(ah, AR_DLCL_IFS(q), SM(0, AR_D_LCL_IFS_CWMIN) | SM(0, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); REG_SET_BIT(ah, AR_DMISC(q), AR_D_MISC_POST_FR_BKOFF_DIS);
==================================分割线===========================================
如果采用最前面的设置三个寄存器的方法来禁用CSMA,使用pktgen的时候还是会panic的。
决解思路:skb中有这么一个域,叫做nfctinfo,源代码里对它的解释是“Relationship of this skb to the connection”,在源代码里它的类型是__u8,但是我在网上查找的资料,都是说这是一个枚举型变量ip_conntrack_info的值。
nfctinfo是一个NetFilter相关的概念,我在驱动的库里面找到了ip_conntrack_info枚举类型的定义,如下:
/* Connection state tracking for netfilter. This is separated from, but required by, the NAT layer; it can also be used by an iptables extension. */ enum ip_conntrack_info { IP_CT_ESTABLISHED, /*作为已建立的连接的一部分*/ IP_CT_RELATED, /*正在建立或已建立的连接,或ICMP错误*/ IP_CT_NEW, /*新连接或重传*/ IP_CT_IS_REPLY, /*大于等着这个数的都是应答*/ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 /*不同的IP_CT 类型*/ };
pktgen没有对这个值做修改,是0,但是使用iperf进行发包时,经过一层层协议栈,这个值已经被赋为2,也就是新建连接来发送。
当禁用掉CSMA之后,更确切地说是忽略退避(设置标志位AR_D_GBL_IFS_MISC_IGNORE_BACKOFF)之后,如果这个nfctinfo的取值是IP_CT_ESTABLISHED则发不出包去。设置为2(IP_CT_NEW)之后就可以了。但是这两者之间的具体关系不了解。在前一篇文章的最后,我讲了在驱动里面为pktgen数据包强制设置发送队列的方法,只需要在那里面再加一行(并且设置队列也不是写死一个2了,而是用驱动自己去选择队列):
if (skb->len > 58){ __le32 *magic = &skb->data[42]; if(*magic == 0xbe9be955){ /* magic number of pktgen */ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); /* BE */ skb->priority = 0; /* skb->nfctinfo = 2; */ } }
但是这么做了之后pktgen的速率还是提不上去,最好还是用本文正文的方法去禁用BACKOFF。