RNDIS 下行速度低

简介: RNDIS 下行速度低

现象

rndis 是微软定义的一套通讯方案。类似的协议还有 qmimbimecmncm 等。


rndis 协议足够简单,可靠。所以最近在使用一款 quectel 公司模块时采用的就是 rndis 模式。在 linux 下 对应驱动是 rndis_host 驱动。 windows 10 下自带 rndis 驱动! 拿到模块首先测速度! 发现模块下行速度 Windows 上速度比 Linux 高很多,而且上行速度则差不多! 单独对比 Linux ,发现上行又比下行高很多。。。问题很奇怪!


产生原因

分析下行收包逻辑:

/*
 * DATA -- host must not write zlps
 */
int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
    int tm = 0;
    /* This check is no longer done by usbnet */
    if (skb->len < dev->net->hard_header_len)
        return 0;
    /* peripheral may have batched packets to us... */
    while (likely(skb->len)) {
        struct rndis_data_hdr    *hdr = (void *)skb->data;
        struct sk_buff        *skb2;
        u32            msg_type, msg_len, data_offset, data_len;
        msg_type = le32_to_cpu(hdr->msg_type);
        msg_len = le32_to_cpu(hdr->msg_len);
        data_offset = le32_to_cpu(hdr->data_offset);
        data_len = le32_to_cpu(hdr->data_len);
        /* don't choke if we see oob, per-packet data, etc */
        if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len
                || (data_offset + data_len + 8) > msg_len)) {
            dev->net->stats.rx_frame_errors++;
            netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n",
                   le32_to_cpu(hdr->msg_type),
                   msg_len, data_offset, data_len, skb->len);
            return 0;
        }
        skb_pull(skb, 8 + data_offset);
        /* at most one packet left? */
        if (likely((data_len - skb->len) <= sizeof *hdr)) {
            skb_trim(skb, data_len);
            break;
        }
        /* try to return all the packets in the batch */
        skb2 = skb_clone(skb, GFP_ATOMIC);
        if (unlikely(!skb2))
            break;
        skb_pull(skb, msg_len - sizeof *hdr);
        skb_trim(skb2, data_len);
        usbnet_skb_return(dev, skb2);
    }
    /* caller will usbnet_skb_return the remaining packet */
    return 1;
}
EXPORT_SYMBOL_GPL(rndis_rx_fixup);点击复制复制失败已复制


收包代码稍微复杂点,因为收包需要考虑到聚合报文的情况!因此起了一个while循环判断。while 里面就是剥离rndis 报文头,并调用网卡收包函数的过程!


这里对 skb 有两次偏移操作:

  1. skb_pull(skb, 8 + data_offset); 这一步从skb 去除当前消息的 rndis 报文头!
  2. skb_pull(skb, msg_len - sizeof *hdr); 因为skb payload 部分已经在skb2 有了一份clone,那么skb 当前的payload 就不重要了。因此,这里实际要做的是继续从skb剥离当前rndis 报文的数据部分(报文头已经剥离掉了)。这一步操作后,skb 将指向下一个rndis 报文的 rndis 报文头!


但是这里第 2 步逻辑错了,这里直接减去 rndis 报文头是错的! 因为 rndis 报文的 payload 之前并不一定全是协议头, payload 的偏移是头部 offset 定义的。


解决方案

方案很简单,修改偏移计算逻辑!

/*
 * DATA -- host must not write zlps
 */
int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
    int tm = 0;
    /* This check is no longer done by usbnet */
    if (skb->len < dev->net->hard_header_len)
        return 0;
    /* peripheral may have batched packets to us... */
    while (likely(skb->len)) {
        struct rndis_data_hdr    *hdr = (void *)skb->data;
        struct sk_buff        *skb2;
        u32            msg_type, msg_len, data_offset, data_len;
        msg_type = le32_to_cpu(hdr->msg_type);
        msg_len = le32_to_cpu(hdr->msg_len);
        data_offset = le32_to_cpu(hdr->data_offset);
        data_len = le32_to_cpu(hdr->data_len);
        /* don't choke if we see oob, per-packet data, etc */
        if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len
                || (data_offset + data_len + 8) > msg_len)) {
            dev->net->stats.rx_frame_errors++;
            netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n",
                   le32_to_cpu(hdr->msg_type),
                   msg_len, data_offset, data_len, skb->len);
            return 0;
        }
        skb_pull(skb, 8 + data_offset);
        /* at most one packet left? */
        if (likely((data_len - skb->len) <= sizeof *hdr)) {
            skb_trim(skb, data_len);
            break;
        }
        /* try to return all the packets in the batch */
        skb2 = skb_clone(skb, GFP_ATOMIC);
        if (unlikely(!skb2))
            break;
        skb_pull(skb, msg_len - data_offset - 8); // here is what I fixed
        skb_trim(skb2, data_len);
        usbnet_skb_return(dev, skb2);
    }
    /* caller will usbnet_skb_return the remaining packet */
    return 1;
}
EXPORT_SYMBOL_GPL(rndis_rx_fixup);
点击复制复制失败已复制


注意

这里的修改仅是规避方案,根本原因还是模块侧封包逻辑的问题。

目录
相关文章
|
2月前
|
数据中心
双绞线电缆带宽和频率范围详解
【10月更文挑战第14天】
138 1
|
4月前
|
存储 网络架构
网络速率与下载速率
【8月更文挑战第8天】
227 1
网络速率与下载速率
|
编解码 网络协议 网络性能优化
VoIP与常见编码的带宽计算
VoIP与常见编码的带宽计算
|
弹性计算
阿里云公网IP按固定宽带/按使用流量带宽计费模式选择方法
阿里云服务器公网IP按固定宽带/按使用流量带宽计费模式选择方法,阿里云服务器公网带宽计费模式按固定带宽和按使用流量哪个划算?按固定带宽计费1M带宽一个月23元,按使用流量计费1GB流量0.8元,如果云服务器带宽使用率低于10%,那么首选按使用流量计费,如果带宽实际利用率较高的话,按固定带宽计费更划算一些。云服务器吧来详细说下阿里云服务器带宽不同计费模式下收费价格、费用计算方法及如何选择更合适说明:
589 0
阿里云公网IP按固定宽带/按使用流量带宽计费模式选择方法
|
运维 监控 前端开发
链路,设备,带宽冗余技术
链路,设备,带宽冗余技术
327 0
|
存储 编解码 网络架构
传输时延和传播时延(补充:频段,信道带宽,数据速率的区别,以及帧大小和帧长)
传输时延和传播时延(补充:频段,信道带宽,数据速率的区别,以及帧大小和帧长)
929 0
HIMA B5233-2 采样率带宽通常是指基带带宽
HIMA B5233-2 采样率带宽通常是指基带带宽
HIMA B5233-2 采样率带宽通常是指基带带宽
|
弹性计算
阿里云带宽计费模式按固定宽带和按使用流量区别对比及选择方法
阿里云服务器公网IP带宽计费方式分为按固定宽带和按使用流量,按固定宽带和按使用流量有什么区别?阿里云公网带宽计费模式如何选择?需要根据大家实际的应用场景来选择
5486 0
阿里云带宽计费模式按固定宽带和按使用流量区别对比及选择方法
|
网络架构
关于网络,带宽,网速,延迟的一些知识
关于网络,带宽,网速,延迟的一些知识
246 0
手机WIFI的上行下行速度不同
手机WIFI的上行下行速度不同
245 0