FreeBSD bnxt以太网驱动源码阅读记录一:

相关开发命令

kldstat
kldload ./xxx.ko
kldunload xxx
pciconf -l
dmesg
ifconfig
//查看CPU核
cat /var/run/dmesg.boot |grep CPU 

重要结构体间的关系(2022/7/13)

struct device_method {
    const char *desc;
    void   *const func;
};
struct driver {
    const char *name;
    const struct device_method *methods;
    uint32_t size;
};
struct if_shared_ctx {
    //...
    driver_t *isc_driver;
    //...
};
重要结构体的内存申请:
(1)if_shared_ctx   变量定义的形式。
(2)device_t dev    上电时枚举
(3)if_ctx_t ctx    iflib_device_register函数里malloc申请
(4)if_t ifp      iflib_register函数里if_alloc申请(类似于netdevice)
(5)if_softc_ctx   这个是一个变量,在if_ctx_t 成员里,而bnxt_softc里面的成员if_softc_ctx是一个指针,指向的是if_ctx_t 里面的if_softc_ctx。。。。。有点绕。
(6)bnxt_softc      iflib_device_register函数里sc = malloc(sctx->isc_driver->size, M_IFLIB, M_WAITOK|M_ZERO);
申请的,注意size。

这几个结构体之间的关系是,ifp作为ctx(if_ctx_t)的成员,而if_ctx_t,if_shared_ctx,device_t,if_softc_ctx,又作为bnxt_softc的成员。
if_shared_ctx,dev,ifp,bnxt_softc都作为ctx的成员。

而从if_ctx_t 的角度看,应该是所有的成员都是包含在if_ctx_t 里面的。

关于static device_method_t bnxt_iflib_methods结构里成员函数的调用问题(2022/7/13)

static int
iflib_register(if_ctx_t ctx)
{
    //...
    driver_t *driver = sctx->isc_driver;
 /*
  * Initialize our context's device specific methods
  */
    kobj_init((kobj_t) ctx, (kobj_class_t) driver);
    kobj_class_compile((kobj_class_t) driver);
    //...
}
应该所有的成员都会调用一遍,但是调用顺序和定义顺序不是一致的,具体怎么调用的要对kobj这个结构分析。

pci_mapping与pci_mapping_free(2022/7/13)

在attach_pre函数里,从设备的配置空间的bar寄存器当中获取资源,用于后面写寄存器。
这里涉及的写或者读寄存器主要有
(1)doorbell(写生产者的index)
(2)hwrm(用于给硬件发消息)
它们都是

struct bnxt_bar_info {
    struct resource     *res;
    bus_space_tag_t     tag;
    bus_space_handle_t  handle;
    bus_size_t      size;
    int         rid;
};

类型,具体的成员含义不太清楚。

主要涉及的api:

bus_alloc_resource_any
bus_release_resource
bar->tag = rman_get_bustag(bar->res);
bar->handle = rman_get_bushandle(bar->res);
bar->size = rman_get_size(bar->res);
bus_space_write_4
//...

alloc_hwrm_dma_mem接口(2022/7/13)

接口申请dma内存,用于给硬件发送信息,然后硬件回复信息。

iflib_dma_alloc(softc->ctx, PAGE_SIZE, &softc->hwrm_cmd_resp, BUS_DMA_NOWAIT);

linux bnxt open函数里队列内存申请相关步骤(2022/7/14)

bnxt_open
        __bnxt_open_nic
		                bnxt_alloc_mem
						bnxt_init_napi
						bnxt_request_irq
						bnxt_init_nic
						                bnxt_init_cp_rings
										bnxt_init_rx_rings
										bnxt_init_tx_rings
										bnxt_init_ring_grps
										bnxt_init_vnics
										bnxt_init_chip
										                bnxt_hwrm_stat_ctx_alloc
														bnxt_hwrm_ring_grp_alloc
														bnxt_hwrm_vnic_alloc
														bnxt_setup_vnic
														bnxt_hwrm_set_vnic_filter
														bnxt_hwrm_ring_alloc

bnxt_attch_pre初始化函数里hwrm相关的api总结(2022/7/14)

bnxt_hwrm_ver_get函数:

按照一定的消息格式给硬件发送消息,然后得到硬件的回复消息,将回复的结果给
softc->ver_info结构体的成员赋值。

/* All the version information for the part */
#define BNXT_VERSTR_SIZE    (3*3+2+1)   /* ie: "255.255.255\0" */
#define BNXT_NAME_SIZE      17
struct bnxt_ver_info {
    uint8_t     hwrm_if_major;
    uint8_t     hwrm_if_minor;
    uint8_t     hwrm_if_update;
    char        hwrm_if_ver[BNXT_VERSTR_SIZE];
    char        driver_hwrm_if_ver[BNXT_VERSTR_SIZE];
    char        hwrm_fw_ver[BNXT_VERSTR_SIZE];
    char        mgmt_fw_ver[BNXT_VERSTR_SIZE];
    char        netctrl_fw_ver[BNXT_VERSTR_SIZE];
    char        roce_fw_ver[BNXT_VERSTR_SIZE];
    char        phy_ver[BNXT_VERSTR_SIZE];
    char        pkg_ver[64];

    char        hwrm_fw_name[BNXT_NAME_SIZE];
    char        mgmt_fw_name[BNXT_NAME_SIZE];
    char        netctrl_fw_name[BNXT_NAME_SIZE];
    char        roce_fw_name[BNXT_NAME_SIZE];
    char        phy_vendor[BNXT_NAME_SIZE];
    char        phy_partnumber[BNXT_NAME_SIZE];

    uint16_t    chip_num;
    uint8_t     chip_rev;
    uint8_t     chip_metal;
    uint8_t     chip_bond_id;
    uint8_t     chip_type;

    uint8_t     hwrm_min_major;
    uint8_t     hwrm_min_minor;
    uint8_t     hwrm_min_update;

    struct sysctl_ctx_list  ver_ctx;
    struct sysctl_oid   *ver_oid;
};

其余的还有:

int vb_hwrm_ver_get(struct vb_softc *softc);
int vb_hwrm_nvm_get_dev_info(struct vb_softc * softc);
int vb_hwrm_func_drv_register(struct vb_softc *softc);
int vb_hwrm_func_rgtr_async_events(struct vb_softc *softc, unsigned long *bmap, int bmap_size);
int vb_hwrm_func_qcaps(struct vb_softc * softc);
int vb_hwrm_func_qcfg(struct vb_softc * softc);
int vb_hwrm_queue_qportcfg(struct vb_softc * softc);
int vb_hwrm_func_reset(struct vb_softc * softc);

这些函数先做的事情都是询问硬件,比如询问硬件支持几个队列,硬件回复以后,我们就可以把值赋给软件,然后开始后续的工作了。

struct bnxt_softc结构体里面重要的成员(结构体汇总)(2022/7/15)

重要结构体:
第一档:
1device_t    dev; //pci设备
2if_ctx_t    ctx; //iflib
3if_softc_ctx_t  scctx; //iflib
4if_shared_ctx_t sctx; //类似于linux的ops操作
5struct ifmedia  *media;
第二档:
1struct bnxt_hw_lro; //1 分片功能
2struct bnxt_ver_info; //1 获取硬件参数
3struct bnxt_nvram_info; //1 获取硬件参数
4struct bnxt_cp_ring;   //3
5struct bnxt_bar_info;  //2 获取bar空间资源,用于后面写寄存器,例如doorbell
6struct bnxt_link_info  //1
7struct bnxt_func_info;  //1
8struct bnxt_func_qcfg;  //1
9struct bnxt_pf_info pf;  //1
10struct bnxt_vf_info vf;  //1
11struct iflib_dma_info //7  dma数据传输
12struct bnxt_cos_queue  //1
13struct rx_port_stats  //1
14struct tx_port_stats  //1
15struct bnxt_ring       //3
16struct bnxt_vnic_info  //1
17struct bnxt_grp_info  //1

其中有这些结构体变量是指针形式:

struct rx_port_stats    *rx_port_stats;
struct tx_port_stats    *tx_port_stats;

struct bnxt_ring    *tx_rings;
struct bnxt_ring    *ag_rings;
struct bnxt_ring    *rx_rings;

struct bnxt_cp_ring *tx_cp_rings;
struct bnxt_cp_ring *rx_cp_rings;

struct bnxt_grp_info    *grp_info;

//这两个是在attch_pre初始化函数里通过malloc申请的内存,通过hwrm接口询问硬件,获取到相关值以后,给它们的成员变量赋值。
struct bnxt_ver_info    *ver_info;
struct bnxt_nvram_info  *nvm_info;

需要重点分析其他的结构体指针变量是如何申请内存的,申请多大内存,由什么决定的?
在bnxt_tx_queues_alloc函数里,
softc->tx_cp_rings = malloc(sizeof(struct bnxt_cp_ring) * ntxqsets, M_DEVBUF, M_NOWAIT | M_ZERO);
softc->tx_rings = malloc(sizeof(struct bnxt_ring) * ntxqsets,  M_DEVBUF, M_NOWAIT | M_ZERO);
 
tx_stats....变量的dma映射也是在这个函数完成的。       

在bnxt_rx_queues_alloc函数里,
softc->rx_cp_rings = malloc(sizeof(struct bnxt_cp_ring) * nrxqsets,  M_DEVBUF, M_NOWAIT | M_ZERO);
softc->rx_rings = malloc(sizeof(struct bnxt_ring) * nrxqsets, M_DEVBUF, M_NOWAIT | M_ZERO); 
softc->ag_rings = malloc(sizeof(struct bnxt_ring) * nrxqsets,  M_DEVBUF, M_NOWAIT | M_ZERO);       
softc->grp_info = malloc(sizeof(struct bnxt_grp_info) * nrxqsets,  M_DEVBUF, M_NOWAIT | M_ZERO); 
 
//其中这两个idi_vaddr是dma的虚拟地址      
softc->rx_port_stats = (void *) softc->hw_rx_port_stats.idi_vaddr;
softc->tx_port_stats = (void *) softc->hw_tx_port_stats.idi_vaddr;

所以:这些有关与队列相关的结构体申请多大的内存空间是由ntxqsets决定的,再接着追踪传入的ntxqsets值是多少。
从打印来看是4.。。。。明天在追踪为什么是4.

ntxqsets值继续追踪(2022/7/18)

在iflib.c文件里,
iflib_queues_alloc(if_ctx_t ctx)函数里面,
if ((err = IFDI_TX_QUEUES_ALLOC(ctx, vaddrs, paddrs, ntxqs, ntxqsets)) != 0)
其中,IFDI_TX_QUEUES_ALLOC调用的就是bnxt_tx_queues_alloc函数,所以ntxqsets就是传入的实参,这个实参的值,得到的结果,从代码逻辑上分析还是比较复杂的,应该有它内部的一套逻辑,但是从这套逻辑来看,与硬件的cpu核数有关。

其他的类似于bnxt开头的一系列函数也都是在iflib这边调用的,调用形式类似于IFDI_XXX_XXX…

static device_method_t bnxt_iflib_methods[] = {
    DEVMETHOD(ifdi_tx_queues_alloc, bnxt_tx_queues_alloc),
    DEVMETHOD(ifdi_rx_queues_alloc, bnxt_rx_queues_alloc),
    DEVMETHOD(ifdi_queues_free, bnxt_queues_free),

    DEVMETHOD(ifdi_attach_pre, bnxt_attach_pre),
    DEVMETHOD(ifdi_attach_post, bnxt_attach_post),
    DEVMETHOD(ifdi_detach, bnxt_detach),

    DEVMETHOD(ifdi_init, bnxt_init),
    DEVMETHOD(ifdi_stop, bnxt_stop),
    DEVMETHOD(ifdi_multi_set, bnxt_multi_set),
    DEVMETHOD(ifdi_mtu_set, bnxt_mtu_set),
    DEVMETHOD(ifdi_media_status, bnxt_media_status),
    DEVMETHOD(ifdi_media_change, bnxt_media_change),
    DEVMETHOD(ifdi_promisc_set, bnxt_promisc_set),
    DEVMETHOD(ifdi_get_counter, bnxt_get_counter),
    DEVMETHOD(ifdi_update_admin_status, bnxt_update_admin_status),
    DEVMETHOD(ifdi_timer, bnxt_if_timer),

    DEVMETHOD(ifdi_intr_enable, bnxt_intr_enable),
    DEVMETHOD(ifdi_tx_queue_intr_enable, bnxt_tx_queue_intr_enable),
    DEVMETHOD(ifdi_rx_queue_intr_enable, bnxt_rx_queue_intr_enable),
    DEVMETHOD(ifdi_intr_disable, bnxt_disable_intr),
    DEVMETHOD(ifdi_msix_intr_assign, bnxt_msix_intr_assign),

    DEVMETHOD(ifdi_vlan_register, bnxt_vlan_register),
    DEVMETHOD(ifdi_vlan_unregister, bnxt_vlan_unregister),

    DEVMETHOD(ifdi_priv_ioctl, bnxt_priv_ioctl),

    DEVMETHOD(ifdi_suspend, bnxt_suspend),
    DEVMETHOD(ifdi_shutdown, bnxt_shutdown),
    DEVMETHOD(ifdi_resume, bnxt_resume),

    DEVMETHOD_END
};

bnxt_softc结构体里nrxqsets与ntxqsets赋值时机(2022/7/18)

softc->ntxqsets = ntxqsets;
softc->nrxqsets = nrxqsets;

这两个值都是在queue的alloc函数里面赋值的,后面大多数的操作都是利用的这两个值进行循环。

写DB时index的格式(2022/7/18)

tx和rx:
32bit:
4bit(key)+4bit(reserved)+idx(24)

其中,tx的key是0,rx的key是1

bnxt_softc结构体里各个结构体成员间的关系分析(与队列有关的)(2022/7/18)

感觉这点比较难,要想实现最简单的发包或者收包功能,感觉需要分析清楚,iflib库做了什么,而我们的驱动程序又要做什么,这是一点,要搞清楚这一点,必须要分析清楚这几个结构体之间的关系。。。。。
好吧,接着分析。。

//....

FreeBSD-MSI相关API(2022/7/19)

#include<dev/pci/pcivar.h>

int pci_msix_count(device_t dev);
int pci_msi_count(device_t dev);
返回值为设备支持的msi/msix消息个数。

int pci_alloc_msix(device_t dev, int *count);
int pci_alloc_msi(device_t dev, int *count);
向设备申请count个消息,如果设备没有那么多,返回值可能实际会小于count个。

int pci_release_msi(device_t dev);
释放掉由上面两个alloc申请的msi/msix消息个数。

iflib_device_register函数里调用bnxt驱动函数汇总(2022/7/20)

昨天改了一些代码,驱动挂在了iflib_device_register函数的最后一步,修改了一个地方,iflib_device_register函数的流程应该走完了(但是不太确定,可能是挂在iflib_add_device_sysctl_post函数里面了),程序又挂了,这下不知道挂在那里了。。
这里汇总下,iflib_device_register函数里面会调用到bnxt驱动这边的那些函数。

IFDI_ATTACH_PRE
IFDI_INTR_DISABLE
IFDI_MSIX_INTR_ASSIGN
IFDI_ATTACH_POST

除了这些,间接调用的还有许多,例如bnxt_init这些。。。。

应该就这么多了,但是这些函数都跑玩了。

bnxt里面中断函数分析(2022/7/20)

涉及的有这些。。

DEVMETHOD(ifdi_intr_enable, bnxt_intr_enable),
DEVMETHOD(ifdi_tx_queue_intr_enable, bnxt_tx_queue_intr_enable),
DEVMETHOD(ifdi_rx_queue_intr_enable, bnxt_rx_queue_intr_enable),
DEVMETHOD(ifdi_intr_disable, bnxt_disable_intr),
DEVMETHOD(ifdi_msix_intr_assign, bnxt_msix_intr_assign),

也就是:
bnxt_intr_enable 
bnxt_tx_queue_intr_enable    
bnxt_rx_queue_intr_enable    
bnxt_disable_intr
bnxt_msix_intr_assign

这些函数和队列关系比较大,也都会在iflib.c里面调用。。。
先分析下函数的实现吧。

bnxt_intr_enable (从描述来看是使能所有的中断的,上面没有tx队列相关的。。因为tx时不需要中断。。):

/* Enable all interrupts */
static void
bnxt_intr_enable(if_ctx_t ctx)
{
    struct bnxt_softc *softc = iflib_get_softc(ctx);
    int i;
    bnxt_do_enable_intr(&softc->def_cp_ring);
    for (i = 0; i < softc->nrxqsets; i++)
        bnxt_do_enable_intr(&softc->rx_cp_rings[i]);
    return;
}

bnxt_disable_intr(禁止中断):

/* Disable all interrupts */
static void
bnxt_disable_intr(if_ctx_t ctx)
{
    struct bnxt_softc *softc = iflib_get_softc(ctx);
    int i;
    /*
     * NOTE: These TX interrupts should never get enabled, so don't
     * update the index
     */
    for (i = 0; i < softc->ntxqsets; i++)
        bnxt_do_disable_intr(&softc->tx_cp_rings[i]);
    for (i = 0; i < softc->nrxqsets; i++)
        bnxt_do_disable_intr(&softc->rx_cp_rings[i]);
    return;
}

bnxt_tx_queue_intr_enable:

/* Enable interrupt for a single queue */
static int
bnxt_tx_queue_intr_enable(if_ctx_t ctx, uint16_t qid)
{
    struct bnxt_softc *softc = iflib_get_softc(ctx);
    bnxt_do_enable_intr(&softc->tx_cp_rings[qid]);
    return 0;
}

bnxt_rx_queue_intr_enable:

static int
bnxt_rx_queue_intr_enable(if_ctx_t ctx, uint16_t qid)
{
    struct bnxt_softc *softc = iflib_get_softc(ctx);
    bnxt_do_enable_intr(&softc->rx_cp_rings[qid]);
    return 0;
}

这两个函数都是针对单个队列的enable。。。tx也需要enable吗?

且这几个函数都是调用的bnxt_do_enable_intr函数做的操作。。。为了揭开谜底,来看看bnxt_do_enable_intr做了些什么吧。

static void inline
bnxt_do_enable_intr(struct bnxt_cp_ring *cpr)
{
    if (cpr->ring.phys_id != (uint16_t)HWRM_NA_SIGNATURE) {
        /* First time enabling, do not set index */
        if (cpr->cons == UINT32_MAX)
            BNXT_CP_ENABLE_DB(&cpr->ring);
        else
            BNXT_CP_IDX_ENABLE_DB(&cpr->ring, cpr->cons);
    }
}

这里又涉及BNXT_CP_ENABLE_DB和BNXT_CP_IDX_ENABLE_DB,其实这些都是写寄存器操作了。。接着分析。。

/*
 * If we update the index, a write barrier is needed after the write to ensure
 * the completion ring has space before the RX/TX ring does.  Since we can't
 * make the RX and AG doorbells covered by the same barrier without remapping
 * MSI-X vectors, we create the barrier over the enture doorbell bar.
 * TODO: Remap the MSI-X vectors to allow a barrier to only cover the doorbells
 *       for a single ring group.
 *
 * A barrier of just the size of the write is used to ensure the ordering
 * remains correct and no writes are lost.
 */
 //CMP 
#define BNXT_CP_DISABLE_DB(ring) do {                       \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, 0,              \
        (ring)->softc->doorbell_bar.size, BUS_SPACE_BARRIER_WRITE);     \
    bus_space_write_4((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell,       \
        htole32(CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_MASK));      \
} while (0)

#define BNXT_CP_ENABLE_DB(ring) do {                        \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, 0,              \
        (ring)->softc->doorbell_bar.size, BUS_SPACE_BARRIER_WRITE);     \
    bus_space_write_4((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell,       \
        htole32(CMPL_DOORBELL_KEY_CMPL));                   \
} while (0)

#define BNXT_CP_IDX_ENABLE_DB(ring, cons) do {                  \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_write_4((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell,       \
        htole32(CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_IDX_VALID |      \
        (cons)));                               \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, 0,              \
        (ring)->softc->doorbell_bar.size, BUS_SPACE_BARRIER_WRITE);     \
} while (0)

#define BNXT_CP_IDX_DISABLE_DB(ring, cons) do {                 \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_write_4((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell,       \
        htole32(CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_IDX_VALID |      \
        CMPL_DOORBELL_MASK | (cons)));                  \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, 0,              \
        (ring)->softc->doorbell_bar.size, BUS_SPACE_BARRIER_WRITE);     \
} while (0)


//TX
#define BNXT_TX_DB(ring, idx) do {                      \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_write_4(                          \
        (ring)->softc->doorbell_bar.tag,                    \
        (ring)->softc->doorbell_bar.handle,                 \
        (ring)->doorbell, htole32(TX_DOORBELL_KEY_TX | (idx)));     \
} while (0)

//RX
#define BNXT_RX_DB(ring, idx) do {                      \
    bus_space_barrier((ring)->softc->doorbell_bar.tag,          \
        (ring)->softc->doorbell_bar.handle, (ring)->doorbell, 4,        \
        BUS_SPACE_BARRIER_WRITE);                       \
    bus_space_write_4(                          \
        (ring)->softc->doorbell_bar.tag,                    \
        (ring)->softc->doorbell_bar.handle,                 \
        (ring)->doorbell, htole32(RX_DOORBELL_KEY_RX | (idx)));     \
} while (0)

这里看下来其实是按照cmp doorbell的格式写寄存器操作。。

hwrm_ring_alloc接口总结(2022/7/20)

这个接口填充了一堆消息给硬件,最后硬件回复了一个环相关的物理编号。。。
接口的主要作用就是预分配许多与环相关的缓冲区(物理内存),我把这些缓冲区的指针(指向这个缓冲区的首地址)等一系列信息,
其实就是许多个BD。。。。有数据过来时,我把当前BD的index写到寄存器里面。。。。网卡,有数据到了,你去相应的BD里把数据取走。。。。

编程手册指导阅读(2022/7/21)

4.1主机和设备进行交流。
利用BAR空间里面的资源,bar0给hwrm下发命令,bar2用作写doorbell,bar4用作MSI-X vector。。。
4.2硬件资源管理规格。
4.2.2主机软件接口模型
4.2.2.1doorbell格式
最后我们都是向doorbell寄存器写操作,doorbell寄存器需要按照它的格式写入。不能随便写一个数值进去。
4.2.2.2BD环结构
BD Ring Structures由驱动程序写入 tx Rings 和 RX Rings,
以向芯片指示主机中有更多缓冲区空间需要传输或可用于接收数据。。。
也就是先申请一大片缓冲区,然后告诉硬件,,后面我写index就可以了。
4.2.2.3完成环的结构
4.3驱动初始化
4.3.1向操作系统请求资源,这一步和bar空间哪里寻求资源的映射。
4.3.2做一些初始值,比如硬件是否支持硬件卸载等,
(1)队列数,环的数量设置
(2)环里面的BD数量设置
(3)coalescing parameters设置
(4)off-load
4.3.3通过hwrm接口做一些配置
涉及,hwrm_ver_get,hwrm_fw_set_time…
4.4打开设备
(1)从主机内存当中分配数据结构
1:TX/RX/completing rings
2:NIC attribute associated structures
3:Forward group IDs
4:Unicast Lists
5:Multicast List
6:RSS
(2)初始化所有的环
1:初始化所有的环和BDS
2:分配RX data 缓冲区和聚合环
3:通过hwrm接口向NIC请求资源
涉及,hwrm_ring_alloc,hwrm_ring_grp_alloc
hwrm_vnic_alloc,hwrm_vnic_rss_cos_lb_ctx_alloc
(3)配置默认的VNIC0和ring group 0
hwrm_vnic_cfg
(4)使能RSS 哈希在vnic 0
hwrm_vnic_rss_cfg
(5)使能features
hwrm_vnic_plcmodes_cfg
hwrm_vnic_tpa_cf
(6)设置中断的coalescing parameters
hwrm_ring_cmpl_ring_cfg_aggint_params
(7)使能中断与TX
4.5关闭设备
涉及一些资源的释放
4.6数据包的传输
4.7数据包的接收
4.8中断处理
4.9统计和计数
通过相关的hwrm_xxx_ctx_xxx命令向硬件
询问,得到统计的信息。。
4.10
4.11NVRAM(non-volatile memory) 配置
4.12驱动更新
4.13驱动加载
使用几个环,以及每个环有几个DB是由主机决定的。。。
在这里插入图片描述

bnxt_txrx系列的填充函数分析(2022/7/22)

总共有这些:

struct if_txrx bnxt_txrx  = {
    .ift_txd_encap = bnxt_isc_txd_encap,
    .ift_txd_flush = bnxt_isc_txd_flush,
    .ift_txd_credits_update = bnxt_isc_txd_credits_update,
    .ift_rxd_available = bnxt_isc_rxd_available,
    .ift_rxd_pkt_get = bnxt_isc_rxd_pkt_get,
    .ift_rxd_refill = bnxt_isc_rxd_refill,
    .ift_rxd_flush = bnxt_isc_rxd_flush,
    .ift_legacy_intr = bnxt_intr
};

其中tx:

    .ift_txd_encap = bnxt_isc_txd_encap,
    .ift_txd_flush = bnxt_isc_txd_flush,
    .ift_txd_credits_update = bnxt_isc_txd_credits_update,

rx:

    .ift_rxd_available = bnxt_isc_rxd_available,
    .ift_rxd_pkt_get = bnxt_isc_rxd_pkt_get,
    .ift_rxd_refill = bnxt_isc_rxd_refill,
    .ift_rxd_flush = bnxt_isc_rxd_flush,

bnxt_intr函数体的内容只有一些打印,看起来没什么大作用。

static int
bnxt_intr(void *sc)
{
    struct bnxt_softc *softc = (struct bnxt_softc *)sc;

    device_printf(softc->dev, "STUB: %s @ %s:%d\n", __func__, __FILE__, __LINE__);
    return ENOSYS;
}

先重点分析下有关tx的这3个函数做了什么:
bnxt_isc_txd_encap:
这个函数也不难,其实就是给BD字段的每个部分的字节赋值,其中,coal_now,no_cmpl在函数里面没有赋值,根据说明一般情况下我们不需要去设置这两个字段,所以函数里面没看到设置,也属于正常。
总体来说就是根据发送一个数据包,是否需要分片,如果需要分片,那么就需要使用多个BD了,如果数据包里面带vlan信息,那就需要更多的BD,然后就是根据需要的BD的个数,给它的成员赋值,赋值的地方主要有type和flag(packet_end,bd_cnt,ihint)。

static int
bnxt_isc_txd_encap(void *sc, if_pkt_info_t pi)
{
    struct bnxt_softc *softc = (struct bnxt_softc *)sc;
    //取到特定的环
    struct bnxt_ring *txr = &softc->tx_rings[pi->ipi_qsidx];
    //BD
    struct tx_bd_long *tbd; //
    struct tx_bd_long_hi *tbdh;
    bool need_hi = false;
    //
    uint16_t flags_type;
    uint16_t lflags;
    uint32_t cfa_meta;
    int seg = 0;

    /* If we have offloads enabled, we need to use two BDs. */
   // if ((pi->ipi_csum_flags & (CSUM_OFFLOAD | CSUM_TSO | CSUM_IP)) || pi->ipi_mflags & M_VLANTAG)
     //   need_hi = true;

    /* TODO: Devices before Cu+B1 need to not mix long and short BDs */
    need_hi = true;
   // need_hi = false;
    //得到特定的BD -> tdb
    pi->ipi_new_pidx = pi->ipi_pidx;
    tbd = &((struct tx_bd_long *)txr->vaddr)[pi->ipi_new_pidx];
    pi->ipi_ndescs = 0; 
    /* No need to byte-swap the opaque value */
    tbd->opaque = ((pi->ipi_nsegs + need_hi) << 24) | pi->ipi_new_pidx; //该字段必须在数据包的第一个 BD 上有效
    tbd->len = htole16(pi->ipi_segs[seg].ds_len);
    tbd->addr = htole64(pi->ipi_segs[seg++].ds_addr);
    //bd_cnt setup
    flags_type = ((pi->ipi_nsegs + need_hi) << TX_BD_SHORT_FLAGS_BD_CNT_SFT) & TX_BD_SHORT_FLAGS_BD_CNT_MASK;
    //ihint setup
    if (pi->ipi_len >= 2048)
        flags_type |= TX_BD_SHORT_FLAGS_LHINT_GTE2K;
    else
        flags_type |= bnxt_tx_lhint[pi->ipi_len >> 9];
/*
    if (need_hi) {
        flags_type |= TX_BD_LONG_TYPE_TX_BD_LONG;

        pi->ipi_new_pidx = RING_NEXT(txr, pi->ipi_new_pidx);
        tbdh = &((struct tx_bd_long_hi *)txr->vaddr)[pi->ipi_new_pidx];
        tbdh->mss = htole16(pi->ipi_tso_segsz);
        tbdh->hdr_size = htole16((pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen) >> 1);
        tbdh->cfa_action = 0;
        lflags = 0;
        cfa_meta = 0;
        if (pi->ipi_mflags & M_VLANTAG) {
            //TODO: Do we need to byte-swap the vtag here?
            cfa_meta = TX_BD_LONG_CFA_META_KEY_VLAN_TAG | pi->ipi_vtag;
            cfa_meta |= TX_BD_LONG_CFA_META_VLAN_TPID_TPID8100;
        }
        tbdh->cfa_meta = htole32(cfa_meta);
        if (pi->ipi_csum_flags & CSUM_TSO) {
            lflags |= TX_BD_LONG_LFLAGS_LSO | TX_BD_LONG_LFLAGS_T_IPID;
        }
        else if(pi->ipi_csum_flags & CSUM_OFFLOAD) {
            lflags |= TX_BD_LONG_LFLAGS_TCP_UDP_CHKSUM | TX_BD_LONG_LFLAGS_IP_CHKSUM;
        }
        else if(pi->ipi_csum_flags & CSUM_IP) {
            lflags |= TX_BD_LONG_LFLAGS_IP_CHKSUM;
        }
        tbdh->lflags = htole16(lflags);
    }
    else {
        flags_type |= TX_BD_SHORT_TYPE_TX_BD_SHORT; //
    }
*/
    //flags_type |= TX_BD_SHORT_TYPE_TX_BD_SHORT;
    for (; seg < pi->ipi_nsegs; seg++) {
        tbd->flags_type = htole16(flags_type);
        pi->ipi_new_pidx = RING_NEXT(txr, pi->ipi_new_pidx);
        tbd = &((struct tx_bd_long *)txr->vaddr)[pi->ipi_new_pidx];
        tbd->len = htole16(pi->ipi_segs[seg].ds_len);
        tbd->addr = htole64(pi->ipi_segs[seg].ds_addr);
        flags_type = TX_BD_SHORT_TYPE_TX_BD_SHORT;
    }
    flags_type |= TX_BD_SHORT_FLAGS_PACKET_END;
    tbd->flags_type = htole16(flags_type);
    pi->ipi_new_pidx = RING_NEXT(txr, pi->ipi_new_pidx);
    return 0;
}

bnxt_isc_txd_credits_update:

static int
bnxt_isc_txd_credits_update(void *sc, uint16_t txqid, bool clear)
{
    struct bnxt_softc *softc = (struct bnxt_softc *)sc;
    //取特定的环
    struct bnxt_cp_ring *cpr = &softc->tx_cp_rings[txqid];
    //转cmpl,这里应该是更新完pidx,由硬件把cmpl环的信息以cmpl环的结构
    //更新到这个vaddr地址里。。。。
    struct tx_cmpl *cmpl = (struct tx_cmpl *)cpr->ring.vaddr;
/*
tx_cmpl
type(0-5,value,tx_l2) + flags(6-15) + unused1(16-31) + opaque(32-63)
v(0) + errors(1-15) + unused2(16-31) + unused3(32-63)
*/
/*
cmp doorbell format:
idx(0-23) + reserved(24-25) + idx_vaild(26,此次写操作是否有效,1有效) + mask(27,与中断有关) + key(31-28,写doorbell的操作类型,2是cmpl ring)
htole32(CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_IDX_VALID | CMPL_DOORBELL_MASK | (cons)));
enable 没有mask这一位。
*/
    htole32(CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_IDX_VALID | CMPL_DOORBELL_MASK | (cons)));
    int avail = 0;
    uint32_t cons = cpr->cons;
    bool v_bit = cpr->v_bit;    //初值是1
    bool last_v_bit;
    uint32_t last_cons;
    uint16_t type;
    uint16_t err;

    for (;;) {

        //这里应该是把值取出来,先保存,后面还是要赋回去的。
        last_cons = cons;
        last_v_bit = v_bit;
        //根据读到的cons,然后进行++操作并判断是否超出了范围,如果超出了范围,就把cons更新为0?
        //v_bit变量,我感觉是为了判断当前的cons值是否是有效值如果是true,那就是有效的,false就是无效的。。
        NEXT_CP_CONS_V(&cpr->ring, cons, v_bit);
        //先把值放到缓存里,
        /*__builtin_prefetch(&((struct cmpl_base *)(cpr)->ring.vaddr)
            [((cons) + (CACHE_LINE_SIZE / sizeof(struct cmpl_base))) &  ((cpr)->ring.ring_size - 1)])*/
        CMPL_PREFETCH_NEXT(cpr, cons);
        //读取值,由于之前已经已经通过地址把值放到缓存里面了,所以这里可以快速把值取出来,
        //利用__builtin_prefetch可以提高cpu的性能(需要特定cpu的支持)
        /*((!!(((struct cmpl_base *)(cmp))->info3_v & htole32(CMPL_BASE_V))) == !!(v_bit) )*/
        //这里看样子值是1(传输成功了),也就是说info3_v应该等于v_bit
        if (!CMP_VALID(&cmpl[cons], v_bit))
            goto done;
        //0x3f,判断是什么类型的mpl,,这里肯定是TX_CMPL_TYPE_TX_L2了。
        type = cmpl[cons].flags_type & TX_CMPL_TYPE_MASK;
        switch (type) {
        case TX_CMPL_TYPE_TX_L2: //0
            //主要是根据这个字段,判断是否传输错误
            err = (le16toh(cmpl[cons].errors_v) & TX_CMPL_ERRORS_BUFFER_ERROR_MASK) >> TX_CMPL_ERRORS_BUFFER_ERROR_SFT;
            if (err)
                device_printf(softc->dev, "TX completion error %u\n", err);
            //cmpl[cons].opaque这个字段是从与之关联的TX BD的opaque复制过来的。
            avail += cmpl[cons].opaque >> 24;
            if (!clear)
                goto done;
            break;
        default:
            //这个是什么操作????
            if (type & 1) {
                NEXT_CP_CONS_V(&cpr->ring, cons, v_bit);
                if (!CMP_VALID(&cmpl[cons], v_bit))
                    goto done;
            }
            device_printf(softc->dev, "Unhandled TX completion type %u\n", type);
            break;
        }
    }
done:
    //这个avail的值感觉不是0,因为调用的地方进行判断时,看上去是要大于0的
    if (clear && avail) {
        cpr->cons = last_cons;
        cpr->v_bit = last_v_bit;
        BNXT_CP_IDX_DISABLE_DB(&cpr->ring, cpr->cons);
    }
    return avail;
}

bnxt_isc_txd_flush:这个函数主要就是把生产者的pindex写入doorbell寄存器,有疑问的地方就是,
为什么要写两次???
这里可能是,第一次写是传输BD,第二次是传输BD里面指向协议栈传输下来的数据,所以需要写两次

static void
bnxt_isc_txd_flush(void *sc, uint16_t txqid, qidx_t pidx)
{
    struct bnxt_softc *softc = (struct bnxt_softc *)sc;
    struct bnxt_ring *tx_ring = &softc->tx_rings[txqid];
    /* pidx is what we last set ipi_new_pidx to */
    BNXT_TX_DB(tx_ring, pidx);
    /* TODO: Cumulus+ doesn't need the double doorbell */
    BNXT_TX_DB(tx_ring, pidx);
    return;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值