自己怎么做网站昆山外贸型网站制作

当前位置: 首页 > news >正文

自己怎么做网站,昆山外贸型网站制作,ui设计软件培训学校,业务平台低价为了实现用户对网卡硬件的配置#xff0c;查询#xff0c;或者执行比如create_cq等命令#xff0c;mellanox网卡提供了command queue mailbox的机制#xff0c;本节将以create_cq为例看下这个过程。 command queue#xff08;后续简称cmdq#xff09;是一个4K对齐的长度…为了实现用户对网卡硬件的配置查询或者执行比如create_cq等命令mellanox网卡提供了command queue mailbox的机制本节将以create_cq为例看下这个过程。 command queue后续简称cmdq是一个4K对齐的长度为4K的连续物理内存如图1所示cmdq中有多个entryentry的数量和entry的size都是从initialization segment中读到的。
图 1 entry格式如图2软件的每一个cmd会对应cmdq中的一个entrycmd的input会存到entry中如果input小于16B那么就直接存储到command_input_inline_data如果大于16B那么剩余的会存到图一中的mailbox里mailbox是一个链表结构每个mailbox可以容纳512B的数据第一个mailbox的指针存储在entry的input mailbox pointer。 图 2 将输入存到entry和对应的mailbox之后软件会写cmdq的doorbelldoorbell位于initialization segment可以理解为一个vector软件会将这次entry对应的index写入到这个vector对应的bit通知硬件执行。硬件执行cmdq中entry的顺序是不确定的初始化entry时entry的ownership会被设置为HW当硬件执行完成后ownership会被重新设置为SW因此软件可以轮询ownership确定硬件是否完成。 然后看下驱动中是怎么做的。 初始化 mlx5_cmd_init主要工作就是创建command queue的buf然后将这块内存的总线地址告诉网卡硬件。 通过dma_pool_create创建dma内存池每个内存块大小为sizeof(mlx5_cmd_prot_block)一个内存块表示了一个mailboxmlx5_cmd_prot_block类型用于表示mailbox。 int mlx5_cmd_init(struct mlx5_core_dev *dev) {int size sizeof(struct mlx5_cmd_prot_block);int align roundup_pow_of_two(size);struct mlx5_cmd *cmd dev-cmd;…cmd-pool dma_pool_create(mlx5_cmd, mlx5_core_dma_dev(dev), size, align, 0);err alloc_cmd_page(dev, cmd);… }然后通过dma_zalloc_coherent分配大小为一页的一致性dma映射这块内存就是command queue的buf虚拟地址保存在cmd_alloc_bufdma地址保存到dma同时还需要保证这个地址是页对齐的。如果不对齐还需要重新分配一次大小为两页的dma映射从而保证页对齐。 static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd cmd) {cmd-cmd_alloc_buf dma_zalloc_coherent(mlx5_core_dma_dev(dev), MLX5_ADAPTER_PAGE_SIZE, cmd-alloc_dma, GFP_KERNEL);…/ make sure it is aligned to 4K */if (!((uintptr_t)cmd-cmd_alloc_buf (MLX5_ADAPTER_PAGE_SIZE - 1))) {cmd-cmd_buf cmd-cmd_alloc_buf;cmd-dma cmd-alloc_dma;cmd-alloc_size MLX5_ADAPTER_PAGE_SIZE;return 0;} dma_free_coherent(mlx5_core_dma_dev(dev), MLX5_ADAPTER_PAGE_SIZE, cmd-cmd_alloc_buf,cmd-alloc_dma);cmd-cmd_alloc_buf dma_zalloc_coherent(mlx5_core_dma_dev(dev),2 * MLX5_ADAPTER_PAGE_SIZE - 1, cmd-alloc_dma, GFP_KERNEL);…cmd-cmd_buf PTR_ALIGN(cmd-cmd_alloc_buf, MLX5_ADAPTER_PAGE_SIZE);cmd-dma ALIGN(cmd-alloc_dma, MLX5_ADAPTER_PAGE_SIZE);cmd-alloc_size 2 * MLX5_ADAPTER_PAGE_SIZE - 1;return 0; }然后从Initialization Segment后续简称iseg读出cmdq_addr_l_sz然后解析出低位的log_sz和log_stridelog_sz以log形式表示cmdq一共有多少个entrylog_stride以log形式表示cmdq的一个entry大小。然后将cmdq的dma地址写到iseg这样硬件就知道cmdq的地址了。最后设置mode为CMD_MODE_POLLING创建一个单线程的workqueue。到这里初始化就完成了。 int mlx5_cmd_init(struct mlx5_core_dev dev) {…cmd_l ioread32be(dev-iseg-cmdq_addr_l_sz) 0xff;cmd-log_sz cmd_l 4 0xf;cmd-log_stride cmd_l 0xf;cmd-max_reg_cmds (1 cmd-log_sz) - 1;cmd-bitmask (1UL cmd-max_reg_cmds) - 1;cmd_h (u32)((u64)(cmd-dma) 32);cmd_l (u32)(cmd-dma);iowrite32be(cmd_h, dev-iseg-cmdq_addr_h);iowrite32be(cmd_l, dev-iseg-cmdq_addr_l_sz);/ Make sure firmware sees the complete address before we proceed */wmb();…cmd-mode CMD_MODE_POLLING;cmd-allowed_opcode CMD_ALLOWED_OPCODE_ALL;cmd-wq create_singlethread_workqueue(cmd-wq_name)… }cmd的下发与执行 输入输出 接下来以create_cq为例看下如何下发一个cmdcreate_cq的输入由结构体mlx5_ifc_create_cq_in_bits解释这个结构体不表示实际内存比如opcode[0x10]通过u8类型表示仅仅是为了编程的方便实际只占了16bit。输出由结构体mlx5_ifc_create_cq_out_bits表示。 struct mlx5_ifc_create_cq_in_bits {u8 opcode[0x10];u8 uid[0x10];u8 reserved_at_20[0x10];u8 op_mod[0x10];u8 reserved_at_40[0x40];struct mlx5_ifc_cqc_bits cq_context;u8 reserved_at_280[0x60];u8 cq_umem_valid[0x1];u8 reserved_at_2e1[0x59f];u8 pas[][0x40]; }; 首先看下如何存储输入输出mlx5_cmd_msg 表示一个msg用于管理cmdq的entry和对应的mailbox其中first用于存储开始的16B如果输入大于16B将会存储到next对应的mailbox链表里。mlx5_cmd_mailbox表示一个mailbox其中buf为mailbox对应的虚拟地址dma为这块buf的dma地址。next为软件侧的链是一个虚拟地址软件通过next遍历mailbox链表硬件遍历mailbox的链表是通过buf里的next是一个dma地址。 struct mlx5_cmd_msg {struct list_head list;struct cmd_msg_cache *parent;u32 len;struct mlx5_cmd_first first;struct mlx5_cmd_mailbox *next; };struct mlx5_cmd_first {__be32 data[4]; };struct mlx5_cmd_mailbox {void *buf;dma_addr_t dma;struct mlx5_cmd_mailbox *next; };然后开始看执行cmd的过程入口函数为mlx5_cmd_do实际直接执行cmd_exec其中callback和context为NULLforce_poling为false。 static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,int out_size, mlx5_cmd_cbk_t callback, void *context,bool force_polling) {struct mlx5_cmd_msg *inb;struct mlx5_cmd_msg *outb;opcode MLX5_GET(mbox_in, in, opcode);pages_queue is_manage_pages(in);gfp callback ? GFP_ATOMIC : GFP_KERNEL;inb alloc_msg(dev, in_size, gfp);… }分配msg cmd_exec首先通过alloc_msg分配input msg初始化的时候已经分配了若干个mlx5_cmd_msg作为cache。如果in的长度小于16就是可以放到一个cmdq entry那么直接分配不复用cache否则会先看cache中有没有满足需要大小的msg如果有则使用cache中的msg假设没有命中cache会通过mlx5_alloc_cmd_msg进行分配。 static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,gfp_t gfp) {struct mlx5_cmd_msg *msg ERR_PTR(-ENOMEM);struct mlx5_cmd *cmd dev-cmd;if (in_size 16)goto cache_miss;… cache_miss:msg mlx5_alloc_cmd_msg(dev, gfp, in_size, 0);return msg; } 先分配msg然后通过mlx5_calc_cmd_blocks计算需要几个mailbox entry计算方法为cmdq entry可以存16B然后剩下的看需要几个mailbox存储一个mailbox可以存储512B。然后通过alloc_cmd_box分配mailbox这里就是通过dma_pool_zalloc从cmd-pool中获取一个dma内存块对于每一个新分配的mailbox通过mailbox的next字段链接到msg的next链表软件通过这里的next就可以遍历链表。然后开始初始化mailbox的buf设置block_num设置token为0然后设置buf的next为下一个mailbox的dma地址硬件通过buf里的next就可以遍历链表了。 static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,gfp_t flags, int size,u8 token) {struct mlx5_cmd_mailbox *tmp, *head NULL;struct mlx5_cmd_prot_block *block;struct mlx5_cmd_msg *msg;int err; int n;int i;msg kzalloc(sizeof(*msg), flags);if (!msg)return ERR_PTR(-ENOMEM);msg-len size;n mlx5_calc_cmd_blocks(msg);for (i 0; i n; i) {tmp alloc_cmd_box(dev, flags);if (IS_ERR(tmp)) {mlx5_core_warn(dev, failed allocating block\n);err PTR_ERR(tmp);goto err_alloc;} block tmp-buf;tmp-next head;block-next cpu_to_be64(tmp-next ? tmp-next-dma : 0);block-block_num cpu_to_be32(n - i - 1);block-token token;head tmp; } msg-next head;return msg; err_alloc:while (head) {tmp head-next;free_cmd_box(dev, head);head tmp; } kfree(msg);return ERR_PTR(err); }static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,int out_size, mlx5_cmd_cbk_t callback, void *context,bool force_polling) {…token alloc_token(dev-cmd);err mlx5_copy_to_msg(inb, in, in_size, token);outb mlx5_alloc_cmd_msg(dev, gfp, out_size, token); }然后开始分配tokentoken就是一个自增的uint8标识一次cmd一次cmd的cmdq entry和对应的所有mailbox的token都需要是一致的然后通过mlx5_copy_to_msg将数据从in中拷贝到msg inb中就是将连续的输入分散拷贝到cmd entry和mailbox中。然后分配output msg和分配input msg一样。 执行 mlx5_cmd_invoke通过cmd_alloc_ent分配一个mlx5_cmd_work_ent ent用于记录上下文信息比如输入输出等。初始化ent中的work_struct work对应的执行函数为cmd_work_handler然后通过queue_work将ent-work提交到cmd-wq中执行这里的wq就是初始化中创建的。wait_func通过wait_for_completion等待cmd-wq完成ent-work的执行。 static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,struct mlx5_cmd_msg *out, void *uout, int uout_size,mlx5_cmd_cbk_t callback,void *context, int page_queue, u8 *status,u8 token, bool force_polling) {struct mlx5_cmd *cmd dev-cmd;struct mlx5_cmd_work_ent ent;ent cmd_alloc_ent(cmd, in, out, uout, uout_size,callback, context, page_queue);ent-token token;ent-polling force_polling;init_completion(ent-handling);if (!callback)init_completion(ent-done);INIT_DELAYED_WORK(ent-cb_timeout_work, cb_timeout_handler);INIT_WORK(ent-work, cmd_work_handler);if (page_queue) {cmd_work_handler(ent-work);} else if (!queue_work(cmd-wq, ent-work)) {mlx5_core_warn(dev, failed to queue work\n);err -ENOMEM;goto out_free;}if (callback)goto out; / mlx5_cmd_comp_handler() will put(ent) */err wait_func(dev, ent);… }然后看下work_queue是如何执行的即cmd_work_handler。 首先执行cmd-bitmask表示了当前cdmq的buff有哪些entry可用cmd_alloc_index就是通过find_first_bit找到bitmask中第一个置为1的位ret如果找到了那么将这位置清0表示占用了将ret记录到ent-idx。 static int cmd_alloc_index(struct mlx5_cmd *cmd) { unsigned long flags;int ret; spin_lock_irqsave(cmd-alloc_lock, flags);ret find_first_bit(cmd-bitmask, cmd-max_reg_cmds);if (ret cmd-max_reg_cmds)clear_bit(ret, cmd-bitmask);spin_unlock_irqrestore(cmd-alloc_lock, flags);return ret cmd-max_reg_cmds ? ret : -ENOMEM; }
static void cmd_work_handler(struct work_struct *work) {struct mlx5_cmd_work_ent *ent container_of(work, struct mlx5_cmd_work_ent, work);struct mlx5_cmd *cmd ent-cmd;bool poll_cmd ent-polling;struct mlx5_cmd_layout *lay;int alloc_ret;int cmd_mode;dev container_of(cmd, struct mlx5_core_dev, cmd);cb_timeout msecs_to_jiffies(mlx5_tout_ms(dev, CMD));complete(ent-handling);sem ent-page_queue ? cmd-pages_sem : cmd-sem;down(sem);if (!ent-page_queue) {alloc_ret cmd_alloc_index(cmd);if (alloc_ret 0) { …up(sem);return;} ent-idx alloc_ret;} else {…} cmd-ent_arr[ent-idx] ent; lay get_inst(cmd, ent-idx);… }然后执行get_inst获取cmdq的第idx个entry即lay前边有说log_stride是以log表示的cmdq entry大小因此就是(idx cmd-log_stride)。 static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx) { return cmd-cmd_buf (idx cmd-log_stride); }然后开始设置lay首先拷贝input msg的前16B到lay-in如果input msg有next表示输入长度超过了16B因此将第一个mailbox的dma地址设置到lay-in_ptr接着设置inlen同理设置out和outlen然后设置tokensignature设置完成后将ownership bit修改为HW。 static void cmd_work_handler(struct work_struct *work) {…ent-lay lay;memset(lay, 0, sizeof(*lay));memcpy(lay-in, ent-in-first.data, sizeof(lay-in));ent-op be32_to_cpu(lay-in[0]) 16;if (ent-in-next)lay-in_ptr cpu_to_be64(ent-in-next-dma);lay-inlen cpu_to_be32(ent-in-len);if (ent-out-next)lay-out_ptr cpu_to_be64(ent-out-next-dma);lay-outlen cpu_to_be32(ent-out-len);lay-type MLX5_PCI_CMD_XPORT;lay-token ent-token;lay-status_own CMD_OWNER_HW;set_signature(ent, !cmd-checksum_disabled);… }对lay的设置完成之后然后开始写cmd queue的doorbelldoorbell是一个vector软件通过写对应的bit表示新的cmd是cmd_buf的哪个位置。 static void cmd_work_handler(struct work_struct work) {…wmb();iowrite32be(1 ent-idx, dev-iseg-cmd_dbell);/ if not in polling dont use ent after this point /if (cmd_mode CMD_MODE_POLLING || poll_cmd) {poll_timeout(ent);/ make sure we read the descriptor after ownership is SW */rmb();mlx5_cmd_comp_handler(dev, 1ULL ent-idx, ent-ret -ETIMEDOUT ?MLX5_CMD_COMP_TYPE_FORCED : MLX5_CMD_COMP_TYPE_POLLING);} }然后通过poll_timeout等待网卡执行实现上就是通过轮询cmq entry的ownership字段当ownership变成SW之后表示硬件已经完成执行。 static void poll_timeout(struct mlx5_cmd_work_ent *ent) {struct mlx5_core_dev *dev container_of(ent-cmd, struct mlx5_core_dev, cmd);u64 cmd_to_ms mlx5_tout_ms(dev, CMD);unsigned long poll_end;u8 own;poll_end jiffies msecs_to_jiffies(cmd_to_ms 1000);do {own READ_ONCE(ent-lay-status_own);if (!(own CMD_OWNER_HW)) {ent-ret 0;return;}cond_resched();} while (time_before(jiffies, poll_end));ent-ret -ETIMEDOUT; }然后执行mlx5_cmd_comp_handler将硬件输出的前16B拷贝到output msg后边的输出可以通过output找到output mailbox获取通过complete通知执行的完成。 static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, enum mlx5_comp_t comp_type) {struct mlx5_cmd *cmd dev-cmd;struct mlx5_cmd_work_ent *ent;…memcpy(ent-out-first.data, ent-lay-out, sizeof(ent-lay-out));complete(ent-done);… }最后回到cmd_exec会将output msg拷贝回out对于create_cq来说就是mlx5_ifc_create_cq_in_bits。