• ixgbe 驱动 为xxx驱动做准备1


      网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动。并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的。V4L2 一样几个类型摄像头合并在一起

      先说一下 驱动总线平台;实际上就是platform_device(设备)platform_driver(驱动)platform(虚拟总线)上的注册、匹配,相互绑定。 但是最核心的还是cdev file_opterations 等

    • :struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)
    • :struct platform_device-->注册:platform_device_register(unregister)
    • :struct platform_driver-->注册:platform_driver_register(unregister)

    设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;

    platform_device:提供设备的硬件资源 比如 中断号 寄存器地址  DMA

    platform_driver : 提供  match id 绑定驱动和device资源的回调probe

    详细 就不说了 很简单。。。。 之前在csdn上写过。。后续慢慢找回吧!!!!!!

    假设:device先注册, 后续注册driver时,根据platform_match 匹配到了设备;

    那么就会调用probe 实现资源的绑定!!!  一般都是  创建设备  注册中断  初始化 file_opteriton 等回调函数

    看下 ixgbe_probe 就知道:

    static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
    {
        struct net_device *netdev;
        struct ixgbe_adapter *adapter = NULL;
        struct ixgbe_hw *hw;
        /*
    static const struct ixgbe_info *ixgbe_info_tbl[] = {
        [board_82598]        = &ixgbe_82598_info,
        [board_82599]        = &ixgbe_82599_info,
        [board_X540]        = &ixgbe_X540_info,
        [board_X550]        = &ixgbe_X550_info,
        [board_X550EM_x]    = &ixgbe_X550EM_x_info,
        [board_x550em_a]    = &ixgbe_x550em_a_info,
    };有很多网卡,根据mactch的记过选择对用的设备信息 
        */
        const struct ixgbe_info *ii = ixgbe_info_tbl[ent->driver_data];

     根据硬件的参数来取得对应的devices info

    通过对应的netdev的结构取得adapter,然后所有的核心操作都是保存在adapter中:

    netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), indices);
    // 分配net_device和ixgbe_adapter,发送队列数为MAX_TX_QUEUE
        if (!netdev) {
            err = -ENOMEM;
            goto err_alloc_etherdev;
        }
    
        SET_NETDEV_DEV(netdev, &pdev->dev);
    
        adapter = netdev_priv(netdev);// 得到ixgbe_adapter的指针
    
        adapter->netdev = netdev;
        adapter->pdev = pdev;
        hw = &adapter->hw;// 得到ixgbe_hw的指针
        hw->back = adapter;
        adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
     // 将BAR0中的总线地址映射成内存地址,赋给hw->hw_addr,允许网卡驱动通过hw->hw_addr访问网卡的BAR0对应的Memory空间
        hw->hw_addr = ioremap(pci_resource_start(pdev, 0),
                      pci_resource_len(pdev, 0));
        adapter->io_addr = hw->hw_addr;
    netdev->netdev_ops = &ixgbe_netdev_ops;// 注册ixgbe_netdev_ops
        ixgbe_set_ethtool_ops(netdev);
        netdev->watchdog_timeo = 5 * HZ;
        strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name));
    
        /* Setup hw api */
        hw->mac.ops   = *ii->mac_ops;
        hw->mac.type  = ii->mac;
        hw->mvals     = ii->mvals;
    
        /* EEPROM */
        hw->eeprom.ops = *ii->eeprom_ops;
        eec = IXGBE_READ_REG(hw, IXGBE_EEC(hw));
        if (ixgbe_removed(hw->hw_addr)) {
            err = -EIO;
            goto err_ioremap;
        }
        /* If EEPROM is valid (bit 8 = 1), use default otherwise use bit bang */
        if (!(eec & BIT(8)))
            hw->eeprom.ops.read = &ixgbe_read_eeprom_bit_bang_generic;
    
        /* PHY  硬件资源的初始化。。。。。。。。*/
        hw->phy.ops = *ii->phy_ops;
        hw->phy.sfp_type = ixgbe_sfp_type_unknown;
        /* ixgbe_identify_phy_generic will set prtad and mmds properly */
        hw->phy.mdio.prtad = MDIO_PRTAD_NONE;
        hw->phy.mdio.mmds = 0;
        hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
        hw->phy.mdio.dev = netdev;
        hw->phy.mdio.mdio_read = ixgbe_mdio_read;
        hw->phy.mdio.mdio_write = ixgbe_mdio_write;
    
        ii->get_invariants(hw);

    设置网卡属性 ethtool 可以查看

    #define IXGBE_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | 
                        NETIF_F_GSO_GRE_CSUM | 
                        NETIF_F_GSO_IPXIP4 | 
                        NETIF_F_GSO_IPXIP6 | 
                        NETIF_F_GSO_UDP_TUNNEL | 
                        NETIF_F_GSO_UDP_TUNNEL_CSUM)
    
        netdev->gso_partial_features = IXGBE_GSO_PARTIAL_FEATURES;
        netdev->features |= NETIF_F_GSO_PARTIAL |
                    IXGBE_GSO_PARTIAL_FEATURES;
    
        if (hw->mac.type >= ixgbe_mac_82599EB)
            netdev->features |= NETIF_F_SCTP_CRC;ixgbe_msix_clean_rings
    
        /* copy netdev features into list of user selectable features */
        netdev->hw_features |= netdev->features |
                       NETIF_F_HW_VLAN_CTAG_RX |
                       NETIF_F_HW_VLAN_CTAG_TX |
                       NETIF_F_RXALL |
                       NETIF_F_HW_L2FW_DOFFLOAD;
    
        if (hw->mac.type >= ixgbe_mac_82599EB)
            netdev->hw_features |= NETIF_F_NTUPLE |
                           NETIF_F_HW_TC;
    
        if (pci_using_dac)
            netdev->features |= NETIF_F_HIGHDMA;
    
        netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID;
        netdev->hw_enc_features |= netdev->vlan_features;

    还有注册中断

    在enable 设备时 就会ixgbe_request_irq

            err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
                      q_vector->name, q_vector);

    static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
    {
        struct ixgbe_q_vector *q_vector = data;

        /* EIAM disabled interrupts (on this vector) for us */

        if (q_vector->rx.ring || q_vector->tx.ring)
            napi_schedule_irqoff(&q_vector->napi);

        return IRQ_HANDLED;
    }
    也就是 注册了napi 回调函数。。。 最后执行其对应的poll

        /* initialize NAPI */
        netif_napi_add(adapter->netdev, &q_vector->napi,
                   ixgbe_poll, 64);
    void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
                int (*poll)(struct napi_struct *, int), int weight)
    {
        INIT_LIST_HEAD(&napi->poll_list);
        hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
        napi->timer.function = napi_watchdog;
        napi->gro_count = 0;
        napi->gro_list = NULL;
        napi->skb = NULL;
        napi->poll = poll;
        if (weight > NAPI_POLL_WEIGHT)
            pr_err_once("netif_napi_add() called with weight %d on device %s ",
                    weight, dev->name);
        napi->weight = weight;
        list_add(&napi->dev_list, &dev->napi_list);
        napi->dev = dev;
    #ifdef CONFIG_NETPOLL
        spin_lock_init(&napi->poll_lock);
        napi->poll_owner = -1;
    #endif
        set_bit(NAPI_STATE_SCHED, &napi->state);
        napi_hash_add(napi);
    }

    注册网络设备dev

    strcpy(netdev->name, "eth%d");
        err = register_netdev(netdev);----->if (dev->netdev_ops->ndo_init) {ret = dev->netdev_ops->ndo_init(dev)

    netdev->netdev_ops = &ixgbe_netdev_ops;

    poll:NAPI驱动最终是会调用网卡驱动挂载的poll回调,在ixgbe中,对应的回调就是ixgbe_poll

    /**
     * ixgbe_poll - NAPI Rx polling callback
     * @napi: structure for representing this polling device
     * @budget: how many packets driver is allowed to clean
     *
     * This function is used for legacy and MSI, NAPI mode
     **/
    int ixgbe_poll(struct napi_struct *napi, int budget)
    {
        struct ixgbe_q_vector *q_vector =
                    container_of(napi, struct ixgbe_q_vector, napi);
        struct ixgbe_adapter *adapter = q_vector->adapter;
        struct ixgbe_ring *ring;
        int per_ring_budget, work_done = 0;
        bool clean_complete = true;
    
    #ifdef CONFIG_IXGBE_DCA
        if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
            ixgbe_update_dca(q_vector);
    #endif
    
        ixgbe_for_each_ring(ring, q_vector->tx) {  TX
            if (!ixgbe_clean_tx_irq(q_vector, ring, budget))
                clean_complete = false;
        }
    
        /* Exit if we are called by netpoll or busy polling is active */
        if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector))
            return budget;
    
        /* attempt to distribute budget to each queue fairly, but don't allow
         * the budget to go below 1 because we'll exit polling */
        if (q_vector->rx.count > 1)
            per_ring_budget = max(budget/q_vector->rx.count, 1);
        else
            per_ring_budget = budget;
    
        ixgbe_for_each_ring(ring, q_vector->rx) {RX
            int cleaned = ixgbe_clean_rx_irq(q_vector, ring,
                             per_ring_budget);
    
            work_done += cleaned;
            if (cleaned >= per_ring_budget)
                clean_complete = false;
        }
    
        ixgbe_qv_unlock_napi(q_vector);
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
            return budget;
    
        /* all work done, exit the polling mode */
        napi_complete_done(napi, work_done);
        if (adapter->rx_itr_setting & 1)
            ixgbe_set_itr(q_vector);
        if (!test_bit(__IXGBE_DOWN, &adapter->state))
            ixgbe_irq_enable_queues(adapter, BIT_ULL(q_vector->v_idx));
    
        return min(work_done, budget - 1);
    }

    ndo_start_xmit---->ixgbe_xmit_frame,这个回调就是驱动提供给协议栈的发送回调接口。我们来看这个函数.

    它的实现很简单,就是选取对应的队列,然后调用ixgbe_xmit_frame_ring来发送数据。

    static netdev_tx_t __ixgbe_xmit_frame(struct sk_buff *skb,
                          struct net_device *netdev,
                          struct ixgbe_ring *ring)
    {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *tx_ring;
    
        /*
         * The minimum packet size for olinfo paylen is 17 so pad the skb
         * in order to meet this minimum size requirement.
         */
        if (skb_put_padto(skb, 17))
            return NETDEV_TX_OK;
    
        tx_ring = ring ? ring : adapter->tx_ring[skb->queue_mapping];
    
        return ixgbe_xmit_frame_ring(skb, adapter, tx_ring);
    }

    用ixgbe_tx_map来发送数据。而ixgbe_tx_map所做的最主要是两步,第一步请求DMA,第二步写寄存器,通知网卡发送数据.

    netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
                  struct ixgbe_adapter *adapter,
                  struct ixgbe_ring *tx_ring)
    {
        struct ixgbe_tx_buffer *first;
        int tso;
        u32 tx_flags = 0;
        unsigned short f;
        u16 count = TXD_USE_COUNT(skb_headlen(skb));
        __be16 protocol = skb->protocol;
        u8 hdr_len = 0;
    
        /*
         * need: 1 descriptor per page * PAGE_SIZE/IXGBE_MAX_DATA_PER_TXD,
         *       + 1 desc for skb_headlen/IXGBE_MAX_DATA_PER_TXD,
         *       + 2 desc gap to keep tail from touching head,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
            count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
    
        if (ixgbe_maybe_stop_tx(tx_ring, count + 3)) {
            tx_ring->tx_stats.tx_busy++;
            return NETDEV_TX_BUSY;
        }
    
        /* record the location of the first descriptor for this packet */
        first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
        first->skb = skb;
        first->bytecount = skb->len;
        first->gso_segs = 1;
    
    -------------------------------------
        ixgbe_tx_map(tx_ring, first, hdr_len);
    
        return NETDEV_TX_OK;
    
    out_drop:
        dev_kfree_skb_any(first->skb);
        first->skb = NULL;
    
        return NETDEV_TX_OK;
    }

     上面的操作是异步的,此时内核还不能释放SKB,而是网卡硬件发送完数据之后,会再次产生中断通知内核,然后内核才能释放内存 ; 此时可以看到 napi的poll 回调函数  会清除tx 队列

  • 相关阅读:
    ubuntu中:configure: error: curses development files not found
    configure: error: liblzma development files not found
    Ubuntu中 configure: WARNING: libcurl not enabled: library not found
    ubuntu中如何查看系统已经安装了哪些包
    ./popins2: error while loading shared libraries: libbifrost.so: cannot open shared object file: No such file or directory
    ubuntu中:configure: WARNING: S3 support not enabled: requires SSL development files
    VueReactAngular三者区别
    阿里云物联网IOT
    阿里云混合云
    阿里云企业服务和云通信
  • 原文地址:https://www.cnblogs.com/codestack/p/12906441.html
Copyright © 2020-2023  润新知