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)相关