The master function receives am interrupt each time on eof the slaves writes to the communication channel, and then handles the channel from a deffered task. The slaves can now receive command completions an async events by interrupts. The open an EQ and the master generates eqes and "pushes" them to the slave's EQ using Firmware "GEN_EQE" command.
Signed-off-by: Yevgeny Petrilin <[email protected]> --- drivers/net/mlx4/cmd.c | 258 +++++++++++++++++++++++++++++++------- drivers/net/mlx4/eq.c | 296 +++++++++++++++++++------------------------ drivers/net/mlx4/main.c | 50 +++----- drivers/net/mlx4/mlx4.h | 97 +++++++++++++-- include/linux/mlx4/cmd.h | 7 +- include/linux/mlx4/device.h | 1 + 6 files changed, 459 insertions(+), 250 deletions(-) diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c index 7efa85f..083ae0f 100644 --- a/drivers/net/mlx4/cmd.c +++ b/drivers/net/mlx4/cmd.c @@ -148,19 +148,11 @@ static int comm_pending(struct mlx4_dev *dev) return (swab32(status) >> 30) != priv->cmd.comm_toggle; } -int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, unsigned long timeout) +static void mlx4_comm_cmd_post(struct mlx4_dev *dev, u8 cmd, u16 param) { struct mlx4_priv *priv = mlx4_priv(dev); - unsigned long end; u32 val; - /* First, verify that the master reports correct status */ - if (comm_pending(dev)) { - mlx4_warn(dev, "Communication channel is not idle\n"); - return -EAGAIN; - } - - /* Write command */ if (cmd == MLX4_COMM_CMD_RESET) priv->cmd.comm_toggle = 0; else if (++priv->cmd.comm_toggle > 2) @@ -168,6 +160,23 @@ int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, unsigned long timeout val = param | (cmd << 16) | (priv->cmd.comm_toggle << 30); __raw_writel((__force u32) cpu_to_be32(val), &priv->mfunc.comm->slave_write); wmb(); +} + +int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param, unsigned long timeout) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + unsigned long end; + int err = 0; + + /* First, verify that the master reports correct status */ + if (comm_pending(dev)) { + mlx4_warn(dev, "Communication channel is not idle\n"); + return -EAGAIN; + } + + /* Write command */ + down(&priv->cmd.poll_sem); + mlx4_comm_cmd_post(dev, cmd, param); end = msecs_to_jiffies(timeout) + jiffies; while (comm_pending(dev) && time_before(jiffies, end)) @@ -175,11 +184,57 @@ int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, unsigned long timeout if (comm_pending(dev)) { mlx4_warn(dev, "Communication channel timed out\n"); - return -ETIMEDOUT; + err = -ETIMEDOUT; } + + up(&priv->cmd.poll_sem); return 0; } +static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op, + u16 param, unsigned long timeout) +{ + struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; + struct mlx4_cmd_context *context; + int err = 0; + + down(&cmd->event_sem); + + spin_lock(&cmd->context_lock); + BUG_ON(cmd->free_head < 0); + context = &cmd->context[cmd->free_head]; + context->token += cmd->token_mask + 1; + cmd->free_head = context->next; + spin_unlock(&cmd->context_lock); + + init_completion(&context->done); + + mlx4_comm_cmd_post(dev, op, param); + + if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { + err = -EBUSY; + goto out; + } + + err = context->result; + +out: + spin_lock(&cmd->context_lock); + context->next = cmd->free_head; + cmd->free_head = context - cmd->context; + spin_unlock(&cmd->context_lock); + + up(&cmd->event_sem); + return err; +} + +int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, unsigned long timeout) +{ + if (mlx4_priv(dev)->cmd.use_events) + return mlx4_comm_cmd_wait(dev, cmd, param, timeout); + return mlx4_comm_cmd_poll(dev, cmd, param, timeout); +} + static int cmd_pending(struct mlx4_dev *dev) { u32 status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); @@ -247,15 +302,15 @@ out: return ret; } -static int mlx4_slave_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, - int out_is_imm, u32 in_modifier, u8 op_modifier, - u16 op, unsigned long timeout) +static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, + int out_is_imm, u32 in_modifier, u8 op_modifier, + u16 op, unsigned long timeout) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_vhcr *vhcr = priv->mfunc.vhcr; int ret; - down(&priv->cmd.poll_sem); + down(&priv->cmd.slave_sem); vhcr->in_param = in_param; vhcr->out_param = out_param ? *out_param : 0; vhcr->in_modifier = in_modifier; @@ -270,7 +325,7 @@ static int mlx4_slave_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_para *out_param = vhcr->out_param; ret = vhcr->errno; } - up(&priv->cmd.poll_sem); + up(&priv->cmd.slave_sem); return ret; } @@ -378,19 +433,61 @@ int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, int out_is_imm, u32 in_modifier, u8 op_modifier, u16 op, unsigned long timeout) { + if (mlx4_is_slave(dev)) + return mlx4_slave_cmd(dev, in_param, out_param, out_is_imm, + in_modifier, op_modifier, op, timeout); + if (mlx4_priv(dev)->cmd.use_events) return mlx4_cmd_wait(dev, in_param, out_param, out_is_imm, in_modifier, op_modifier, op, timeout); - - if (mlx4_is_slave(dev)) - return mlx4_slave_cmd_poll(dev, in_param, out_param, out_is_imm, - in_modifier, op_modifier, op, timeout); else return mlx4_cmd_poll(dev, in_param, out_param, out_is_imm, in_modifier, op_modifier, op, timeout); } EXPORT_SYMBOL_GPL(__mlx4_cmd); + +static int mlx4_ARM_COMM_CHANNEL(struct mlx4_dev *dev) +{ + return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_ARM_COMM_CHANNEL, MLX4_CMD_TIME_CLASS_B); +} + +int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_slave_event_eq_info *event_eq = + &priv->mfunc.master.slave_state[slave].event_eq; + struct mlx4_cmd_mailbox *mailbox; + u32 in_modifier = 0; + int err; + + if (!event_eq->use_int) + return 0; + + /* Create the event only if the slave is registered */ + if ((event_eq->event_type & eqe->type) == 0) + return 0; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + if (eqe->type == MLX4_EVENT_TYPE_CMD) { + ++event_eq->token; + eqe->event.cmd.token = cpu_to_be16(event_eq->token); + } + + memcpy(mailbox->buf, (u8 *) eqe, 28); + + in_modifier = (slave & 0xff) | ((event_eq->eqn & 0xff) << 16); + + err = mlx4_cmd(dev, mailbox->dma, in_modifier, 0, + MLX4_CMD_GEN_EQE, MLX4_CMD_TIME_CLASS_B); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + static int mlx4_ACCESS_MEM(struct mlx4_dev *dev, u64 master_addr, int slave, u64 slave_addr, int size, int is_read) @@ -597,12 +694,21 @@ static struct mlx4_cmd_info { }, { + .opcode = MLX4_CMD_COMM_INT, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .verify = NULL, + .wrapper = mlx4_COMM_INT_wrapper + }, + { .opcode = MLX4_CMD_INIT_PORT, .has_inbox = false, .has_outbox = false, .out_is_imm = false, .verify = NULL, - .wrapper = mlx4_INIT_PORT_wrapper}, + .wrapper = mlx4_INIT_PORT_wrapper + }, { .opcode = MLX4_CMD_CLOSE_PORT, .has_inbox = false, @@ -629,6 +735,14 @@ static struct mlx4_cmd_info { }, { + .opcode = MLX4_CMD_MAP_EQ, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .verify = NULL, + .wrapper = mlx4_MAP_EQ_wrapper + }, + { .opcode = MLX4_CMD_SW2HW_EQ, .has_inbox = true, .has_outbox = false, @@ -1108,6 +1222,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, u16 para if (cmd == MLX4_COMM_CMD_RESET) { mlx4_warn(dev, "Received reset from slave:%d\n", slave); + slave_state[slave].active = false; goto reset_slave; } @@ -1146,6 +1261,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, u16 para "number for slave %d\n", slave); goto reset_slave; } + slave_state[slave].active = true; break; case MLX4_COMM_CMD_VHCR_POST: if ((slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_EN) && @@ -1167,45 +1283,59 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, u16 para __raw_writel((__force u32) cpu_to_be32(reply), &priv->mfunc.comm[slave].slave_read); wmb(); + if (mlx4_GEN_EQE(dev, slave, &priv->mfunc.master.cmd_eqe)) + mlx4_warn(dev, "Failed to generate command completion eqe " + "for slave %d\n", slave); + return; reset_slave: /* FIXME: cleanup any slave resources */ slave_state[slave].last_cmd = MLX4_COMM_CMD_RESET; slave_state[slave].comm_toggle = 0; + memset(&slave_state[slave].event_eq, 0, + sizeof(struct mlx4_slave_event_eq_info)); __raw_writel((__force u32) 0, &priv->mfunc.comm[slave].slave_write); __raw_writel((__force u32) 0, &priv->mfunc.comm[slave].slave_read); wmb(); } /* master command processing */ -static void mlx4_master_poll_comm(struct work_struct *work) +void mlx4_master_comm_channel(struct work_struct *work) { - struct delayed_work *delay = container_of(work, struct delayed_work, work); - struct mlx4_mfunc *mfunc = container_of(delay, struct mlx4_mfunc, comm_work); + struct mlx4_mfunc_master_ctx *master = container_of(work, + struct mlx4_mfunc_master_ctx, + comm_work); + struct mlx4_mfunc *mfunc = container_of(master, struct mlx4_mfunc, master); struct mlx4_priv *priv = container_of(mfunc, struct mlx4_priv, mfunc); struct mlx4_dev *dev = &priv->dev; + u32 *bit_vec; u32 comm_cmd; - int polled = 0; - int i; - - /* Give each slave a chance for one command */ - for (i = 0; i < dev->num_slaves; i++) { - comm_cmd = swab32(readl(&priv->mfunc.comm[i].slave_write)); - if (comm_cmd >> 30 != priv->mfunc.master.slave_state[i].comm_toggle) { - mlx4_master_do_cmd(dev, i, comm_cmd >> 16, comm_cmd, comm_cmd >> 30); - polled = 1; + u32 vec; + int i, j, slave; + + bit_vec = master->comm_arm_bit_vector; + for (i = 0; i < COMM_CHANNEL_BIT_ARRAY_SIZE; i++) { + vec = be32_to_cpu(bit_vec[i]); + for (j = 0; j < 32; j++) { + if (!(vec & (1 << j))) + continue; + slave = (i * 32) + j; + comm_cmd = swab32(readl(&mfunc->comm[slave].slave_write)); + if (comm_cmd >> 30 != master->slave_state[slave].comm_toggle) + mlx4_master_do_cmd(dev, slave, comm_cmd >> 16, comm_cmd, comm_cmd >> 30); } } - queue_delayed_work(priv->mfunc.comm_wq, &priv->mfunc.comm_work, - polled ? 0 : HZ / 10); + + if (mlx4_ARM_COMM_CHANNEL(dev)) + mlx4_warn(dev, "Failed to arm comm channel events"); } int mlx4_multi_func_init(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_slave_state *s_state; - int i, port; + int i, err, port; priv->mfunc.vhcr = dma_alloc_coherent(&(dev->pdev->dev), PAGE_SIZE, &priv->mfunc.vhcr_dma, @@ -1253,20 +1383,30 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) spin_lock_init(&s_state->lock); } - INIT_DELAYED_WORK(&priv->mfunc.comm_work, mlx4_master_poll_comm); - priv->mfunc.comm_wq = create_singlethread_workqueue("mlx4_comm"); - if (!priv->mfunc.comm_wq) + memset(&priv->mfunc.master.cmd_eqe, 0, sizeof(struct mlx4_eqe)); + priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD; + INIT_WORK(&priv->mfunc.master.comm_work, mlx4_master_comm_channel); + INIT_WORK(&priv->mfunc.master.slave_event_work, mlx4_gen_slave_eqe); + priv->mfunc.master.comm_wq = create_singlethread_workqueue("mlx4_comm"); + if (!priv->mfunc.master.comm_wq) goto err_slaves; + err = mlx4_ARM_COMM_CHANNEL(dev); + if (err) { + mlx4_err(dev, " Failed to arm comm channel eq: %x\n", err); + goto err_thread; + } + } else { + sema_init(&priv->cmd.slave_sem, 1); priv->cmd.comm_toggle = 0; - INIT_DELAYED_WORK(&priv->mfunc.comm_work, mlx4_slave_async_eq_poll); - priv->mfunc.comm_wq = create_singlethread_workqueue("mlx4_event"); - if (!priv->mfunc.comm_wq) goto err_comm; } return 0; +err_thread: + flush_workqueue(priv->mfunc.master.comm_wq); + destroy_workqueue(priv->mfunc.master.comm_wq); err_slaves: while (--i) { for (port = 1; port <= MLX4_MAX_PORTS; port++) @@ -1323,8 +1463,9 @@ void mlx4_multi_func_cleanup(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i, port; - if (priv->mfunc.vhcr) { - destroy_workqueue(priv->mfunc.comm_wq); + if (mlx4_is_master(dev)) { + flush_workqueue(priv->mfunc.master.comm_wq); + destroy_workqueue(priv->mfunc.master.comm_wq); for (i = 0; i < dev->num_slaves; i++) { for (port = 1; port <= MLX4_MAX_PORTS; port++) kfree(priv->mfunc.master.slave_state[i].vlan_filter[port]); @@ -1356,6 +1497,7 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); int i; + int err = 0; priv->cmd.context = kmalloc(priv->cmd.max_cmds * sizeof (struct mlx4_cmd_context), @@ -1382,9 +1524,17 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) priv->cmd.use_events = 1; + if (mlx4_is_slave(dev)) { + err = mlx4_cmd(dev, 0, 1, 0, MLX4_CMD_COMM_INT, MLX4_CMD_TIME_CLASS_A); + if (err) { + mlx4_err(dev, "Failed to move to events for the slave\n"); + priv->cmd.use_events = 0; + } + } + down(&priv->cmd.poll_sem); - return 0; + return err; } /* @@ -1403,6 +1553,9 @@ void mlx4_cmd_use_polling(struct mlx4_dev *dev) kfree(priv->cmd.context); up(&priv->cmd.poll_sem); + + if (mlx4_is_slave(dev)) + mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_COMM_INT, MLX4_CMD_TIME_CLASS_A); } struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) @@ -1433,3 +1586,20 @@ void mlx4_free_cmd_mailbox(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbo kfree(mailbox); } EXPORT_SYMBOL_GPL(mlx4_free_cmd_mailbox); + +int mlx4_COMM_INT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_slave_event_eq_info *event_eq = + &priv->mfunc.master.slave_state[slave].event_eq; + + if (vhcr->in_modifier) + event_eq->use_int = true; + else + event_eq->use_int = false; + + return 0; +} + diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index 9126c8e..e5adca2 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -99,46 +99,8 @@ struct mlx4_eq_context { (1ull << MLX4_EVENT_TYPE_SRQ_CATAS_ERROR) | \ (1ull << MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ (1ull << MLX4_EVENT_TYPE_SRQ_LIMIT) | \ - (1ull << MLX4_EVENT_TYPE_CMD)) - -struct mlx4_eqe { - u8 reserved1; - u8 type; - u8 reserved2; - u8 subtype; - union { - u32 raw[6]; - struct { - __be32 cqn; - } __attribute__((packed)) comp; - struct { - u16 reserved1; - __be16 token; - u32 reserved2; - u8 reserved3[3]; - u8 status; - __be64 out_param; - } __attribute__((packed)) cmd; - struct { - __be32 qpn; - } __attribute__((packed)) qp; - struct { - __be32 srqn; - } __attribute__((packed)) srq; - struct { - __be32 cqn; - u32 reserved1; - u8 reserved2[3]; - u8 syndrome; - } __attribute__((packed)) cq_err; - struct { - u32 reserved1[2]; - __be32 port; - } __attribute__((packed)) port_change; - } event; - u8 reserved3[3]; - u8 owner; -} __attribute__((packed)); + (1ull << MLX4_EVENT_TYPE_CMD) | \ + (1ull << MLX4_EVENT_TYPE_COMM_CHANNEL)) static void eq_set_ci(struct mlx4_eq *eq, int req_not) { @@ -161,35 +123,87 @@ static struct mlx4_eqe *next_eqe_sw(struct mlx4_eq *eq) return !!(eqe->owner & 0x80) ^ !!(eq->cons_index & eq->nent) ? NULL : eqe; } -void mlx4_slave_event(struct mlx4_dev *dev, int slave, u8 type, u8 port, u32 param) +static struct mlx4_eqe *next_slave_event_eqe(struct mlx4_slave_event_eq *slave_eq) +{ + struct mlx4_eqe *eqe = + &slave_eq->event_eqe[slave_eq->cons & (SLAVE_EVENT_EQ_SIZE - 1)]; + return (!!(eqe->owner & 0x80) ^ !!(slave_eq->cons & SLAVE_EVENT_EQ_SIZE)) ? + eqe : NULL; +} +void mlx4_gen_slave_eqe(struct work_struct *work) +{ + struct mlx4_mfunc_master_ctx *master = container_of(work, + struct mlx4_mfunc_master_ctx, + slave_event_work); + struct mlx4_mfunc *mfunc = container_of(master, struct mlx4_mfunc, master); + struct mlx4_priv *priv = container_of(mfunc, struct mlx4_priv, mfunc); + struct mlx4_dev *dev = &priv->dev; + struct mlx4_slave_event_eq *slave_eq = &mfunc->master.slave_eq; + struct mlx4_eqe *eqe; + u8 slave; + int i; + + for (eqe = next_slave_event_eqe(slave_eq); eqe; + eqe = next_slave_event_eqe(slave_eq)) { + slave = eqe->slave_id; + + /* All active slaves need to receive the event */ + if (slave == ALL_SLAVES) { + for (i = 0; i < dev->num_slaves; i++) { + if (master->slave_state[i].active) + if (mlx4_GEN_EQE(dev, i, eqe)) + mlx4_warn(dev, "Failed to generate event " + "for slave %d\n", i); + } + } else { + if (mlx4_GEN_EQE(dev, slave, eqe)) + mlx4_warn(dev, "Failed to generate event " + "for slave %d\n", slave); + } + ++slave_eq->cons; + } +} + + +static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_slave_state *ctx = &priv->mfunc.master.slave_state[slave]; - unsigned long flags; + struct mlx4_slave_event_eq *slave_eq = &priv->mfunc.master.slave_eq; + struct mlx4_eqe *s_eqe = + &slave_eq->event_eqe[slave_eq->prod & (SLAVE_EVENT_EQ_SIZE - 1)]; - if (ctx->last_cmd != MLX4_COMM_CMD_VHCR_POST) { - mlx4_warn(dev, "received event for inactive slave:%d\n", slave); + if ((!!(s_eqe->owner & 0x80)) ^ (!!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE))) { + mlx4_warn(dev, "Master failed to generate an EQE for slave: %d. " + "No free EQE on slave events queue\n", slave); return; } - /* Unconditionally add the new event - during overflows, we drop the - * oldest events */ - spin_lock_irqsave(&ctx->lock, flags); - ctx->eq[ctx->eq_pi & MLX4_MFUNC_EQE_MASK].type = type; - ctx->eq[ctx->eq_pi & MLX4_MFUNC_EQE_MASK].port = port; - ctx->eq[ctx->eq_pi & MLX4_MFUNC_EQE_MASK].param = param; - ++ctx->eq_pi; - spin_unlock_irqrestore(&ctx->lock, flags); + memcpy(s_eqe, eqe, sizeof(struct mlx4_eqe) - 1); + s_eqe->slave_id = slave; + /* ensure all information is written before setting the ownersip bit */ + wmb(); + s_eqe->owner = !!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE) ? 0x0 : 0x80; + ++slave_eq->prod; + + queue_work(priv->mfunc.master.comm_wq, &priv->mfunc.master.slave_event_work); } -static void mlx4_slave_event_all(struct mlx4_dev *dev, u8 type, u8 port, u32 param) +static void mlx4_slave_event(struct mlx4_dev *dev, int slave, struct mlx4_eqe* eqe) { struct mlx4_priv *priv = mlx4_priv(dev); - int i; + struct mlx4_slave_state *s_slave = &priv->mfunc.master.slave_state[slave]; - for (i = 0; i < dev->num_slaves; ++i) - if (priv->mfunc.master.slave_state[i].last_cmd == MLX4_COMM_CMD_VHCR_POST) - mlx4_slave_event(dev, i, type, port, param); + if (!s_slave->active) { + mlx4_warn(dev, "Trying to pass event to inactive slave\n"); + return; + } + + slave_event(dev, slave, eqe); +} + +static void mlx4_slave_event_all(struct mlx4_dev *dev, struct mlx4_eqe* eqe) +{ + slave_event(dev, ALL_SLAVES, eqe); } int mlx4_GET_EVENT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, @@ -216,24 +230,9 @@ int mlx4_GET_EVENT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vh return 0; } -static int mlx4_GET_EVENT(struct mlx4_dev *dev, struct mlx4_slave_eqe *eqe) -{ - int ret; - u64 out_param; - - ret = mlx4_cmd_imm(dev, 0, &out_param, 0, 0, MLX4_CMD_GET_EVENT, - MLX4_CMD_TIME_CLASS_A); - if (!ret) { - eqe->type = out_param & 0xff; - eqe->port = (out_param >> 8) & 0xff; - eqe->param = out_param >> 32; - } else - mlx4_err(dev, "Failed retrieving event\n"); - return ret; -} - static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) { + struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_eqe *eqe; int cqn; int eqes_found = 0; @@ -263,9 +262,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) case MLX4_EVENT_TYPE_WQ_ACCESS_ERROR: if (mlx4_is_master(dev)) { /* TODO: forward only to slave owning the QP */ - mlx4_slave_event(dev, 0, eqe->type, 0, - be32_to_cpu(eqe->event.qp.qpn) & - 0xffffff); + mlx4_slave_event(dev, 0, eqe); } else mlx4_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, eqe->type); @@ -275,9 +272,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR: if (mlx4_is_master(dev)) { /* TODO: forward only to slave owning the SRQ */ - mlx4_slave_event(dev, 0, eqe->type, 0, - be32_to_cpu(eqe->event.srq.srqn) & - 0xffffff); + mlx4_slave_event(dev, 0, eqe); } else mlx4_srq_event(dev, be32_to_cpu(eqe->event.srq.srqn) & 0xffffff, eqe->type); @@ -295,20 +290,14 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) if (eqe->subtype == MLX4_PORT_CHANGE_SUBTYPE_DOWN) { mlx4_dispatch_event(dev, MLX4_DEV_EVENT_PORT_DOWN, port); - if (mlx4_is_master(dev)) { - mlx4_slave_event_all(dev, MLX4_EVENT_TYPE_PORT_CHANGE, - port, MLX4_DEV_EVENT_PORT_DOWN); - } mlx4_priv(dev)->sense.do_sense_port[port] = 1; } else { mlx4_dispatch_event(dev, MLX4_DEV_EVENT_PORT_UP, port); - if (mlx4_is_master(dev)) { - mlx4_slave_event_all(dev, MLX4_EVENT_TYPE_PORT_CHANGE, - port, MLX4_DEV_EVENT_PORT_UP); - } mlx4_priv(dev)->sense.do_sense_port[port] = 0; } + if (mlx4_is_master(dev)) + mlx4_slave_event_all(dev, eqe); break; case MLX4_EVENT_TYPE_CQ_ERROR: @@ -318,8 +307,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) be32_to_cpu(eqe->event.cq_err.cqn) & 0xffffff); if (mlx4_is_master(dev)) { /* TODO: forward only to slave owning the CQ */ - mlx4_slave_event(dev, 0, eqe->type, 0, - be32_to_cpu(eqe->event.cq_err.cqn)); + mlx4_slave_event(dev, 0, eqe); } else mlx4_cq_event(dev, be32_to_cpu(eqe->event.cq_err.cqn), eqe->type); @@ -329,6 +317,20 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) mlx4_warn(dev, "EQ overrun on EQN %d\n", eq->eqn); break; + case MLX4_EVENT_TYPE_COMM_CHANNEL: + if (!mlx4_is_master(dev)) { + mlx4_warn(dev, "Received comm channel event " + "for non master device\n"); + break; + } + memcpy(&priv->mfunc.master.comm_arm_bit_vector, + eqe->event.comm_channel_arm.bit_vec, + sizeof(u32) * COMM_CHANNEL_BIT_ARRAY_SIZE); + queue_work(priv->mfunc.master.comm_wq, + &priv->mfunc.master.comm_work); + break; + + case MLX4_EVENT_TYPE_EEC_CATAS_ERROR: case MLX4_EVENT_TYPE_ECC_DETECT: default: @@ -359,57 +361,6 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) return eqes_found; } -void mlx4_slave_async_eq_poll(struct work_struct *work) -{ - struct delayed_work *delay = container_of(work, struct delayed_work, work); - struct mlx4_mfunc *mfunc = container_of(delay, struct mlx4_mfunc, comm_work); - struct mlx4_priv *priv = container_of(mfunc, struct mlx4_priv, mfunc); - struct mlx4_dev *dev = &priv->dev; - struct mlx4_slave_eqe eqe; - int ret; - int i; - - for (i = 0; i < MLX4_MFUNC_MAX_EQES; i++) { - ret = mlx4_GET_EVENT(dev, &eqe); - if (ret || eqe.type == MLX4_EVENT_TYPE_NONE) - break; - - switch (eqe.type) { - case MLX4_EVENT_TYPE_PATH_MIG: - case MLX4_EVENT_TYPE_COMM_EST: - case MLX4_EVENT_TYPE_SQ_DRAINED: - case MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE: - case MLX4_EVENT_TYPE_WQ_CATAS_ERROR: - case MLX4_EVENT_TYPE_PATH_MIG_FAILED: - case MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR: - case MLX4_EVENT_TYPE_WQ_ACCESS_ERROR: - mlx4_qp_event(dev, eqe.param, eqe.type); - break; - - case MLX4_EVENT_TYPE_SRQ_LIMIT: - case MLX4_EVENT_TYPE_SRQ_CATAS_ERROR: - mlx4_srq_event(dev, eqe.param, eqe.type); - break; - - case MLX4_EVENT_TYPE_PORT_CHANGE: - mlx4_dispatch_event(dev, eqe.param, eqe.port); - break; - - case MLX4_EVENT_TYPE_CQ_ERROR: - mlx4_cq_event(dev, eqe.param, eqe.type); - break; - - case MLX4_EVENT_TYPE_EQ_OVERFLOW: - mlx4_warn(dev, "slave async EQ overrun\n"); - break; - - default: - mlx4_warn(dev, "Unhandled event:%02x\n", eqe.type); - } - } - queue_delayed_work(priv->mfunc.comm_wq, &priv->mfunc.comm_work, HZ); -} - static irqreturn_t mlx4_interrupt(int irq, void *dev_ptr) { struct mlx4_dev *dev = dev_ptr; @@ -436,6 +387,30 @@ static irqreturn_t mlx4_msi_x_interrupt(int irq, void *eq_ptr) return IRQ_HANDLED; } +int mlx4_MAP_EQ_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_slave_event_eq_info *event_eq = + &priv->mfunc.master.slave_state[slave].event_eq; + u32 in_modifier = vhcr->in_modifier; + u32 eqn = in_modifier & 0x1FF; + u64 in_param = vhcr->in_param; + + if (in_modifier >> 31) { + /* unmap */ + event_eq->event_type &= ~in_param; + return 0; + } + + event_eq->eqn = eqn; + event_eq->event_type = in_param; + + return 0; +} + static int mlx4_MAP_EQ(struct mlx4_dev *dev, u64 event_mask, int unmap, int eq_num) { @@ -748,18 +723,16 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) } } - if (!mlx4_is_slave(dev)) { - err = mlx4_create_eq(dev, MLX4_NUM_ASYNC_EQE + MLX4_NUM_SPARE_EQE, - (dev->flags & MLX4_FLAG_MSI_X) ? dev->caps.num_comp_vectors : 0, - &priv->eq_table.eq[dev->caps.num_comp_vectors]); - if (err) - goto err_out_comp; - } + err = mlx4_create_eq(dev, MLX4_NUM_ASYNC_EQE + MLX4_NUM_SPARE_EQE, + (dev->flags & MLX4_FLAG_MSI_X) ? dev->caps.num_comp_vectors : 0, + &priv->eq_table.eq[dev->caps.num_comp_vectors]); + if (err) + goto err_out_comp; if (dev->flags & MLX4_FLAG_MSI_X) { const char *eq_name; - for (i = 0; i < dev->caps.num_comp_vectors + !mlx4_is_slave(dev); ++i) { + for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) { if (i < dev->caps.num_comp_vectors) { snprintf(priv->eq_table.irq_names + i * MLX4_IRQNAME_SIZE, @@ -797,22 +770,19 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) priv->eq_table.have_irq = 1; } - if (!mlx4_is_slave(dev)) { /* hw async events cannot be shared */ - err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, - priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); - if (err) - mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", - priv->eq_table.eq[dev->caps.num_comp_vectors].eqn, err); - } + err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); + if (err) + mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", + priv->eq_table.eq[dev->caps.num_comp_vectors].eqn, err); - for (i = 0; i < dev->caps.num_comp_vectors + !(mlx4_is_slave(dev)); ++i) + for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) eq_set_ci(&priv->eq_table.eq[i], 1); return 0; err_out_async: - if (!mlx4_is_slave(dev)) - mlx4_free_eq(dev, &priv->eq_table.eq[dev->caps.num_comp_vectors]); + mlx4_free_eq(dev, &priv->eq_table.eq[dev->caps.num_comp_vectors]); err_out_comp: i = dev->caps.num_comp_vectors; @@ -840,14 +810,12 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i; - if (!mlx4_is_slave(dev)) { - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, - priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); - } + mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, + priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); mlx4_free_irqs(dev); - for (i = 0; i < dev->caps.num_comp_vectors + !mlx4_is_slave(dev); ++i) + for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) mlx4_free_eq(dev, &priv->eq_table.eq[i]); if (!mlx4_is_slave(dev)) diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index f7fed9a..78a6255 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -807,10 +807,10 @@ static void mlx4_slave_exit(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); - down(&priv->cmd.poll_sem); + down(&priv->cmd.slave_sem); if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_TIME)) mlx4_warn(dev, "Failed to close slave function.\n"); - up(&priv->cmd.poll_sem); + up(&priv->cmd.slave_sem); } static void mlx4_close_hca(struct mlx4_dev *dev) @@ -830,7 +830,8 @@ static int mlx4_init_slave(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); u64 dma = (u64) priv->mfunc.vhcr_dma; - down(&priv->cmd.poll_sem); + down(&priv->cmd.slave_sem); + priv->cmd.max_cmds = 1; mlx4_warn(dev, "Sending reset\n"); if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_TIME)) goto err; @@ -846,12 +847,12 @@ static int mlx4_init_slave(struct mlx4_dev *dev) goto err; if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_EN, dma, MLX4_COMM_TIME)) goto err; - up(&priv->cmd.poll_sem); + up(&priv->cmd.slave_sem); return 0; err: mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, 0); - up(&priv->cmd.poll_sem); + up(&priv->cmd.slave_sem); return -EIO; } @@ -1005,13 +1006,11 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) goto err_mr_table_free; } - if (!mlx4_is_slave(dev)) { - err = mlx4_cmd_use_events(dev); - if (err) { - mlx4_err(dev, "Failed to switch to event-driven " - "firmware commands, aborting.\n"); - goto err_eq_table_free; - } + err = mlx4_cmd_use_events(dev); + if (err) { + mlx4_err(dev, "Failed to switch to event-driven " + "firmware commands, aborting.\n"); + goto err_eq_table_free; } err = mlx4_NOP(dev); @@ -1094,8 +1093,7 @@ err_cq_table_free: mlx4_cleanup_cq_table(dev); err_cmd_poll: - if (!mlx4_is_slave(dev)) - mlx4_cmd_use_polling(dev); + mlx4_cmd_use_polling(dev); err_eq_table_free: mlx4_cleanup_eq_table(dev); @@ -1126,10 +1124,11 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) int i; if (msi_x) { - /* The master only uses en event EQ, - * Each one of the slaves have 1 completion eq */ + /* In multifunction mode each function gets 2 msi-X vectors + * one for data path completions anf the other for asynch events + * or command completions */ if (mlx4_is_mfunc(dev)) - nreq = 1 + !!mlx4_is_master(dev); + nreq = 4; else nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs, num_possible_cpus() + 1); @@ -1155,7 +1154,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) goto no_msi; } - dev->caps.num_comp_vectors = nreq - !mlx4_is_slave(dev); + dev->caps.num_comp_vectors = nreq - 1; for (i = 0; i < nreq; ++i) priv->eq_table.eq[i].irq = entries[i].vector; @@ -1470,12 +1469,6 @@ slave_start: pci_set_drvdata(pdev, dev); - /* Start serving comm channel: - * - In master function: poll for commands - * - in slave functions: poll for events - * TODO - enable comm channel interrupts */ - if (mlx4_is_mfunc(dev)) - queue_delayed_work(priv->mfunc.comm_wq, &priv->mfunc.comm_work, 0); return 0; err_port: @@ -1486,8 +1479,7 @@ err_port: mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); - if (!mlx4_is_slave(dev)) - mlx4_cmd_use_polling(dev); + mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_pd_table(dev); @@ -1549,9 +1541,6 @@ static void mlx4_remove_one(struct pci_dev *pdev) int p; if (dev) { - /* Stop serving commands and events over comm channel */ - if (mlx4_is_mfunc(dev)) - cancel_delayed_work_sync(&priv->mfunc.comm_work); mlx4_stop_sense(dev); mlx4_unregister_device(dev); @@ -1564,8 +1553,7 @@ static void mlx4_remove_one(struct pci_dev *pdev) mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); - if (!mlx4_is_slave(dev)) - mlx4_cmd_use_polling(dev); + mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_pd_table(dev); diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index 7a7f787..91803aa 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -178,6 +178,53 @@ struct mlx4_icm_table { struct mlx4_icm **icm; }; + +struct mlx4_eqe { + u8 reserved1; + u8 type; + u8 reserved2; + u8 subtype; + union { + u32 raw[6]; + struct { + __be32 cqn; + } __attribute__((packed)) comp; + struct { + u16 reserved1; + __be16 token; + u32 reserved2; + u8 reserved3[3]; + u8 status; + __be64 out_param; + } __attribute__((packed)) cmd; + struct { + __be32 qpn; + } __attribute__((packed)) qp; + struct { + __be32 srqn; + } __attribute__((packed)) srq; + struct { + __be32 cqn; + u32 reserved1; + u8 reserved2[3]; + u8 syndrome; + } __attribute__((packed)) cq_err; + struct { + u32 reserved1[2]; + __be32 port; + } __attribute__((packed)) port_change; + struct { + #define COMM_CHANNEL_BIT_ARRAY_SIZE 4 + u32 reserved; + u32 bit_vec[COMM_CHANNEL_BIT_ARRAY_SIZE]; + } __attribute__((packed)) comm_channel_arm; + } event; +#define ALL_SLAVES 0xff + u8 slave_id; + u8 reserved3[2]; + u8 owner; +} __attribute__((packed)); + struct mlx4_eq { struct mlx4_dev *dev; void __iomem *doorbell; @@ -190,6 +237,19 @@ struct mlx4_eq { struct mlx4_mtt mtt; }; +struct mlx4_slave_eqe { + u8 type; + u8 port; + u32 param; +}; + +struct mlx4_slave_event_eq_info { + u32 eqn; + bool use_int; + u16 token; + u64 event_type; +}; + struct mlx4_profile { int num_qp; int rdmarc_per_qp; @@ -218,12 +278,6 @@ struct mlx4_comm { u32 slave_read; }; -struct mlx4_slave_eqe { - u8 type; - u8 port; - u32 param; -}; - struct mlx4_mcast_entry { struct list_head list; u64 addr; @@ -250,6 +304,7 @@ struct mlx4_slave_state { u8 last_cmd; u8 init_port_mask; u8 pf_num; + bool active; u8 function; dma_addr_t vhcr_dma; u16 mtu[MLX4_MAX_PORTS + 1]; @@ -257,16 +312,31 @@ struct mlx4_slave_state { struct mlx4_slave_eqe eq[MLX4_MFUNC_MAX_EQES]; struct list_head mcast_filters[MLX4_MAX_PORTS + 1]; struct mlx4_vlan_fltr *vlan_filter[MLX4_MAX_PORTS + 1]; + struct mlx4_slave_event_eq_info event_eq; u16 eq_pi; u16 eq_ci; spinlock_t lock; }; +#define SLAVE_EVENT_EQ_SIZE 128 +struct mlx4_slave_event_eq { + u32 eqn; + u32 cons; + u32 prod; + struct mlx4_eqe event_eqe[SLAVE_EVENT_EQ_SIZE]; +}; + struct mlx4_mfunc_master_ctx { struct mlx4_slave_state *slave_state; int init_port_ref[MLX4_MAX_PORTS + 1]; u16 max_mtu[MLX4_MAX_PORTS + 1]; int disable_mcast_ref[MLX4_MAX_PORTS + 1]; + struct workqueue_struct *comm_wq; + struct work_struct comm_work; + struct work_struct slave_event_work; + u32 comm_arm_bit_vector[4]; + struct mlx4_eqe cmd_eqe; + struct mlx4_slave_event_eq slave_eq; }; struct mlx4_vhcr { @@ -282,8 +352,6 @@ struct mlx4_vhcr { struct mlx4_mfunc { struct mlx4_comm __iomem *comm; - struct workqueue_struct *comm_wq; - struct delayed_work comm_work; struct mlx4_vhcr *vhcr; dma_addr_t vhcr_dma; @@ -296,6 +364,7 @@ struct mlx4_cmd { struct mutex hcr_mutex; struct semaphore poll_sem; struct semaphore event_sem; + struct semaphore slave_sem; int max_cmds; spinlock_t context_lock; int free_head; @@ -522,7 +591,6 @@ void mlx4_free_ownership(struct mlx4_dev *dev); int mlx4_alloc_eq_table(struct mlx4_dev *dev); void mlx4_free_eq_table(struct mlx4_dev *dev); -void mlx4_slave_event(struct mlx4_dev *dev, int slave, u8 type, u8 port, u32 param); int mlx4_GET_EVENT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, struct mlx4_cmd_mailbox *outbox); @@ -576,7 +644,16 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, struct mlx4_profile *request, struct mlx4_dev_cap *dev_cap, struct mlx4_init_hca_param *init_hca); -void mlx4_slave_async_eq_poll(struct work_struct *work); +void mlx4_master_comm_channel(struct work_struct *work); +void mlx4_gen_slave_eqe(struct work_struct *work); + +int mlx4_MAP_EQ_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox); +int mlx4_COMM_INT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox); +int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe); int mlx4_cmd_init(struct mlx4_dev *dev); void mlx4_cmd_cleanup(struct mlx4_dev *dev); diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 9225791..a6901c3 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -125,6 +125,10 @@ enum { MLX4_CMD_SET_MCAST_FLTR = 0x48, MLX4_CMD_DUMP_ETH_STATS = 0x49, + /* Communication channel commands */ + MLX4_CMD_ARM_COMM_CHANNEL = 0x57, + MLX4_CMD_GEN_EQE = 0x58, + /* virtual commands */ MLX4_CMD_ALLOC_RES = 0xf00, MLX4_CMD_FREE_RES = 0xf01, @@ -132,7 +136,8 @@ enum { MLX4_CMD_GET_EVENT = 0xf03, MLX4_CMD_QUERY_SLAVE_CAP = 0xf04, MLX4_CMD_MCAST_ATTACH = 0xf05, - MLX4_CMD_PROMISC = 0xf07, + MLX4_CMD_COMM_INT = 0xf07, + MLX4_CMD_PROMISC = 0xf08, /* debug commands */ MLX4_CMD_QUERY_DEBUG_MSG = 0x2a, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c03a176..1b553b0 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -101,6 +101,7 @@ enum mlx4_event { MLX4_EVENT_TYPE_EQ_OVERFLOW = 0x0f, MLX4_EVENT_TYPE_ECC_DETECT = 0x0e, MLX4_EVENT_TYPE_CMD = 0x0a, + MLX4_EVENT_TYPE_COMM_CHANNEL = 0x18, MLX4_EVENT_TYPE_NONE = 0xff, }; -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
