中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

工信部網(wǎng)站備案注銷二級(jí)域名在線掃描

工信部網(wǎng)站備案注銷,二級(jí)域名在線掃描,宣傳推廣策略,濰坊中脈網(wǎng)站建設(shè)公司用戶通過ibv_create_cq接口創(chuàng)建完成隊(duì)列,函數(shù)原型和常見用法如下,本節(jié)以該用法為例看下cq的創(chuàng)建過程。 struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,void *cq_context,struct ibv_comp_channel *channel,int comp_vector); cq …

用戶通過ibv_create_cq接口創(chuàng)建完成隊(duì)列,函數(shù)原型和常見用法如下,本節(jié)以該用法為例看下cq的創(chuàng)建過程。

struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,void *cq_context,struct ibv_comp_channel *channel,int comp_vector);
cq = ibv_create_cq(ctx, ncqe, NULL, NULL, 0);             

用戶態(tài)

ncqe為cq隊(duì)列的容量,cqe_sz是cqe的大小,默認(rèn)64B;mlx5_alloc_cq_buf就是通過posix_memalign分配cq隊(duì)列的內(nèi)存,記錄到cq->buf_a。

static struct ibv_cq_ex *create_cq(struct ibv_context *context,const struct ibv_cq_init_attr_ex *cq_attr,int cq_alloc_flags,struct mlx5dv_cq_init_attr *mlx5cq_attr)
{...ncqe = align_queue_size(cq_attr->cqe + 1);cqe_sz = get_cqe_size(mlx5cq_attr);mlx5_alloc_cq_buf(to_mctx(context), cq, &cq->buf_a, ncqe, cqe_sz);cq->dbrec  = mlx5_alloc_dbrec(to_mctx(context), cq->parent_domain,&cq->custom_db);...
}

然后通過mlx5_alloc_dbrec分配dbr,dbr位于物理內(nèi)存,大小為8B,同時(shí)對(duì)齊到8B,記錄了軟件poll到了什么位置,即ci,以及cq的狀態(tài)信息,dbr地址會(huì)被記錄到cqc中,當(dāng)用戶執(zhí)行poll_cq之后會(huì)更新dbr,后續(xù)會(huì)具體介紹。

__be32 *mlx5_alloc_dbrec(struct mlx5_context *context, struct ibv_pd *pd,bool *custom_alloc)
{   struct mlx5_db_page *page;__be32 *db = NULL;int i, j;...
default_alloc:pthread_mutex_lock(&context->dbr_map_mutex);page = list_top(&context->dbr_available_pages, struct mlx5_db_page,available);if (page)goto found;page = __add_page(context);if (!page)goto out;found:...return db;
}

分配dbr的時(shí)候首先嘗試去空閑鏈表中獲取,如果拿不到則執(zhí)行__add_page,開始時(shí)空閑鏈表為空,因此執(zhí)行__add_page。

static struct mlx5_db_page *__add_page(struct mlx5_context *context)
{   struct mlx5_db_page *page;int ps = to_mdev(context->ibv_ctx.context.device)->page_size;int pp;int i;int nlong;int ret;pp = ps / context->cache_line_size; nlong = (pp + 8 * sizeof(long) - 1) / (8 * sizeof(long));page = malloc(sizeof *page + nlong * sizeof(long));if (!page) return NULL;if (mlx5_is_extern_alloc(context))ret = mlx5_alloc_buf_extern(context, &page->buf, ps);elseret = mlx5_alloc_buf(&page->buf, ps, ps);if (ret) {free(page);return NULL;}page->num_db  = pp;page->use_cnt = 0;for (i = 0; i < nlong; ++i)page->free[i] = ~0;cl_qmap_insert(&context->dbr_map, (uintptr_t) page->buf.buf,&page->cl_map);list_add(&context->dbr_available_pages, &page->available);return page;
}struct mlx5_db_page {cl_map_item_t           cl_map;struct list_node        available;struct mlx5_buf         buf;int             num_db;int             use_cnt;unsigned long           free[0];
};  

ps為page size,mlx5一次性分配一個(gè)物理頁用于存儲(chǔ)多個(gè)dbr,用結(jié)構(gòu)體mlx5_db_page page描述。通過mlx5_alloc_buf分配了大小為ps的內(nèi)存,即一個(gè)物理頁,地址記錄到page中buf。這里為了防止false sharing,將dbr地址對(duì)齊到了cache_line_size,因此一個(gè)物理頁能存儲(chǔ)dbr的數(shù)量pp為page大小除以cacheline大小,pp記錄到page的num_db,page中use_cnt初始化為0,表示這個(gè)page中還沒有dbr被占用。page中free數(shù)組相當(dāng)于一個(gè)bitmap,記錄了這個(gè)page中dbr的空閑情況,nlong表示為了記錄num_db個(gè)dbr需要幾個(gè)long,即最少需要幾個(gè)long才能有num_db個(gè)bit,free數(shù)組初始化為全1,表示所有dbr都為空閑。mlx通過樹和鏈表的方式管理每一個(gè)mlx5_db_page,因此將page插入到dbr_available_pages鏈表head,鏈表節(jié)點(diǎn)為list_node available,將page插入到dbr_map,樹節(jié)點(diǎn)為cl_map_item_t cl_map。

然后再回到mlx5_alloc_dbrec的邏輯

__be32 *mlx5_alloc_dbrec(struct mlx5_context *context, struct ibv_pd *pd,bool *custom_alloc)
{   ...++page->use_cnt;if (page->use_cnt == page->num_db)list_del(&page->available);for (i = 0; !page->free[i]; ++i)/* nothing */;j = ffsl(page->free[i]);--j;page->free[i] &= ~(1UL << j); db = page->buf.buf + (i * 8 * sizeof(long) + j) * context->cache_line_size;out:pthread_mutex_unlock(&context->dbr_map_mutex);return db;
}

首先增加use_cnt,表示又占用了一個(gè)dbr,如果use_cnt等于num_db,表示這個(gè)page已經(jīng)滿了,因此從空閑鏈表中刪除。遍歷free數(shù)組,找到第一個(gè)不為一的long,說明這個(gè)long里有空閑的dbr,然后通過ffsl找到free[i]中第一個(gè)為1的位置 j,然后將free[i]的第 j 位改為0,表示占用了,然后索引對(duì)應(yīng)的dbr,因?yàn)橐粋€(gè)long能存8 * sizeof(long)個(gè)dbr,因此這次分配的索引就是(i * 8 * sizeof(long) + j),最后將這個(gè)地址記錄到db。

然后回到create_cq邏輯。

static struct ibv_cq_ex *create_cq(struct ibv_context *context,const struct ibv_cq_init_attr_ex *cq_attr,int cq_alloc_flags,struct mlx5dv_cq_init_attr *mlx5cq_attr)
{...cq->dbrec[MLX5_CQ_SET_CI]   = 0; cq->dbrec[MLX5_CQ_ARM_DB]   = 0; cq->arm_sn          = 0; cq->cqe_sz          = cqe_sz;cq->flags           = cq_alloc_flags;cmd_drv->buf_addr = (uintptr_t) cq->buf_a.buf;cmd_drv->db_addr  = (uintptr_t) cq->dbrec;cmd_drv->cqe_size = cqe_sz;{struct ibv_cq_init_attr_ex cq_attr_ex = *cq_attr;cq_attr_ex.cqe = ncqe - 1;ret = ibv_cmd_create_cq_ex(context, &cq_attr_ex, &cq->verbs_cq,&cmd_ex.ibv_cmd, sizeof(cmd_ex),&resp_ex.ibv_resp, sizeof(resp_ex),CREATE_CQ_CMD_FLAGS_TS_IGNORED_EX);}...
}

初始化dbrec和cq,然后將buf地址,dbrec地址,cqe_sz記錄到cmd_drv,然后執(zhí)行ibv_cmd_create_cq_ex,進(jìn)入到了內(nèi)核態(tài)。

內(nèi)核態(tài)

dma映射

int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,struct ib_udata *udata)
{if (udata) {err = create_cq_user(dev, udata, cq, entries, &cqb, &cqe_size,&index, &inlen);...}
}

進(jìn)入內(nèi)核態(tài)后執(zhí)行mlx5_ib_create_cq,由于是用戶態(tài)的create_cq,因此執(zhí)行create_cq_user,cqb類型為mlx5_ifc_create_cq_in_bits,即第二章中介紹的create_cq的cmd,會(huì)在create_cq_user創(chuàng)建。

static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,struct mlx5_ib_cq *cq, int entries, u32 **cqb,int *cqe_size, int *index, int *inlen)
{struct mlx5_ib_create_cq ucmd = {};*cqe_size = ucmd.cqe_size;cq->buf.umem = ib_umem_get_peer(udata, ucmd.buf_addr,entries * ucmd.cqe_size,IB_ACCESS_LOCAL_WRITE, 0);...
}

entries為cq容量,cqe_size為cqe大小,然后執(zhí)行ib_umem_get_peer,因?yàn)橛脩魬B(tài)中的buf_addr或者dbr的地址均為虛擬地址,用戶態(tài)軟件使用虛擬地址訪問cq,但是硬件需要使用總線地址訪問cq,所以ib_umem_get_peer作用就是將虛擬地址連續(xù)的addr轉(zhuǎn)為總線地址。

struct ib_umem *ib_umem_get_peer(struct ib_udata *udata, unsigned long addr,                  size_t size, int access,                                                     unsigned long peer_mem_flags)                                                
{return __ib_umem_get(udata, addr, size, access,                                           IB_PEER_MEM_ALLOW | peer_mem_flags);
}

首先分配ib_umem umem,相關(guān)信息都會(huì)記錄到umem里。

struct ib_umem *__ib_umem_get(struct ib_udata *udata,unsigned long addr, size_t size, int access,unsigned long peer_mem_flags)
{struct ib_umem *umem;struct page **page_list;unsigned long dma_attr = 0;struct mm_struct *mm;unsigned long npages;int ret;struct scatterlist *sg = NULL;unsigned int gup_flags = FOLL_WRITE;unsigned long dma_attrs = 0;...umem = kzalloc(sizeof(*umem), GFP_KERNEL);if (!umem)return ERR_PTR(-ENOMEM);umem->context = context;umem->length     = size;umem->address    = addr;...
}

然后分配page_list,page_list用于保存接下來通過get_user_pages返回的物理頁,ib_umem_num_pages計(jì)算得到npages,表示這段內(nèi)存一共占用了多少個(gè)物理頁。然后創(chuàng)建sg_table,用于保存離散的物理頁集合,sg被賦值為sg_table的scatterlist sgl。

{page_list = (struct page **) __get_free_page(GFP_KERNEL);if (!page_list) {ret = -ENOMEM;goto umem_kfree;}npages = ib_umem_num_pages(umem);...cur_base = addr & PAGE_MASK;ret = sg_alloc_table(&umem->sg_head, npages, GFP_KERNEL);sg = umem->sg_head.sgl;...
}

接下來開始通過get_user_pages獲取入?yún)ddr這個(gè)虛擬地址對(duì)應(yīng)的物理頁集合,get_user_pages通過每個(gè)頁的虛擬地址找vma,找到之后通過follow_page_mask獲取對(duì)應(yīng)的物理頁,如果沒有分配物理頁就分配,并且pin住保證不會(huì)交換,因?yàn)閜age_list大小為一個(gè)物理頁,所以這里一次性最多傳進(jìn)去的page數(shù)為PAGE_SIZE / sizeof (struct page *)。

{while (npages) {cond_resched();down_read(&mm->mmap_sem);ret = get_user_pages_longterm(cur_base,min_t(unsigned long, npages,PAGE_SIZE / sizeof (struct page *)),gup_flags, page_list, NULL);if (ret < 0) {pr_debug("%s: failed to get user pages, nr_pages=%lu, flags=%u\n", __func__,min_t(unsigned long, npages,PAGE_SIZE / sizeof(struct page *)),gup_flags);up_read(&mm->mmap_sem);goto umem_release;}cur_base += ret * PAGE_SIZE;npages -= ret;sg = ib_umem_add_sg_table(sg, page_list, ret,dma_get_max_seg_size(context->device->dma_device),&umem->sg_nents);up_read(&mm->mmap_sem);}}

然后開始將返回的ret個(gè)物理頁通過ib_umem_add_sg_table加入到sg_table中,因?yàn)榈谝淮握{(diào)用的時(shí)候sg里還沒有保存page,所以可以通過sg_page判斷是否是第一次調(diào)用。對(duì)于非第一次的調(diào)用這里會(huì)嘗試合并本次操作的page到當(dāng)前的sg里,通過page_to_pfn(sg_page(sg))可以拿到當(dāng)前sg里物理頁的pfn,然后加上sg的物理頁數(shù)就得到了sg保存的連續(xù)物理頁后的第一個(gè)物理頁的pfn,如果和page_list[0]的pfn相等,說明是連續(xù)的,可以合并,通過設(shè)置update_cur_sg表示可以將page_list合并到當(dāng)前sg。

static struct scatterlist *ib_umem_add_sg_table(struct scatterlist *sg,struct page **page_list,unsigned long npages,unsigned int max_seg_sz,int *nents)
{unsigned long first_pfn;unsigned long i = 0;bool update_cur_sg = false;bool first = !sg_page(sg);if (!first && (page_to_pfn(sg_page(sg)) + (sg->length >> PAGE_SHIFT) ==page_to_pfn(page_list[0])))update_cur_sg = true;...
}

然后開始循環(huán)添加page_list的頁面到sg_table,拿到這次循環(huán)要處理的第一個(gè)page即first_page和他的pfn即first_pfn,然后從i往后看接下來有多少個(gè)物理頁是連續(xù)的,len表示連續(xù)的page數(shù)量,如果可以合并到當(dāng)前sg,那么直接更新sg的長度信息,然后continue。如果不能合并且不是第一次執(zhí)行,那么就需要通過sg_next切換到下一個(gè)sg并更新,最后返回當(dāng)前處理的sg。

static struct scatterlist *ib_umem_add_sg_table(struct scatterlist *sg,struct page **page_list,unsigned long npages,unsigned int max_seg_sz,int *nents)
{while (i != npages) {unsigned long len;struct page *first_page = page_list[i];first_pfn = page_to_pfn(first_page);                               */for (len = 0; i != npages &&first_pfn + len == page_to_pfn(page_list[i]) &&len < (max_seg_sz >> PAGE_SHIFT);len++)i++;/* Squash N contiguous pages from page_list into current sge */if (update_cur_sg) {if ((max_seg_sz - sg->length) >= (len << PAGE_SHIFT)) {sg_set_page(sg, sg_page(sg),sg->length + (len << PAGE_SHIFT),0);update_cur_sg = false;continue;}update_cur_sg = false;}/* Squash N contiguous pages into next sge or first sge */if (!first)sg = sg_next(sg);(*nents)++;sg_set_page(sg, first_page, len << PAGE_SHIFT, 0);first = false;}return sg;
}

最后通過sg_mark_end標(biāo)記當(dāng)前sg為最后一個(gè)sg,如果access屬性有IB_ACCESS_RELAXED_ORDERING,那么dma_attr需要設(shè)置上DMA_ATTR_WEAK_ORDERING,表示讀寫可以亂序。最后通過ib_dma_map_sg_attrs將sg_table的物理頁執(zhí)行dma映射,實(shí)際用的就是dma_map_sg_attrs。

struct ib_umem *__ib_umem_get(struct ib_udata *udata,unsigned long addr, size_t size, int access,unsigned long peer_mem_flags)
{...sg_mark_end(sg);if (access & IB_ACCESS_RELAXED_ORDERING)dma_attr |= DMA_ATTR_WEAK_ORDERING;umem->nmap = ib_dma_map_sg_attrs(context->device,umem->sg_head.sgl,umem->sg_nents,DMA_BIDIRECTIONAL, dma_attrs);  ...
}

這里就完成了對(duì)cq buf的dma映射,回到create_cq_user的邏輯:

page_size = mlx5_umem_find_best_cq_quantized_pgoff(cq->buf.umem, cqc, log_page_size, MLX5_ADAPTER_PAGE_SHIFT,page_offset, 64, &page_offset_quantized);if (!page_size) {err = -EINVAL;goto err_umem;}    err = mlx5_ib_db_map_user(context, udata, ucmd.db_addr, &cq->db);if (err)goto err_umem;ncont = ib_umem_num_dma_blocks(cq->buf.umem, page_size);

網(wǎng)卡支持多種大小的頁大小,mlx5_umem_find_best_cq_quantized_pgoff就是計(jì)算出最合適的頁大小,返回給pag_size,page_offset_quantized為buffer首地址相對(duì)于頁的偏移,假設(shè)這里返回的還是4K,ib_umem_num_dma_blocks計(jì)算cq buff一共占用了多少個(gè)物理頁。
然后執(zhí)行mlx5_ib_db_map_user完成對(duì)dbr的dma映射。

int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context,struct ib_udata *udata, unsigned long virt,struct mlx5_db *db)
{   struct mlx5_ib_user_db_page *page;int err = 0;mutex_lock(&context->db_page_mutex);list_for_each_entry(page, &context->db_page_list, list)if ((current->mm == page->mm) &&(page->user_virt == (virt & PAGE_MASK)))goto found;page = kmalloc(sizeof(*page), GFP_KERNEL);if (!page) {err = -ENOMEM;goto out;}page->user_virt = (virt & PAGE_MASK);page->refcnt    = 0;page->umem =ib_umem_get_peer(udata, virt & PAGE_MASK,PAGE_SIZE, 0, 0);if (IS_ERR(page->umem)) {err = PTR_ERR(page->umem);kfree(page);goto out;}mmgrab(current->mm);page->mm = current->mm;list_add(&page->list, &context->db_page_list);found:db->dma = sg_dma_address(page->umem->sg_head.sgl) + (virt & ~PAGE_MASK);db->u.user_page = page;++page->refcnt;out:mutex_unlock(&context->db_page_mutex);return err;
}

用戶態(tài)分配dbr的時(shí)候會(huì)一次性分配一個(gè)page以容納多個(gè)dbr,因此這里核心邏輯就是判斷當(dāng)前dbr所在的頁是否已經(jīng)執(zhí)行過dma映射,執(zhí)行過dma映射的頁會(huì)保存在db_page_list鏈表中,所以這里先遍歷鏈表里所有的mlx5_ib_user_db_page,如果發(fā)現(xiàn)和當(dāng)前要映射的page是同一個(gè)進(jìn)程,并且虛擬地址相等,就說明已經(jīng)映射過,那直接通過umem中的sg_table拿到首地址加上偏移就得到了dma地址;如果沒有找到,說明是第一次映射,將ib_umem_get_peer完成映射,將信息保存到page的umem,然后將當(dāng)前page加入到db_page_list。

cmd初始化

然后開始初始化mailbox機(jī)制中的輸入,即mlx5_ifc_create_cq_in_bits,如下所示,其中cq context entry即cqc,pas為cq buff對(duì)應(yīng)的物理頁集合。

在這里插入圖片描述
在這里插入圖片描述

void mlx5_ib_populate_pas(struct ib_umem *umem, size_t page_size, __be64 *pas,u64 access_flags)
{struct ib_block_iter biter;rdma_umem_for_each_dma_block (umem, &biter, page_size) {*pas = cpu_to_be64(rdma_block_iter_dma_address(&biter) |access_flags);pas++;}   
}

然后執(zhí)行mlx5_ib_populate_pas,這里會(huì)將sg_table記錄的物理內(nèi)存即cq buff按照page_size大小記錄到pas數(shù)組。
首先創(chuàng)建一個(gè)ib_block_iter,初始化設(shè)置__sg為scatterlist,__sg_nents 為nents,即sg_table里的成員個(gè)數(shù)。

#define rdma_umem_for_each_dma_block(umem, biter, pgsz)                        \for (__rdma_umem_block_iter_start(biter, umem, pgsz);                  \__rdma_block_iter_next(biter);)static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter,struct ib_umem *umem,unsigned long pgsz)
{__rdma_block_iter_start(biter, umem->sg_head.sgl, umem->nmap, pgsz);
}void __rdma_block_iter_start(struct ib_block_iter *biter,struct scatterlist *sglist, unsigned int nents,unsigned long pgsz)
{memset(biter, 0, sizeof(struct ib_block_iter));biter->__sg = sglist;biter->__sg_nents = nents;/* Driver provides best block size to use */biter->__pg_bit = __fls(pgsz);
}struct ib_block_iter {/* internal states */struct scatterlist *__sg;   /* sg holding the current aligned block */dma_addr_t __dma_addr;      /* unaligned DMA address of this block */unsigned int __sg_nents;    /* number of SG entries */unsigned int __sg_advance;  /* number of bytes to advance in sg in next step */unsigned int __pg_bit;      /* alignment of current block */
};

然后通過__rdma_block_iter_next遍歷sg_table,biter的dma_addr設(shè)置為當(dāng)前scatterlist的dma地址,__sg_advance表示在當(dāng)前entry中的偏移,第一次為0,所以biter第一次的__dma_addr就是第一個(gè)entry的dma地址,將dma地址記錄到pas第一項(xiàng),然后開始移動(dòng)到下一個(gè)物理頁,即將__sg_advance加上物理頁大小,如果__sg_advance大于當(dāng)前entry對(duì)應(yīng)的物理內(nèi)存長度,那么通過sg_next移動(dòng)到scatterlist的下一個(gè)entry,直到遍歷完成所有entry,就將所有物理頁記錄到了pas。

bool __rdma_block_iter_next(struct ib_block_iter *biter)
{unsigned int block_offset;if (!biter->__sg_nents || !biter->__sg)return false;biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance;block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1);biter->__sg_advance += BIT_ULL(biter->__pg_bit) - block_offset;if (biter->__sg_advance >= sg_dma_len(biter->__sg)) {biter->__sg_advance = 0;biter->__sg = sg_next(biter->__sg);biter->__sg_nents--;}return true;
}
static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,struct mlx5_ib_cq *cq, int entries, u32 **cqb,int *cqe_size, int *index, int *inlen)
{...cqc = MLX5_ADDR_OF(create_cq_in, *cqb, cq_context);MLX5_SET(cqc, cqc, log_page_size,order_base_2(page_size) - MLX5_ADAPTER_PAGE_SHIFT);MLX5_SET(cqc, cqc, page_offset, page_offset_quantized);...
}

然后開始設(shè)置mlx5_ifc_create_cq_in_bits的cqc,cqc的格式如下所示,log_page_size表示以log表示的物理頁大小;page_offset表示cq buff首地址相對(duì)物理頁的偏移,對(duì)于cq這個(gè)值必須為0。
在這里插入圖片描述

cmd執(zhí)行

然后回到create_cq的邏輯

int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,struct ib_udata *udata)
{...cqc = MLX5_ADDR_OF(create_cq_in, cqb, cq_context);MLX5_SET(cqc, cqc, cqe_sz,cqe_sz_to_mlx_sz(cqe_size,cq->private_flags &MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD));MLX5_SET(cqc, cqc, log_cq_size, ilog2(entries));MLX5_SET(cqc, cqc, uar_page, index);MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn);MLX5_SET64(cqc, cqc, dbr_addr, cq->db.dma);if (cq->create_flags & IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN)MLX5_SET(cqc, cqc, oi, 1);err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen, out, sizeof(out));...
}

設(shè)置好cqc之后執(zhí)行mlx5_core_create_cq,會(huì)執(zhí)行到mlx5_create_cq

int mlx5_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,u32 *in, int inlen, u32 *out, int outlen)
{int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context),c_eqn_or_apu_element);u32 din[MLX5_ST_SZ_DW(destroy_cq_in)] = {};struct mlx5_eq_comp *eq;int err;eq = mlx5_eqn2comp_eq(dev, eqn);if (IS_ERR(eq))return PTR_ERR(eq);memset(out, 0, outlen);MLX5_SET(create_cq_in, in, opcode, MLX5_CMD_OP_CREATE_CQ);err = mlx5_cmd_do(dev, in, inlen, out, outlen);if (err)return err;cq->cqn = MLX5_GET(create_cq_out, out, cqn);cq->cons_index = 0;cq->arm_sn     = 0;cq->eq         = eq; cq->uid = MLX5_GET(create_cq_in, in, uid);refcount_set(&cq->refcount, 1); init_completion(&cq->free);...
}

就是執(zhí)行mlx5_cmd_do,通過第二章介紹的mailbox機(jī)制將cmd下發(fā)給硬件執(zhí)行,執(zhí)行完成后將結(jié)果通過out返回,得到cqn記錄到cq,到這里cq的創(chuàng)建就完成了。

http://www.risenshineclean.com/news/34739.html

相關(guān)文章:

  • 江門恒陽網(wǎng)站建設(shè)百度推廣首次開戶需要多少錢
  • 網(wǎng)站建設(shè)團(tuán)隊(duì)介紹推廣公司屬于什么公司
  • 余姚網(wǎng)站建設(shè)全渠道營銷管理平臺(tái)
  • dede中英文網(wǎng)站 視頻站長統(tǒng)計(jì)app軟件下載官網(wǎng)安卓
  • 網(wǎng)站建設(shè)免費(fèi)軟件有哪些推特最新消息今天
  • 局域網(wǎng)網(wǎng)站怎么做谷歌搜索排名
  • 用vue做的網(wǎng)站怎么實(shí)現(xiàn)響應(yīng)式株洲專業(yè)seo優(yōu)化
  • 深圳微網(wǎng)站開發(fā)最新全國疫情消息
  • 南昌網(wǎng)站關(guān)鍵詞優(yōu)化廣州百度關(guān)鍵詞推廣
  • 天津網(wǎng)絡(luò)優(yōu)化網(wǎng)站建設(shè)互聯(lián)網(wǎng)產(chǎn)品運(yùn)營
  • 教人做飲料的網(wǎng)站寧波網(wǎng)絡(luò)營銷策劃公司
  • 做圖片類型網(wǎng)站需要什么服務(wù)器網(wǎng)站設(shè)計(jì)師
  • dw做網(wǎng)站怎么用到j(luò)ava免費(fèi)發(fā)帖推廣網(wǎng)站
  • 網(wǎng)站項(xiàng)目評(píng)價(jià)河源疫情最新通報(bào)
  • 網(wǎng)站開發(fā)是前端還是后臺(tái)有友情鏈接的網(wǎng)站
  • 珠海建網(wǎng)站上海aso蘋果關(guān)鍵詞優(yōu)化
  • 網(wǎng)站做的好的醫(yī)院google瀏覽器下載
  • 貿(mào)易公司做網(wǎng)站有優(yōu)勢(shì)嗎競價(jià)是什么意思
  • 網(wǎng)頁設(shè)計(jì) 傳統(tǒng)網(wǎng)站全網(wǎng)推廣代理
  • 河南企業(yè)網(wǎng)站制作wordpress免費(fèi)建站
  • 網(wǎng)絡(luò)上建個(gè)網(wǎng)站買東西多少錢怎么找專業(yè)的營銷團(tuán)隊(duì)
  • 網(wǎng)上購物系統(tǒng)源碼seo診斷a5
  • 視頻公司的網(wǎng)站設(shè)計(jì)模板網(wǎng)站建站公司
  • 如何對(duì)網(wǎng)站建設(shè)和維護(hù)企業(yè)策劃
  • 用織夢(mèng)網(wǎng)站后臺(tái)發(fā)布文章為什么還需要審核谷歌下載安裝
  • 公司網(wǎng)站建設(shè)南寧百度競價(jià)收費(fèi)標(biāo)準(zhǔn)
  • 房地產(chǎn)營銷網(wǎng)站建設(shè)新浪微指數(shù)
  • 鄭州中揚(yáng)科技網(wǎng)站建設(shè)公司怎么樣網(wǎng)絡(luò)營銷方案ppt
  • 手機(jī)端網(wǎng)站建站品牌營銷案例分析
  • wordpress耗資源關(guān)閉深圳最好的外貿(mào)seo培訓(xùn)