• Linux网络设备驱动之数据发送流程(五)


      从网络设备驱动的结构分析可知,Linux 网络子系统在发送数据包时,会调用驱动程序提供的 hard_start_transmit( ) 函数,该函数用于启动数据包的发送。在设备初始化的时候,这个函数指针需被初始化以指向设备的 xxx_tx( )函数。

      网络设备驱动完成数据包发送的流程如下。

    1. 网络设备驱动程序从上层协议传递过来的 sk_buff 参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
    2. 对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填充 0。
    3. 设置硬件的寄存器,驱使网络设备进行数据发送操作。

      完成上述3个步骤的网络设备驱动程序的数据包发送函数模板如下所示:

    int xxx_tx(struct sk_buff *skb, struct net_device *dev)
    {
        int len;
        char *data, shortpkt[ETH_ZLEN];
        if (xxx_send_available(···)) {     /* 发送队列未满,可以发送 */
            /* 获得有效数据指针和长度*/
            data = skb->data;
            len = skb->len;
            if (len < ETH_ZLEN) {
                /* 如果帧长小于以太网帧最小长度, 补0 */
                memset(shortpkt, 0, ETH_ZELN);
                memcpy(shortpkt, skb->data, skb->len);
                len = ETH_ZLEN;
                data = shortpkt;
            } 
    
            dev->trans_start = jiffies;  /* 记录发送时间戳 */
            
            if (avail) {   /* 设置硬件寄存器,让硬件把数据包发送出去 */
                xxx_hw_tx(data, len, dev);
            } else {
                netif_stop_queue(dev);
                ···   
            }
        }
    }                            

      这里特别要强调 netif_stop_queue( ) 的调用,当发送队列为满 或 因其他原因来不及发送当前上层传下来的数据包时,则调用此函数阻止上层继续向网络设备驱动传递数据包。当忙于发送的数据包被发送完成后,在以 TX 结束的中断处理中,应该调用 netif_wake_queue( ) 唤醒被阻塞的上层,以启动它继续向网络设备驱动传送数据包。

      当数据传输超时时,意味着当前的发送操作失败 或 硬件已陷入未知状态,此时,数据包发送超时处理函数 xxx_tx_timeout( ) 将被调用。这个函数也需要调用由 Linux 内核提供的netif_wake_queue( ) 函数以重新启动设备发送队列,如下代码所示:

    /*
     * 网络设备驱动程序的数据包发送超时函数模板
     */
    
    void xxx_tx_timeout(struct net_device *dev)
    {
        ···
        netif_wake_queue(dev);   /* 重新启动设备发送队列 */   
    }

      从以上内容可知, netif_wake_queue( ) 和 netif_stop_queue( ) 是数据发送流程中要调用的两个非常重要的函数,分别用于唤醒 和 阻止上层向下传送数据包,他们的原型定义于 include/linux/netdevice.h,如下:

    void netif_tx_wake_queue(struct netdev_queue *dev_queue)
    {
        if (test_and_clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state)) {
            struct Qdisc *q;
    
            rcu_read_lock();
            q = rcu_dereference(dev_queue->qdisc);
            __netif_schedule(q);
            rcu_read_unlock();
        }
    }
    
    /**
     *    netif_wake_queue - restart transmit
     *    @dev: network device
     *
     *    Allow upper layers to call the device hard_start_xmit routine.
     *    Used for flow control when transmit resources are available.
     */
    static inline void netif_wake_queue(struct net_device *dev)
    {
        netif_tx_wake_queue(netdev_get_tx_queue(dev, 0));
    }
    
    
    
    static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue)
    {
        if (WARN_ON(!dev_queue)) {
            pr_info("netif_stop_queue() cannot be called before register_netdev()
    ");
            return;
        }
        set_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state);
    }
    
    /**
     *    netif_stop_queue - stop transmitted packets
     *    @dev: network device
     *
     *    Stop upper layers calling the device hard_start_xmit routine.
     *    Used for flow control when transmit resources are unavailable.
     */
    static inline void netif_stop_queue(struct net_device *dev)
    {
        netif_tx_stop_queue(netdev_get_tx_queue(dev, 0));
    }
  • 相关阅读:
    HTML + CSS短标题(二,三,四文字长度)两端对齐的方式
    转载 ----MAC 上搭建lua
    转载 -- 基于原生JS与OC方法互相调用并传值(附HTML代码)
    转载 ---资深HR告诉你:我如何筛选简历与选择人员的
    转载 OSX开发推荐书籍列表
    IOS --支付宝SDK 分解讲解
    IOS -RSA加密及解密
    ios --转载 使用SMSSDK实现短信验证:
    ios --转载 在mac上安装让rvm及cocoa pods详解
    简单说明Python中的装饰器的用法
  • 原文地址:https://www.cnblogs.com/wanglouxiaozi/p/13378959.html
Copyright © 2020-2023  润新知