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

bnxt里涉及中断相关的函数汇总(2022/7/25)

主要涉及以下5个函数,分别来看看他们都做了些什么吧。

    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:

/* 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_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;
}

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;
}

其中上面的4个函数都涉及到了,bnxt_do_disable_intr函数和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_do_disable_intr:

static void inline
bnxt_do_disable_intr(struct bnxt_cp_ring *cpr)
{
    if (cpr->ring.phys_id != (uint16_t)HWRM_NA_SIGNATURE)
        BNXT_CP_DISABLE_DB(&cpr->ring);
}

而BNXT_CP_ENABLE_DB和BNXT_CP_DISABLE_DB,BNXT_CP_IDX_ENABLE_DB都是宏。

#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)

所以,在上面的4个函数里,其实都是写doorbell寄存器的操作,关键是理解这几个宏有什么不同,我们接着分析:

首先是BNXT_CP_DISABLE_DB:

它有CMPL_DOORBELL_MASK位,这个位是屏蔽中断的意思,
因为这个宏都是在中断函数里面调用的,所以后续有中断来,我先屏蔽它们,先把当前的事情做完。。

BNXT_CP_ENABLE_DB:

这个刚好和上面那个相反,可以接收中断的意思,不过这里是这个意思吗?感觉也不太好理解,就写一个带了CMPL_DOORBELL_KEY_CMPL的位就使能了?可以让后续的中断来了?
不过从另一个角度分析,此次写操作,它没有带CMPL_DOORBELL_MASK这个,其实就是相当于或许可以有中断触发了,对吧。

这两个就有意思了,因为它带cons,,,实现分析下在哪里会调用它们。
BNXT_CP_IDX_ENABLE_DB:

在这里插入代码片

BNXT_CP_IDX_DISABLE_DB:

在这里插入代码片

bnxt_msix_intr_assign:
这个函数其实是相当于申请中断号和设置中断回调函数的。
其中涉及的回调函数有bnxt_handle_def_cp和bnxt_handle_rx_cp。

static int
bnxt_msix_intr_assign(if_ctx_t ctx, int msix)
{
    struct bnxt_softc *softc = iflib_get_softc(ctx);
    int rc;
    int i;
    char irq_name[16];
    //def_cp
    rc = iflib_irq_alloc_generic(ctx, &softc->def_cp_ring.irq, softc->def_cp_ring.ring.id + 1, IFLIB_INTR_ADMIN,
                bnxt_handle_def_cp, softc, 0, "def_cp");
    if (rc) {
        device_printf(iflib_get_dev(ctx),  "Failed to register default completion ring handler\n");
        return rc;
    }
    //rx
    for (i = 0; i<softc->scctx->isc_nrxqsets; i++) {
        snprintf(irq_name, sizeof(irq_name), "rxq%d", i);
        rc = iflib_irq_alloc_generic(ctx, &softc->rx_cp_rings[i].irq, softc->rx_cp_rings[i].ring.id + 1, IFLIB_INTR_RX,
            bnxt_handle_rx_cp, &softc->rx_cp_rings[i], i, irq_name);
        if (rc) {
            device_printf(iflib_get_dev(ctx), "Failed to register RX completion ring handler\n");
            i--;
            goto fail;
        }
    }
    //tx
    for (i=0; i<softc->scctx->isc_ntxqsets; i++)
        iflib_softirq_alloc_generic(ctx, NULL, IFLIB_INTR_TX, NULL, i, "tx_cp");

    return rc;
fail:
    for (; i>=0; i--)
        iflib_irq_free(ctx, &softc->rx_cp_rings[i].irq);
    iflib_irq_free(ctx, &softc->def_cp_ring.irq);
    return rc;
}

bnxt_handle_def_cp:

//中断处理回调函数
static int
bnxt_handle_def_cp(void *arg)
{
    struct bnxt_softc *softc = arg;

    BNXT_CP_DISABLE_DB(&softc->def_cp_ring.ring);
    GROUPTASK_ENQUEUE(&softc->def_cp_task); //这个后面在分析
    return FILTER_HANDLED;
}

bnxt_handle_rx_cp:

//中断处理回调函数
static int
bnxt_handle_rx_cp(void *arg)
{
    struct bnxt_cp_ring *cpr = arg;

    /* Disable further interrupts for this queue */
    BNXT_CP_DISABLE_DB(&cpr->ring);
    return FILTER_SCHEDULE_THREAD;
}

这两个回调函数里面做的事情由此看来也是调用BNXT_CP_DISABLE_DB写doorbell寄存器的。

bnxt_ring 结构体和bnxt_cp_ring 结构体各成员变量含义分析(2022/7/25)

bnxt_ring :

struct bnxt_ring {
    uint64_t        paddr;
    vm_offset_t     doorbell;
    caddr_t         vaddr;
    struct bnxt_softc   *softc;
    uint32_t        ring_size;  /* Must be a power of two */
    uint16_t        id;     /* Logical ID */
    uint16_t        phys_id;
    struct bnxt_full_tpa_start *tpa_start;
};

一个一个的分析吧,感觉就差这一块,差不多就把整个发包的逻辑搞明白了。
首先是softc成员,不用多说,这指向的是我们bnxt这个驱动程序的结构体。
paddr和vaddr:
它两赋值的地方在相应的alloc函数里面:

softc->tx_cp_rings[i].ring.vaddr = vaddrs[i * ntxqs];
softc->tx_cp_rings[i].ring.paddr = paddrs[i * ntxqs];

其中,vaddrs和paddrs,ntxqs都是在iflib那边调用该函数时传过来的参数。
其中paddr值在后面用hwrm_ring_alloc(该命令为环分配和操作做一些基本准备)时会被填充进去,
其实它就是一个值,这个值指向环的页表的首地址(可以这么理解吧),其实就是申请了一大块的内存地址,paddr指向这一大块内存地址的起始。
这一大块内存地址里面按照字节来理解的话,它被划分为了一个个的BD结构体,这个结构体的类型后面再说。这是个物理地址,而
vaddrs是虚拟地址,为什么要将这个地址告诉硬件呢??这个不太敢肯定的说,后面深入看的时候在记录,但敢肯定的是,肯定是为了硬件和主机内存建立起联系嘛,对吧。

doorbell:

这个是doorbell寄存器的地址,其实这里我的疑问是有几个这样的寄存器,只能通过芯片手册取了解了。。。
后面,比如要发包时,我将这个tx ring相关的doorbell写入相关的idx就可以了。

赋值的地方在这里,是在一个循环里。
softc->tx_rings[i].id = (softc->scctx->isc_nrxqsets * 2) + 1 + i;
softc->tx_rings[i].doorbell = softc->tx_rings[i].id * 0x80; //linux那边这里也要乘以0x80

其实按照这里做一个猜想,就是一个ring应该是有一个专门的与之关联的doorbell寄存器的。

ring_size:
这个值我觉得是这个对应的ring里面他有几个BD这个的结构体。

赋值的地方:
softc->tx_rings[i].ring_size = softc->scctx->isc_ntxd[1];

id:
ring的逻辑id,大概作用就是用来标记主机内存这边用malloc申请一个ring结构时的标记。

初始值:
softc->tx_rings[i].id = (softc->scctx->isc_nrxqsets * 2) + 1 + i;
softc->tx_rings[i].doorbell = softc->tx_rings[i].id * 0x80;

使用的地方,填充到这里在hwrm_alloc_ring里面
//要分配的环的逻辑环号。该值确定门铃区域中将对环进行更新的位置
 req.logical_id = htole16(ring->id);

//与之关联的tx ring的cmpl ring的id,,这些id的作用主要是为了标识相应的ring的,
//而实际上这个ring是有硬件决定的,也就是ring->phys_id
req.cmpl_ring_id = htole16(cmpl_ring_id);

//硬件回复
ring->phys_id = le16toh(resp->ring_id);
也就是说,我给硬件一个id号为1,硬件回复的也可能是2,,以硬件为主。

phys_id:
这个phys_id我感觉没多大作用,从整个代码上看,它的初值就是-1,然会会在调用hwrm_alloc_ring是,会得到一个新的值

ring->phys_id = le16toh(resp->ring_id);

后面做一些什么操作的时候就用这个值做一些判断,主要就是说有没有调用hwrm_alloc_ring系列的函数,如果你没有调用,我就不让你做这些操作。。

tpa_start:
这个暂时先不分析吧。

bnxt_cp_ring :

struct bnxt_cp_ring {
    struct bnxt_ring    ring;
    struct if_irq       irq;
    uint32_t        cons;
    bool            v_bit;      /* Value of valid bit */
    struct ctx_hw_stats *stats;
    uint32_t        stats_ctx_id;
    uint32_t        last_idx;   /* Used by RX rings only
                         * set to the last read pidx
                         */
};

这个结构体的成员也是一个一个分析吧。
ring:
不必多说,cmpl ring也是一个ring嘛,所以有点类似与继承,把ring的特性继承过来了。
irq:
与中断有关。这个的作用和最上面的分析有关。
cons和v_bit的作用也不展开了,在前面已经分析了。
stats和stats_ctx_id这个是和统计有关的。。。先不关心。
last_idx看注释是和RX ring有关,我们先不分析。

bnxt_hwrm_ring_alloc调用情况分析(2022/7/25)

在这里插入图片描述
不难看出,一个调用了6次这个函数,也就是说,在驱动程序里面希望从硬件得到6个独一无二的ring。
根据type可以看出,有3个是L2_CMPL(softc->rx_cp_rings[i].ring,softc->def_cp_ring.ring,softc->tx_cp_rings[i].ring),
2个是RX(softc->rx_rings[i], softc->ag_rings[i]),1个是TX(softc->tx_rings[i])。

FreeBSD驱动开发常用的API汇总(2022/7/27)

DMA相关


申请硬件资源相关

#include<sys/param.h>
#include<sys/bus.h>
#include<machine/resource.h>
#include<sys/rman.h>
#include<machine/bus.h>

struct resource *
bus_alloc_resource(device_t dev, int type, int *rid, rman_res_t start,
    rman_res_t end, rman_res_t count, u_int flags);

struct resource *
bus_alloc_resource_any(device_t dev, int type, int *rid, unsigned int flags);

void
bus_release_resources(device_t dev, const struct resource_spec *rs,
    struct resource **res);
。。。。

中断相关

#include<sys/param.h>
#include<sys/bus.h>
int
bus_setup_intr(device_t dev, struct resource *r, int flags,
    driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep);
参数
dev:发出中断的设备
r:bus_alloc_resource的返回值(这个函数一般就回去申请相关的中断号资源了)
flags:区分不同类别的中断处理程序
filter和handler:中断处理函数
arg:传给中断处理函数的参数
一般是用handler回调函数
int
bus_teardown_intr(device_t dev, struct resource *r, void *cookie);
取消中断处理程序的注册。

任务队列(task_queue)相关


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值