DPDK NIC 초기화

Page content

constructor attribute

http://phoxis.org/2011/04/27/c-language-constructors-and-destructors-with-gcc/

constructor attribute을 가진 함수는 main 함수를 실행하기 전에 호출한다.

예제 (출처)

#include <stdio.h>
 
void begin (void) __attribute__((constructor));
void end (void) __attribute__((destructor));
 
int main (void)
{
  printf ("\nInside main ()");
}
 
void begin (void)
{
  printf ("\nIn begin ()");
}
 
void end (void)
{
  printf ("\nIn end ()\n");
}

실행하면

In begin ()
Inside main ()
In end ()

DPDK

DPDK의 경우 device driver들을 모두 constructor attirbute을 사용해서 main 함수 전에 호출되록 한다.

PMD_REGISTER_DRIVER(pmd_igb_drv);  
PMD_REGISTER_DRIVER(pmd_igbvf_drv);  
PMD_REGISTER_DRIVER(em_pmd_drv);  

Physical device 외에 virtual device들도 동일하게 등록한다.

PMD_REGISTER_DRIVER(cryptodev_aesni_mb_pmd_drv);  
PMD_REGISTER_DRIVER(pmd_qat_drv);  

PMD_REGISTER_DRIVER()lib/librte_eal/common/include/rte_dev.h에 다음과 같이 정의되어 있다.

#define PMD_REGISTER_DRIVER(d)\
void devinitfn_ ##d(void);\
void __attribute__((constructor, used)) devinitfn_ ##d(void)\
{\
    rte_eal_driver_register(&d);\
}

rte_eal_deriver_register()lib/librte_eal/common/eal_common_dev.c에서 static 변수로 정의된 rte_driver_list[]에 함수 인자로 넘겨진 driver를 등록한다.

인자는 다음 struct 형태로 정의된다.

/**  
 * A structure describing a device driver.  
 */  
struct rte_driver {   
    TAILQ_ENTRY(rte_driver) next;      /**< Next in list. */  
    enum pmd_type type;                /**< PMD Driver type */  
    const char *name;                  /**< Driver name. */  
    rte_dev_init_t *init;              /**< Device init. function. */  
    rte_dev_uninit_t *uninit;          /**< Device uninit. function. */  
};  

등록된 디바이스들은 rte_eal_init() 초기화 과정에서 호출되는 함수 rte_eal_dev_init()에서 각 디바이스의 초기화 함수가 호출된다.

int
rte_eal_dev_init(void)
{
    /* call the init function for each virtual device */
    TAILQ_FOREACH(devargs, &devargs_list, next) {
        if (devargs->type != RTE_DEVTYPE_VIRTUAL)
            continue;

        if (rte_eal_vdev_init(devargs->virt.drv_name,
                    devargs->args)) {
        ...
    }

    /* Once the vdevs are initalized, start calling all the pdev drivers */
    TAILQ_FOREACH(driver, &dev_driver_list, next) {
        if (driver->type != PMD_PDEV)
            continue;

        /* PDEV drivers don't get passed any parameters */
        driver->init(NULL, NULL);
    }

QAT device

static struct rte_driver pmd_qat_drv = {
    .type = PMD_PDEV,
    .init = rte_qat_pmd_init,
};
QAT PMD 초기화
  1. rte_qat_pmd_init()
  2. rte_cryptodev_pmd_driver_register()
  3. rte_cryptodev_init() & rte_eal_pci_register()

AESNI_MB device

static struct rte_driver cryptodev_aesni_mb_pmd_drv = {
    .name = CRYPTODEV_NAME_AESNI_MB_PMD,
    .type = PMD_VDEV,
    .init = cryptodev_aesni_mb_init,
    .uninit = cryptodev_aesni_mb_uninit
};
AESNI_MB 초기화
  1. cryptodev_aesni_mb_init()
  2. cryptodev_aesni_mb_create()
  • CPU가 AES 연산을 지원하는 지 확인
  • CPU가 AVX2/AVX/SSE4_1 중 최소 한 가지를 지원하는 지 확인
  • rte_cryptodev_pmd_virtual_dev_init()를 이용해 PMD device로 등록

e1000

static int
rte_em_pmd_init(const char *name __rte_unused, const char *params __rte_unused)
{
    rte_eth_driver_register(&rte_em_pmd);
    return 0;
}

struct rte_driver em_pmd_drv = {
    .type = PMD_PDEV,
    .init = rte_em_pmd_init,
};

Physical NIC은 rte_eth_driver_register()함수를 이용하여 rte_eth_dev에 등록된다. 이때 등록되는 디바이스 구조체는 다음과 같다.

static struct eth_driver rte_em_pmd = {
    .pci_drv = {
        .name = "rte_em_pmd",
        .id_table = pci_id_em_map,
        .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
            RTE_PCI_DRV_DETACHABLE,
    },
    .eth_dev_init = eth_em_dev_init,
    .eth_dev_uninit = eth_em_dev_uninit,
    .dev_private_size = sizeof(struct e1000_adapter),
};

rte_eth 구조체에 등록하는 함수 rte_eth_driver_register()는 다음과 같이 구현되어 있어, PCI device list에 NIC을 등록한다. 등록된 PCI device들은 rte_eal_init() 과정에서 호출되는 PCI scan 과정을 통해 실제 NIC을 찾는 과정을 거친다.

/**
 * Register an Ethernet [Poll Mode] driver.
 *
 * Function invoked by the initialization function of an Ethernet driver
 * to simultaneously register itself as a PCI driver and as an Ethernet
 * Poll Mode Driver.
 * Invokes the rte_eal_pci_register() function to register the *pci_drv*
 * structure embedded in the *eth_drv* structure, after having stored the
 * address of the rte_eth_dev_init() function in the *devinit* field of
 * the *pci_drv* structure.
 * During the PCI probing phase, the rte_eth_dev_init() function is
 * invoked for each PCI [Ethernet device] matching the embedded PCI
 * identifiers provided by the driver.
 */
void
rte_eth_driver_register(struct eth_driver *eth_drv)
{
    eth_drv->pci_drv.devinit = rte_eth_dev_init;
    eth_drv->pci_drv.devuninit = rte_eth_dev_uninit;
    rte_eal_pci_register(&eth_drv->pci_drv);
}