1. 程式人生 > >linux 網路裝置驅動之alloc_etherdev

linux 網路裝置驅動之alloc_etherdev

最近在看網路驅動時,發現這個函式:

struct net_device *netdev;


netdev = alloc_etherdev(sizeof(synopGMACPciNetworkAdapter));

順著這個函式進行追蹤:

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)

由上面兩個巨集可以看出,alloc_etherdev最後呼叫的函式是alloc_etherdev_mqs函式,且傳遞的引數為:

sizeof_priv:synopGMACPciNetworkAdapter結構體大小。因為net_device可以由驅動程式擴充套件私有空間,此引數表示擴充套件的私有空間大小。是網路裝置驅動程式私有資料塊的大小,在alloc_netdev_mqs函式中,將和net_device資料結構一起分配,但是sizeof_priv也可以設定為0,不需要私有資料塊,或者自己分配私有資料塊記憶體。如果和net_device資料結構一起分配驅動程式的私有資料塊,則其私有資料塊的記憶體地址通過函式net_dec_priv獲取。

count:傳送佇列的個數

count:接收佇列的個數

所以最終是呼叫到函式數alloc_etherdev_mqs時,傳遞的引數是:sizeof(synopGMACPciNetworkAdapter)   1    1

函式實現如下:

struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
          unsigned int rxqs)
{
 return alloc_netdev_mqs(sizeof_priv, "eth%d",  ether_setup, txqs, rxqs);
}

該函式是封裝的alloc_netdev_mqs函式,傳遞的引數為:

struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        void (*setup)(struct net_device *),
        unsigned int txqs, unsigned int rxqs)
{
    struct net_device *dev;
    size_t alloc_size;
    struct net_device *p;


    BUG_ON(strlen(name) >= sizeof(dev->name)); //net_device資料結構中裝置名稱的最大長度是16個位元組

    /*將net_device資料結構的大小按32位元組對齊後,和sizeof_priv私有資料大小相加,產生分配的總記憶體位元組大小*/

    alloc_size = sizeof(struct net_device);
    if (sizeof_priv) {
        /* ensure 32-byte alignment of private area */
        alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
        alloc_size += sizeof_priv;
    }

     /*在這裡增加31個位元組,是為後面將分配後net_device資料結構的地址調整到32位元組邊界對齊,預留空間*/
    /* ensure 32-byte alignment of whole construct */
    alloc_size += NETDEV_ALIGN - 1;

   

    p = kzalloc(alloc_size, GFP_KERNEL); //分配記憶體
    if (!p) {
        printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
        return NULL;
    }

  /*將net_device資料結構的地址對齊到32位元組邊界,並記錄下調整後的地址和實際分配的地址之間的長度,便於釋放空間時使用分配的實際起始地址*/

   dev = PTR_ALIGN(p, NETDEV_ALIGN);
    dev->padded = (char *)dev - (char *)p;

/*分配一個per_cpu變數,記錄該結構的引用計數*/ 

  dev->pcpu_refcnt = alloc_percpu(int);
    if (!dev->pcpu_refcnt)
        goto free_p;

    /*初始化裝置的硬體地址列表,並分配一個硬體地址成員*/

    if (dev_addr_init(dev))
        goto free_pcpu;

    /*初始化多播和單播硬體地址列表*/

    dev_mc_init(dev);
    dev_uc_init(dev);

   /*設定裝置的網路空間*/

    dev_net_set(dev, &init_net);

    dev->gso_max_size = GSO_MAX_SIZE;

    INIT_LIST_HEAD(&dev->ethtool_ntuple_list.list);
    dev->ethtool_ntuple_list.count = 0;
    INIT_LIST_HEAD(&dev->napi_list);
    INIT_LIST_HEAD(&dev->unreg_list);
    INIT_LIST_HEAD(&dev->link_watch_list);
    dev->priv_flags = IFF_XMIT_DST_RELEASE;

   /*呼叫setup函式,初始化net_device結構中與裝置型別密切相關的成員*/
    setup(dev);

   /*分配接收佇列和傳送佇列*/ 

  dev->num_tx_queues = txqs;
    dev->real_num_tx_queues = txqs;
    if (netif_alloc_netdev_queues(dev))
        goto free_all;

#ifdef CONFIG_RPS
    dev->num_rx_queues = rxqs;
    dev->real_num_rx_queues = rxqs;
    if (netif_alloc_rx_queues(dev))
        goto free_all;
#endif

    /*設定網路裝置名稱*/

    strcpy(dev->name, name);
    dev->group = INIT_NETDEV_GROUP;
    return dev;

free_all:
    free_netdev(dev);
    return NULL;

free_pcpu:
    free_percpu(dev->pcpu_refcnt);
    kfree(dev->_tx);
#ifdef CONFIG_RPS
    kfree(dev->_rx);
#endif

free_p:
    kfree(p);
    return NULL;
}