ixgbe 驱动 为xxx驱动做准备1
阅读原文时间:2023年07月09日阅读:2

  网卡都是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\n",
                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 队列

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章